在最開始,先重復(fù)一下第一篇的內(nèi)容,這個系列是寫我們?nèi)绾蝸斫M織代碼,如何提高可擴展性和維護性的,并不涉及到網(wǎng)絡(luò)拓補結(jié)構(gòu)或各類中間件的使用。

首先,提一提面向?qū)ο笤O(shè)計的五大原則:SOLID。

SOLID原則

SOLID都是些什么呢?

  • SRP, Single responsibility principle,單一職責(zé)。一個類只能有一個職責(zé),如果這個類需要被修改,那只能是某一個需求更改導(dǎo)致的(僅此一個,沒有更多的)。例如,book類里面有一個print的函數(shù),當(dāng)我們修改book類的書名時,我們需要改book類,當(dāng)我們把book的打印從打印到A4改成打印成6寸時,也需要修改此類,這就違背了SRP原則。

  • OCP, Open/closed principle,開閉原則,Open for extension, but closed for modification

  • LSP, Liskov substitution principle,父類能夠被子類無憂的替代,不必?fù)?dān)心產(chǎn)生副作用。

  • ISP, Interface segregation principle,如果一個接口能夠被拆分成多個接口,那就不該用這個通用的接口來呈現(xiàn)。

  • DIP, Dependency Inversion principle,依賴于抽象,而不依賴與具體的實現(xiàn)。

今天先從SRP和DI開始。

單一職責(zé)

它的定義上面已經(jīng)講過了。
這一條是用來幫助我們創(chuàng)建更為松耦合和模塊化的程序,因為每種不同的行為我們都封裝到一個新的類或?qū)ο罄锩嫒チ恕N磥硪黾有碌男袨榛蛱匦?,新增的?nèi)容也會增加新的對象里面,而不是把這些行為加到原來的對象上去。
這樣做的好處是更安全,更不容易出錯,因為以前的類可能是含有多種多樣的依賴關(guān)系的,但新增加的類卻沒有那些依賴在里面。所以我們可以放心的修改系統(tǒng)行為,不必?fù)?dān)心修改帶來的副作用。

在分層結(jié)構(gòu)中,這個思想也有體現(xiàn)。我們的UI/Presentation層專管UI的顯示,logic層專攻業(yè)務(wù)邏輯,數(shù)據(jù)訪問層只做數(shù)據(jù)訪問相關(guān)內(nèi)容。其實,都是同樣的道理。

如果SRP思想貫穿了你的整個程序,你的邏輯層里的某一個服務(wù)是不是就成了微服務(wù),一個微服務(wù)只有一個單一的職責(zé),你要給你的程序增加功能,那再加一個微服務(wù)即可(切忌在已有的微服務(wù)上添加)

依賴反轉(zhuǎn)(Dependency Inversion)

應(yīng)用程序內(nèi)部的依賴不應(yīng)該依賴于具體的實現(xiàn),應(yīng)該依賴于抽象。
也是五大原則之一。

很多應(yīng)用的依賴是在編譯期就確定了依賴的順序,就如同模塊A調(diào)用了模塊B的一個函數(shù),剛好這個模塊B內(nèi)的函數(shù)又調(diào)用了模塊C的某個函數(shù),于是,A就依賴B,B依賴于C。

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動開發(fā)培訓(xùn)

應(yīng)用了DIP之后,就像這個樣子:

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動開發(fā)培訓(xùn)

可以看到,ClassA不再依賴于Class B,轉(zhuǎn)而依賴InterfaceB,ClassB也不再依賴于Class C,轉(zhuǎn)變成Interface B依賴于Interface C。

ClassA現(xiàn)在依賴的是B的抽象(即Interface B),這使得A在運行時依然可以調(diào)用到B的函數(shù),運行時應(yīng)有的行為還能得以保留。不過B依賴已經(jīng)變化了,它現(xiàn)在依賴于Interface B了(反轉(zhuǎn)依賴),現(xiàn)在的好處在于,不但B的功能可用,未來你想變成B1,B2,B3,這些都不必修改Class A了(如果你現(xiàn)在還不明白如何去實現(xiàn),參考Ioc Container的實現(xiàn))。

