Haskell簡明教程(二):從命令式語言進行抽象
這一系列是我學習Learn You a Haskell For Great Good
之後,總結,編寫的學習筆記。
這個系列主要分為五個部分:
- ofollow,noindex" target="_blank">從遞迴說起
- Haskell初步:語法
- Haskell進階:Monoid, Applicative, Monad
- 實戰:Haskell和JSON
從移動端推送看什麼是抽象以及如何抽象
這一篇我們講要講述一個實際的開發案例。在移動網際網路時代,iOS家有APNs推送技術一統天下, Android有GCM,只不過在國內幾乎都被國產廠商給閹割了。於是乎國內的Android推送服務 百花齊放,系統廠商提供的有華為,小米等,第三方提供的有極光,個推等。今天我們就講講如何 同時接入極光和小米的推送,從實際業務中看看如何進行抽象。
在建模過程中我們可以想象到,這是一個很好的應用面向物件思維的案例,首先推送服務 都需要提供這樣幾個功能:
push_id tag(標籤,第三方提供的便利的群發推送方式) push_id push_id
於是我們可以寫出這樣一個基類:
import abc import requests class AbstractPush(metaclass=abc.ABCMeta): @abc.abstractmethod def push_by_push_id(self, push_id_list): pass @abc.abstractmethod def push_by_tag(self, tag_list): pass @abc.abstractmethod def update_tag(self, push_id, add_tag_list, remove_tag_list): pass @abc.abstractmethod def query_tag(self, push_id): pass
所有的子類都必須實現這些方法,例如我們有極光推送:
# jpusn.py from .base import AbstractPush class JPush(AbstractPush): def push_by_push_id(self, push_id_list): pass def push_by_tag(self, tag_list): pass def update_tag(self, push_id, add_tag_list, remove_tag_list): pass def query_tag(self, push_id): pass
和小米推送:
# mipusn.py from .base import AbstractPush class MiPush(AbstractPush): def push_by_push_id(self, push_id_list): pass def push_by_tag(self, tag_list): pass def update_tag(self, push_id, add_tag_list, remove_tag_list): pass def query_tag(self, push_id): pass
所以上面的程式碼實際上是這樣的一種關係:
我們使用的時候就可以分別例項化不同的推送物件,然後呼叫對應的方法。
例如:MiPush().push_by_push_id("the_given_mi_push_id")
。
這就是經典的面向物件的思維。
如果我們反過來呢看看呢?我們已知所有的推送都實現了以上的方法,但是實際上我們 根本不需要知道它具體是什麼推送,只需要知道它實現了這個方法就行。其實就是我們 常說的橋接模式,看一下維基百科 的解釋。
那麼就會變成這樣一種關係:
這是基於一種什麼樣的想法呢?我們假設每種推送都是一個黑盒子,沒個黑盒子上都有四個洞
ABCD,不論是哪個黑盒子,從同一個編號的洞裡丟東西進去,總是會從另一個固定的洞裡出來。
就好象:A->C
,B->C
這樣。這就是傳說中的抽象。在Haskell中我們將會大量運用這種
能力。
其實我們在日常的程式設計中已經用到了這種能力,在Java中我們稱之為介面(interface
)。
Golang中也是,Python中雖然沒有明確的介面的概念,但是通過上面的示例程式碼我們便可以
夠造出這種抽象,在C中我們往往通過函式指標和結構體指標來達到這種效果。
我們把上述的程式碼用Go來描述,就當作是我們的總結。
type Push interface { push_by_push_id(push_id_list []string) push_by_tag(tag_list []string) update_tag(push_id string, add_tag_list []string, remove_tag_list []string) query_tag(push_id string) }
抽象是軟體開發中的一把利器,程式設計其實就是一個將複雜的任務拆解成無數個小任務,然後各個擊破 的過程。而抽象則是將無數個小任務的共同點找出來,然後一箭n雕的一種能力。
這一篇其實是https://jiajunhuang.com/articles/2017_08_12-tcp_ip.md.html 第一節 "為什麼要分層" 的一個更詳細的重複,但是抽象這麼重要的能力,就算再多重申一百遍都 不為過。
總結
通過這一篇文章,我們從一個實際生產的案例看到了如何進行抽象,以及抽象和麵向物件 過程的一些微小而又巨大的差異。接下來我們將從命令式語言跳到Haskell的具體語法, 看看在Haskell中,是否也有我們所謂的抽象和介面這些概念。