依賴翻轉(zhuǎn)是構(gòu)建松耦合應(yīng)用的關(guān)鍵所在,既然具體的實現(xiàn)都依賴于更高層的抽象了,那么程序就應(yīng)該更容易被測試、維護和模塊化。其實依賴注入(Dependency injection)也是準(zhǔn)照這個原則來擴展實現(xiàn)的,所以掌握這個方法也是十分的重要。

以下原則跟SOLID無關(guān),但我認(rèn)為也是比較重要的。

顯式依賴

如果函數(shù)或類必須依賴其他類或?qū)ο蟮哪承顟B(tài)才能正常工作,那應(yīng)該顯式的聲明其依賴的對象或類。
這有點拗口。

實際上類的構(gòu)造函數(shù)提供了一個約定:我要構(gòu)造這個類A的對象,需要提供1、2、3、4個參數(shù),如果沒有這幾個參數(shù),我的類就可能工作不正常。
ClassA的依賴關(guān)系很明確,就是要1、2、3、4個參數(shù),這是合理的。

假設(shè)這樣一種情況,ClassA除了上述依賴以外,后來增加了一個新的依賴,在增加這個依賴的時候,我沒有將它顯示的寫在構(gòu)造函數(shù)里面,因為這個依賴項是個全局對象。

一般來說這樣的代碼運行起來也沒有什么問題,但是在實際上,它在邏輯上已經(jīng)引發(fā)了一個問題:你的這個類的依賴關(guān)系內(nèi)外不一致。試想如果在另外一個場景里,這個全局的對象失效了或不存在,那么你的這個類就不能用了。
你的類已經(jīng)不能用了,但是你們團隊里面的其他成員可能并不知道,他們?nèi)匀话凑罩暗哪J嚼^續(xù)使用你的類,于是這就引起了潛在的錯誤。

如果你的類能夠顯式聲明它的所有依賴,你的類才能夠更友好,代碼本身也能更清楚的表達(dá)自身的含義。你的小組其他成員相信,只要完全的傳遞了你要的參數(shù),你的代碼就一定會按既定的邏輯運行下去。

避免重復(fù)(Don't repeat yourself)

重復(fù)代碼是后期bug修復(fù)的大敵。

我們要盡量避免編程時使用復(fù)制和粘貼的功能,如果同樣的邏輯在代碼里的多個位置出現(xiàn),每次我們維護的時候就不得不同時維護多處。當(dāng)功能轉(zhuǎn)交給其他人時,其他人也會厭煩多處查找這些問題;其他人將這類代碼轉(zhuǎn)交給你時,你也會頭疼不已。

忽略持久化(Persistence Ignorance)

這個概念PI,有些地方也稱作持久性無知,是指你需要存儲某個對象,但是這些代碼不應(yīng)該受到持久化方式更改的的影響。
在.Net里,有一種類叫POCOs(Plain Old CLR Objects),在Java里,這種叫POJOs(Plain Old Java Objects),這種類是不需要從基類或接口來繼承的。
我們需要保證這類對象不要插手如何保存或如何獲?。ǔ志没瞳@取數(shù)據(jù)回來)。

做到這一點,我們的業(yè)務(wù)邏輯能更純粹,而存儲對象時,也能靈活地按需調(diào)整(比如說用Redis或Azure或Sql Server等等),無論存儲的策略如何調(diào)整,我們的業(yè)務(wù)邏輯都是穩(wěn)定的。

注意,如果你的代碼有以下幾類情況,那這些代碼可能就是違反PI規(guī)則的:

  • 必須繼承自某個基類或必須實現(xiàn)某個接口

  • 類的內(nèi)部含有保存他們自己的代碼

  • 必須要寫構(gòu)造函數(shù)

  • 屬性上要求加Virtual

  • 有自定義的存儲相關(guān)的Attribute

如果類里面有上面的邏輯,暗示著這些類的持久化跟持久化的策略產(chǎn)生關(guān)聯(lián)性,可以理解為她們是耦合的,將來如果要更換新的持久化方式,也許就比較困難了。

有界上下文(Bounded Contexts)

有界上下文是DDD中的一個核心模式,這種模式是幫助我們剝離開復(fù)雜的應(yīng)用程序,將他們隔離開,形成不同的上下文邊界。不同的模塊有著不同的上下文,且能獨立調(diào)用,而各自的模塊可以有自己的持久化的,互不干擾。

在大型的應(yīng)用程序中,不同的人對不同的的東西可能取相同的名字,這跟我們程序的類一樣,為何我們要在外面放一個namespace在外面,其實也是形成一個邊界。程序內(nèi)部也是如此。

例如,售樓部內(nèi)的員工把商品房認(rèn)為是產(chǎn)品(Product);但是在工程部,可能他們把刷灰和修理管道的服務(wù)叫做產(chǎn)品,為了消除這些歧義,可以定義一個邊界,分離開兩種情況,以免在系統(tǒng)內(nèi)產(chǎn)生混淆。

結(jié)語

這是這個系列的最后內(nèi)容,實際上每一個小點都能展開成一大章節(jié)來說,后續(xù)有時間可以進(jìn)行延伸的討論。而因為這篇的內(nèi)容除了SOLID外,跨度都還比較大,以至于我都不好寫標(biāo)題,暫以《其他原則》為題好了。

面向?qū)ο缶幊唐鋵嵧﹄y的,我們很多的人都沒有真正的理解面向?qū)ο?,以至于所謂的面向?qū)ο?,就是我們用上了class關(guān)鍵字而已。如果有時間,可以專門看看設(shè)計模式的書,相信還是會有很多的收獲的。

這些內(nèi)容可能過于偏于理論,但是有了這些理論的指導(dǎo),我們的開發(fā)的日子才會更容易一些。但話又說回來,如果你要做的項目是個一桿子買賣,根本不會持續(xù)維護,那你還是盡量的使用反模式吧,又快又省心不燒腦。可是如果你的項目是長度維護的項目,必要的考量還是需要的,否則,你就陷入了泥潭,總是發(fā)夢期望公司將原來的推翻重寫,一次償還所有的技術(shù)債了。

這種人呢,我們把他叫做『老賴』,以前的技術(shù)債還不清了……那不還了,申請『破產(chǎn)』,我們重來吧。

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動開發(fā)培訓(xùn)
本文地址:http://www.cnblogs.com/asis/p/architecture-others.html
https://1few.com/architecture-others/

標(biāo)簽: 架構(gòu)

好文要頂 關(guān)注我 收藏該文 iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動開發(fā)培訓(xùn) iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動開發(fā)培訓(xùn)

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動開發(fā)培訓(xùn)

xiao.chun
關(guān)注 - 1
粉絲 - 5

+加關(guān)注

1

0

上一篇:架構(gòu)漫談系列(2) 封裝(Encapsulation)

posted @ 2017-07-07 11:32 xiao.chun 閱讀(239) 評論(3編輯 收藏

評論列表

  

#1樓 2017-07-07 12:00 —阿輝  

引用如果函數(shù)或類必須依賴其他類或?qū)ο蟮哪承顟B(tài)才能正常工作,那應(yīng)該顯式的聲明其依賴的對象或類
如何顯示的聲明對應(yīng)的依賴對象和類?

支持(0)反對(0)

  

#2樓[樓主2017-07-07 14:22 xiao.chun  

@ —阿輝
考慮下面的偽代碼,我認(rèn)為ClassA2的設(shè)計優(yōu)于ClassA1,
因為其實ClassA1和A2都依賴于depden1~3再加上MyState類。
由于MyState有全局變量,所以ClassA1中省略了全局變量的依賴。

假設(shè)ClassA1拿到某個地方去,現(xiàn)在沒有那個全局變量了,程序就報錯了,MyState是ClassA1的隱式依賴。

```csharp
class Program
{
public static MyState GlobalState { get; } = new MyState();

static void Main(string[] args)
{
}


}

internal class ClassA1
{
public ClassA1(Dependency1 depend1, Dependency2 depend2, Dependency3 depend3)
{
if (Program.GlobalState.IsOK)
{

}
}
}

internal class ClassA2
{
public ClassA2(Dependency1 depend1, Dependency2 depend2, Dependency3 depend3, MyState state)
{
if (state.IsOK)
{

}
}
}
```

ClassA1的壞處在于,就算我有辦法尋求到MyState的替代,我也傳遞不到ClassA1里面去,還是得修改代碼的,而ClassA2則沒有這個問題。

http://www.cnblogs.com/asis/p/architecture-others.html