引言
對于設(shè)計模式品擎,自己很早之前就看了好多本設(shè)計模式書籍,其中一些還看了好幾遍秽五,也一直希望自己能在編碼的時候把這些設(shè)計模式用上去孽查〖玻可是坦喘,在日常的打碼中,用的做多的就是單例西设,其次是觀察者和建造者模式 ( builder ) 用得比較多瓣铣,其他的基本很少用到。
用不到的原因是還是不能夠理解設(shè)計模式的思想贷揽,無法將這些設(shè)計模式和編碼遇到的問題聯(lián)系起來棠笑,從而用不到設(shè)計模式。
其實設(shè)計模式的提出都是為了解決一個常見的問題而總結(jié)出來的辦法禽绪。所以當(dāng)你思考采用何種設(shè)計模式的時候蓖救,你應(yīng)該先問問自己當(dāng)前問題的是什么?根據(jù)問題去選取合適的設(shè)計模式印屁。
等你熟悉了設(shè)計模式的以后循捺,你會發(fā)現(xiàn)部分設(shè)計模式之間存在包含關(guān)系,甚至很相像雄人,但是不同的設(shè)計模式解決的問題是不一樣的从橘。
當(dāng)我們在設(shè)計一個模塊的時候可以從以下幾個角度去考慮:
這個模塊與其他模塊的關(guān)系是什么樣的?
模塊中哪些部分是不變的础钠,哪些部分是在不斷變化的恰力,是如何變化的?
類與類之間的關(guān)系是怎么樣的旗吁,為什么需要依賴踩萎,怎么可以不依賴?
要不要加一個接口很钓?接口的存在是為了解決什么問題驻民?
當(dāng)然,本文并不是教你是如何使用設(shè)計模式履怯。而是講解設(shè)計模式的設(shè)計原則回还。設(shè)計模式在被設(shè)計出來的時候,也是遵循一些規(guī)則的叹洲。
設(shè)計模式六大原則柠硕,具體如下:
單一職責(zé)原則(類和方法,接口)
開閉原則 (擴(kuò)展開放,修改關(guān)閉)
里氏替換原則(基類和子類之間的關(guān)系)
依賴倒置原則(依賴抽象接口蝗柔,而不是具體對象)
接口隔離原則(接口按照功能細(xì)分)
迪米特法則 (類與類之間的親疏關(guān)系)
每一個設(shè)計原則旁邊都有個括號闻葵,是用來解釋,或者描述應(yīng)用范圍的癣丧。下面將詳細(xì)介紹每一個原則槽畔。
單一職責(zé)原則的定義(類、方法胁编、接口)
單一職責(zé)原則(Single Responsibility Principle厢钧,SRP)又稱單一功能原則。這里的職責(zé)是指類變化的原因嬉橙,單一職責(zé)原則規(guī)定一個類應(yīng)該有且僅有一個引起它變化的原因早直,否則類應(yīng)該被拆分(There should never be more than one reason for a class to change)。
該原則提出對象不應(yīng)該承擔(dān)太多職責(zé)市框,如果一個對象承擔(dān)了太多的職責(zé)霞扬,至少存在以下兩個缺點:
一個職責(zé)的變化可能會削弱或者抑制這個類實現(xiàn)其他職責(zé)的能力;
當(dāng)客戶端需要該對象的某一個職責(zé)時枫振,不得不將其他不需要的職責(zé)全都包含進(jìn)來喻圃,從而造成冗余代碼或代碼的浪費。
單一職責(zé)原則的優(yōu)點
單一職責(zé)原則的核心就是控制類的粒度大小粪滤、將對象解耦斧拍、提高其內(nèi)聚性。如果遵循單一職責(zé)原則將有以下優(yōu)點额衙。
降低類的復(fù)雜度饮焦。一個類只負(fù)責(zé)一項職責(zé),其邏輯肯定要比負(fù)責(zé)多項職責(zé)簡單得多窍侧。
提高類的可讀性县踢。復(fù)雜性降低,自然其可讀性會提高伟件。
提高系統(tǒng)的可維護(hù)性硼啤。可讀性提高斧账,那自然更容易維護(hù)了谴返。
變更引起的風(fēng)險降低。變更是必然的咧织,如果單一職責(zé)原則遵守得好嗓袱,當(dāng)修改一個功能時,可以顯著降低對其他功能的影響习绢。
單一職責(zé)原則的實現(xiàn)方法
單一職責(zé)原則是最簡單但又最難運用的原則渠抹,需要設(shè)計人員發(fā)現(xiàn)類的不同職責(zé)并將其分離蝙昙,再封裝到不同的類或模塊中。而發(fā)現(xiàn)類的多重職責(zé)需要設(shè)計人員具有較強(qiáng)的分析設(shè)計能力和相關(guān)重構(gòu)經(jīng)驗梧却。
示例
publicinterface UserService {
? ? publicvoid login(String username, String password);
? ? publicvoid register(String email, String username, String password);
? ? publicvoid logError(String msg);
? ? publicvoid sendEmail(String email);
}
這段代碼很顯然存在很大的問題奇颠,UserService 既要負(fù)責(zé)用戶的注冊和登錄,還要負(fù)責(zé)日志的記錄和郵件的發(fā)送放航,并且后者的行為明顯區(qū)別于前者烈拒。
假設(shè)我要修改發(fā)送郵件的邏輯就得修改這個類,這時候 qa 還得回歸登錄注冊邏輯广鳍,這樣明顯不合理荆几。
因此我們需要進(jìn)行拆分,根據(jù)具體的職能可將其具體拆分如下:
UserService:只負(fù)責(zé)登錄注冊
publicinterface UserService {
? ? publicvoid login(String username, String password);
? ? publicvoid register(String email, String username, String password);
}
LogService?:只負(fù)責(zé)日志
publicinterface LogService {
? ? publicvoid logError(String msg);
}
EmailService:?只負(fù)責(zé)發(fā)送郵件
publicinterface EmailService {
? ? publicvoid sendEmail(String email);
}
這時候搜锰,咱們再去回顧前面提到的優(yōu)點伴郁,就能深深體會了耿战。
這里只是講了接口蛋叼,其實對類也一樣,甚至方法也是一樣的剂陡。
對于類來說狈涮,根據(jù)類名,確保里面提供的方法都是屬于這個類的鸭栖。
對于方法歌馍,不要把不相關(guān)的對象實例作為參數(shù)傳進(jìn)來。如果你發(fā)現(xiàn)某個方法依賴某個不相關(guān)的對象晕鹊,那么這個方法的實現(xiàn)可能就存在問題松却。
比如 android 中圖片下載后顯示到 imageView 中,我提供如下的方法:
loadImage(String url, ImageView view) {
// 下載圖片溅话,展示圖片
}
對于?loadImage 這個方法晓锻,參數(shù) url 是ok 的,但是參數(shù) ImageView 卻是不合理的飞几。因為這里做了兩個操作砚哆,下載圖片,展示圖片屑墨。應(yīng)該將這個方法在進(jìn)行拆分:
// 下載圖片
?loadImage(String url) {
}
// 顯示圖片
displayImage(String url, ImageView view) {
// 調(diào)用 getBitmap (url)? 獲取圖片
// 獲取圖片后將其設(shè)置到 view 中躁锁。
}
// 根據(jù) url 獲取圖片,?
getBitmap(String url) {
}
這樣整個邏輯就很清晰卵史。后續(xù)需要修改下載邏輯战转,也不會影響到展示邏輯。當(dāng)然其實還有個問題是以躯,這兩個方法要不要放在一個類里面槐秧?
開閉原則
開閉原則的實現(xiàn)方法:可以通過“抽象約束、封裝變化”來實現(xiàn)開閉原則,即通過接口或者抽象類為軟件實體定義一個相對穩(wěn)定的抽象層色鸳,而將相同的可變因素封裝在相同的具體實現(xiàn)類中社痛。
因為抽象靈活性好,適應(yīng)性廣命雀,只要抽象的合理蒜哀,可以基本保持軟件架構(gòu)的穩(wěn)定。而軟件中易變的細(xì)節(jié)可以從抽象派生來的實現(xiàn)類來進(jìn)行擴(kuò)展吏砂,當(dāng)軟件需要發(fā)生變化時撵儿,只需要根據(jù)需求重新派生一個實現(xiàn)類來擴(kuò)展就可以了。
示例
// 矩形publicclass Rectangle {
? ? publicdouble getWidth() {
? ? ? ? return width;
? ? }
? ? publicdouble getHeight() {
? ? ? ? return height;
? ? }
}
需要計算矩形的面積
// 面積計算器publicclass AreaCalculator {
? ? publicdouble area(Rectangle shape){
? ? ? ? returnshape.getWidth() * shape.getHeight();
? ? }
}
假設(shè)這時候狐血,又多了一個圓形類
// 圓形publicclass Circular {
? ? publicdouble getRadius(){
? ? ? ? return radius;
? ? }
}
同樣也需要計算他的面積淀歇,這時候就會變成下面這樣子:
publicclass AreaCalculator {
? ? publicdouble area(Object shape){
? ? ? ? if(shapeinstanceof Rectangle) {
? ? ? ? ? ? Rectangle rectangle = (Rectangle) shape;
? ? ? ? ? ? returnrectangle.getWidth() * rectangle.getHeight();
? ? ? ? } elseif(shapeinstanceof Circular) {
? ? ? ? ? ? Circular circular = (Circular) shape;
? ? ? ? ? ? returncircular.getRadius() * circular.getRadius() * Math.PI;
? ? ? ? } else {
? ? ? ? ? ? thrownewRuntimeException("There is no such type.");
? ? ? ? }
? ? }
}
這么更改完成,完全沒有問題匈织。但是在真實的生產(chǎn)環(huán)境中浪默,情況更為復(fù)雜,更改涉及的部分較多缀匕,那樣就可能導(dǎo)致牽一發(fā)動全身纳决。并且,以前編寫的經(jīng)過測試的一些功能需要重新測試乡小,甚至導(dǎo)致某些功能不可用阔加。
改進(jìn)版,把計算面積這個公有邏輯變成一個接口:
publicinterface Shape {
? ? publicdouble getArea();
}
publicclassRectangleimplements Shape{
? ? publicdouble getWidth() {
? ? ? ? return width;
? ? }
? ? publicdouble getHeight() {
? ? ? ? return height;
? ? }
? ? publicdouble getArea() {
? ? ? ? returngetWidth() * getHeight();
? ? }
}
這樣满钟,當(dāng)需求變更胜榔,需要計算圓形面積的時候,我們只需創(chuàng)建一個圓形的類湃番,并實現(xiàn) Shape 接口即可:
publicclassCircularimplements Shape {
? ? publicdouble getRadius(){
? ? ? ? return radius;
? ? }
? ? publicdouble getArea() {
? ? ? ? returngetRadius() * getRadius() * Math.PI;
? ? }
}
計算三角形面積夭织、四邊形面積... 的時候,我們只需讓它們?nèi)崿F(xiàn) Shape 接口即可牵辣,無需修改源代碼摔癣。
里氏替換原則
里氏替換原則主要闡述了有關(guān)繼承的一些原則,也就是什么時候應(yīng)該使用繼承纬向,什么時候不應(yīng)該使用繼承择浊,以及其中蘊含的原理。里氏替換原是繼承復(fù)用的基礎(chǔ)逾条,它反映了基類與子類之間的關(guān)系琢岩,是對開閉原則的補(bǔ)充,是對實現(xiàn)抽象化的具體步驟的規(guī)范师脂。
里氏替換原則的作用
里氏替換原則的主要作用如下担孔。
里氏替換原則是實現(xiàn)開閉原則的重要方式之一江锨。
它克服了繼承中重寫父類造成的可復(fù)用性變差的缺點。
它是動作正確性的保證糕篇。即類的擴(kuò)展不會給已有的系統(tǒng)引入新的錯誤啄育,降低了代碼出錯的可能性。
加強(qiáng)程序的健壯性拌消,同時變更時可以做到非常好的兼容性挑豌,提高程序的維護(hù)性、可擴(kuò)展性墩崩,降低需求變更時引入的風(fēng)險氓英。
里氏替換原則的實現(xiàn)方法(繼承)
里氏替換原則通俗來講就是:子類可以擴(kuò)展父類的功能,但不能改變父類原有的功能鹦筹。也就是說:子類繼承父類時铝阐,除添加新的方法完成新增功能外,盡量不要重寫父類的方法铐拐。
根據(jù)上述理解徘键,對里氏替換原則的定義可以總結(jié)如下:
子類可以實現(xiàn)父類的抽象方法,但不能覆蓋父類的非抽象方法
子類中可以增加自己特有的方法
當(dāng)子類的方法重載父類的方法時余舶,方法的前置條件(即方法的輸入?yún)?shù))要比父類的方法更寬松
當(dāng)子類的方法實現(xiàn)父類的方法時(重寫/重載或?qū)崿F(xiàn)抽象方法)啊鸭,方法的后置條件(即方法的的輸出/返回值)要比父類的方法更嚴(yán)格或相等
通過重寫父類的方法來完成新的功能寫起來雖然簡單锹淌,但是整個繼承體系的可復(fù)用性會比較差匿值,特別是運用多態(tài)比較頻繁時,程序運行出錯的概率會非常大赂摆。
如果程序違背了里氏替換原則挟憔,則繼承類的對象在基類出現(xiàn)的地方會出現(xiàn)運行錯誤。
這時其修正方法是:取消原來的繼承關(guān)系烟号,重新設(shè)計它們之間的關(guān)系绊谭。
關(guān)于里氏替換原則的例子,最有名的是“正方形不是長方形”汪拥。當(dāng)然达传,生活中也有很多類似的例子,例如迫筑,企鵝宪赶、鴕鳥和幾維鳥從生物學(xué)的角度來劃分,它們屬于鳥類脯燃;但從類的繼承關(guān)系來看搂妻,由于它們不能繼承“鳥”會飛的功能,所以它們不能定義成“鳥”的子類辕棚。同樣欲主,由于“氣球魚”不會游泳邓厕,所以不能定義成“魚”的子類;“玩具炮”炸不了敵人扁瓢,所以不能定義成“炮”的子類等详恼。
對于正方形和長方形最好的做法是再添加一個父類,他們同時繼承自這個父類引几。
依賴倒置(抽線細(xì)節(jié))
依賴倒置原則是實現(xiàn)開閉原則的重要途徑之一单雾,它降低了客戶與實現(xiàn)模塊之間的耦合。
由于在軟件設(shè)計中她紫,細(xì)節(jié)具有多變性硅堆,而抽象層則相對穩(wěn)定,因此以抽象為基礎(chǔ)搭建起來的架構(gòu)要比以細(xì)節(jié)為基礎(chǔ)搭建起來的架構(gòu)要穩(wěn)定得多贿讹。這里的抽象指的是接口或者抽象類渐逃,而細(xì)節(jié)是指具體的實現(xiàn)類。
使用接口或者抽象類的目的是制定好規(guī)范和契約民褂,而不去涉及任何具體的操作茄菊,把展現(xiàn)細(xì)節(jié)的任務(wù)交給它們的實現(xiàn)類去完成。
依賴赊堪、倒置原則的作用
依賴倒置原則的主要作用如下面殖。
依賴倒置原則可以降低類間的耦合性。
依賴倒置原則可以提高系統(tǒng)的穩(wěn)定性哭廉。
依賴倒置原則可以減少并行開發(fā)引起的風(fēng)險脊僚。
依賴倒置原則可以提高代碼的可讀性和可維護(hù)性。
依賴倒置原則的實現(xiàn)方法
依賴倒置原則的目的是通過要面向接口的編程來降低類間的耦合性遵绰,所以我們在實際編程中只要遵循以下4點辽幌,就能在項目中滿足這個規(guī)則。
每個類盡量提供接口或抽象類椿访,或者兩者都具備乌企。
變量的聲明類型盡量是接口或者是抽象類。
任何類都不應(yīng)該從具體類派生成玫。
使用繼承時盡量遵循里氏替換原則加酵。
依賴倒置原則在“顧客購物程序”中的應(yīng)用。
分析:本程序反映了 “顧客類”與“商店類”的關(guān)系哭当。商店類中有 sell() 方法猪腕,顧客類通過該方法購物以下代碼定義了顧客類通過韶關(guān)網(wǎng)店 ShaoguanShop 購物
class Customer {
? ? publicvoid shopping(ShaoguanShop shop) {
? ? ? ? //購物? ? ? ? System.out.println(shop.sell());
? ? }
}
但是,這種設(shè)計存在缺點荣病,如果該顧客想從另外一家商店(如婺源網(wǎng)店 WuyuanShop)購物码撰,就要將該顧客的代碼修改如下:
class Customer {
? ? publicvoid shopping(WuyuanShop shop) {
? ? ? ? //購物? ? ? ? System.out.println(shop.sell());
? ? }
}
顧客每更換一家商店,都要修改一次代碼个盆,這明顯違背了開閉原則脖岛。
存在以上缺點的原因是:顧客類設(shè)計時同具體的商店類綁定了朵栖,這違背了依賴倒置原則。
解決方法是:定義“婺源網(wǎng)店”和“韶關(guān)網(wǎng)店”的共同接口 Shop柴梆,顧客類面向該接口編程陨溅,其代碼修改如下:
class Customer {
? ? publicvoid shopping(Shop shop) {
? ? ? ? //購物? ? ? ? System.out.println(shop.sell());
? ? }
}class Customer {
? ? publicvoid shopping(Shop shop) {
? ? ? ? //購物? ? ? ? System.out.println(shop.sell());
? ? }
}
這樣,不管顧客類 Customer 訪問什么商店绍在,或者增加新的商店门扇,都不需要修改原有代碼了,其類如下圖所示:
程序代碼如下:
package principle;publicclass DIPtest
{
? ? publicstaticvoid main(String[] args)
? ? {
? ? ? ? Customer wang=new Customer();
? ? ? ? System.out.println("顧客購買以下商品:");
? ? ? ? wang.shopping(new ShaoguanShop());
? ? ? ? wang.shopping(new WuyuanShop());
? ? }
}//商店interface Shop
{
? ? publicString sell();//賣}//韶關(guān)網(wǎng)店classShaoguanShopimplements Shop
{
? ? public String sell()
? ? {
? ? ? ? return"韶關(guān)土特產(chǎn):香菇偿渡、木耳……";
? ? }
}//婺源網(wǎng)店classWuyuanShopimplements Shop
{
? ? public String sell()
? ? {
? ? ? ? return"婺源土特產(chǎn):綠茶臼寄、酒糟魚……";
? ? }
} //顧客class Customer
{
? ? publicvoid shopping(Shop shop)
? ? {
? ? ? ? //購物? ? ? ? System.out.println(shop.sell());
? ? }
}
程序的運行結(jié)果如下:
顧客購買以下商品:
韶關(guān)土特產(chǎn):香菇、木耳……
婺源土特產(chǎn):綠茶溜宽、酒糟魚……
接口隔離原則(接口)
接口隔離原則(Interface Segregation Principle吉拳,ISP)要求程序員盡量將臃腫龐大的接口拆分成更小的和更具體的接口,讓接口中只包含客戶感興趣的方法适揉。
2002 年羅伯特·C.馬丁給“接口隔離原則”的定義是:客戶端不應(yīng)該被迫依賴于它不使用的方法(Clients should not be forced to depend on methods they do not use)留攒。該原則還有另外一個定義:一個類對另一個類的依賴應(yīng)該建立在最小的接口上(The dependency of one class to another one should depend on the smallest possible interface)。
以上兩個定義的含義是:要為各個類建立它們需要的專用接口嫉嘀,而不要試圖去建立一個很龐大的接口供所有依賴它的類去調(diào)用炼邀。
接口隔離原則和單一職責(zé)都是為了提高類的內(nèi)聚性、降低它們之間的耦合性剪侮,體現(xiàn)了封裝的思想拭宁,但兩者是不同的:
單一職責(zé)原則注重的是職責(zé),而接口隔離原則注重的是對接口依賴的隔離票彪。
單一職責(zé)原則主要是約束類红淡,它針對的是程序中的實現(xiàn)和細(xì)節(jié);接口隔離原則主要約束接口降铸,主要針對抽象和程序整體框架的構(gòu)建。
接口隔離原則的優(yōu)點
接口隔離原則是為了約束接口摇零、降低類對接口的依賴性推掸,遵循接口隔離原則有以下 5 個優(yōu)點。
將臃腫龐大的接口分解為多個粒度小的接口驻仅,可以預(yù)防外來變更的擴(kuò)散谅畅,提高系統(tǒng)的靈活性和可維護(hù)性。
接口隔離提高了系統(tǒng)的內(nèi)聚性噪服,減少了對外交互毡泻,降低了系統(tǒng)的耦合性。
如果接口的粒度大小定義合理粘优,能夠保證系統(tǒng)的穩(wěn)定性仇味;但是呻顽,如果定義過小,則會造成接口數(shù)量過多丹墨,使設(shè)計復(fù)雜化廊遍;如果定義太大,靈活性降低贩挣,無法提供定制服務(wù)喉前,給整體項目帶來無法預(yù)料的風(fēng)險。
使用多個專門的接口還能夠體現(xiàn)對象的層次王财,因為可以通過接口的繼承卵迂,實現(xiàn)對總接口的定義。
能減少項目工程中的代碼冗余绒净。過大的大接口里面通常放置許多不用的方法狭握,當(dāng)實現(xiàn)這個接口的時候,被迫設(shè)計冗余的代碼疯溺。
接口隔離原則的實現(xiàn)方法
在具體應(yīng)用接口隔離原則時论颅,應(yīng)該根據(jù)以下幾個規(guī)則來衡量。
接口盡量小囱嫩,但是要有限度恃疯。一個接口只服務(wù)于一個子模塊或業(yè)務(wù)邏輯。
為依賴接口的類定制服務(wù)墨闲。只提供調(diào)用者需要的方法今妄,屏蔽不需要的方法。
了解環(huán)境鸳碧,拒絕盲從盾鳞。每個項目或產(chǎn)品都有選定的環(huán)境因素,環(huán)境不同瞻离,接口拆分的標(biāo)準(zhǔn)就不同深入了解業(yè)務(wù)邏輯腾仅。
提高內(nèi)聚,減少對外交互套利。使接口用最少的方法去完成最多的事情推励。
對于接口隔離,大家還是可以參考單一職責(zé)提到的示例:
publicinterface UserService {
? ? publicvoid login(String username, String password);
? ? publicvoid register(String email, String username, String password);
? ? publicvoid logError(String msg);
? ? publicvoid sendEmail(String email);
}
這時候肉迫,應(yīng)該就能理解拆分的好處了验辞。
迪米特法則 (類與類之間的關(guān)系)
迪米特法則(Law of Demeter,LoD)又叫作最少知識原則(Least Knowledge Principle喊衫,LKP)跌造,產(chǎn)生于 1987 年美國東北大學(xué)(Northeastern University)的一個名為迪米特(Demeter)的研究項目,由伊恩·荷蘭(Ian Holland)提出族购,被 UML 創(chuàng)始者之一的布奇(Booch)普及壳贪,后來又因為在經(jīng)典著作《程序員修煉之道》(The Pragmatic Programmer)提及而廣為人知陵珍。
迪米特法則的定義是:只與你的直接朋友交談,不跟“陌生人”說話(Talk only to your immediate friends and not to strangers)撑碴。其含義是:如果兩個軟件實體無須直接通信撑教,那么就不應(yīng)當(dāng)發(fā)生直接的相互調(diào)用,可以通過第三方轉(zhuǎn)發(fā)該調(diào)用醉拓。其目的是降低類之間的耦合度伟姐,提高模塊的相對獨立性。
迪米特法則中的“朋友”是指:當(dāng)前對象本身亿卤、當(dāng)前對象的成員對象愤兵、當(dāng)前對象所創(chuàng)建的對象、當(dāng)前對象的方法參數(shù)等排吴,這些對象同當(dāng)前對象存在關(guān)聯(lián)秆乳、聚合或組合關(guān)系,可以直接訪問這些對象的方法钻哩。
迪米特法則的優(yōu)點
迪米特法則要求限制軟件實體之間通信的寬度和深度屹堰,正確使用迪米特法則將有以下兩個優(yōu)點。
降低了類之間的耦合度街氢,提高了模塊的相對獨立性扯键。
由于親合度降低,從而提高了類的可復(fù)用率和系統(tǒng)的擴(kuò)展性珊肃。
但是荣刑,過度使用迪米特法則會使系統(tǒng)產(chǎn)生大量的中介類,從而增加系統(tǒng)的復(fù)雜性伦乔,使模塊之間的通信效率降低厉亏。所以,在釆用迪米特法則時需要反復(fù)權(quán)衡烈和,確保高內(nèi)聚和低耦合的同時爱只,保證系統(tǒng)的結(jié)構(gòu)清晰。
迪米特法則的實現(xiàn)方法
從迪米特法則的定義和特點可知斥杜,它強(qiáng)調(diào)以下兩點:
從依賴者的角度來說虱颗,只依賴應(yīng)該依賴的對象。
從被依賴者的角度說蔗喂,只暴露應(yīng)該暴露的方法。
所以高帖,在運用迪米特法則時要注意以下 6 點缰儿。
在類的劃分上,應(yīng)該創(chuàng)建弱耦合的類散址。類與類之間的耦合越弱乖阵,就越有利于實現(xiàn)可復(fù)用的目標(biāo)宣赔。
在類的結(jié)構(gòu)設(shè)計上,盡量降低類成員的訪問權(quán)限瞪浸。
在類的設(shè)計上儒将,優(yōu)先考慮將一個類設(shè)置成不變類。
在對其他類的引用上对蒲,將引用其他對象的次數(shù)降到最低钩蚊。
不暴露類的屬性成員,而應(yīng)該提供相應(yīng)的訪問器(set 和 get 方法)蹈矮。
謹(jǐn)慎使用序列化(Serializable)功能
明星與經(jīng)紀(jì)人的關(guān)系實例砰逻。
分析:明星由于全身心投入藝術(shù),所以許多日常事務(wù)由經(jīng)紀(jì)人負(fù)責(zé)處理泛鸟,如與粉絲的見面會蝠咆,與媒體公司的業(yè)務(wù)洽淡等。這里的經(jīng)紀(jì)人是明星的朋友北滥,而粉絲和媒體公司是陌生人刚操,所以適合使用迪米特法則,其類圖如下圖所示再芋。
代碼如下:
package principle;publicclass LoDtest
{
? ? publicstaticvoid main(String[] args)
? ? {
? ? ? ? Agent agent=new Agent();
? ? ? ? agent.setStar(newStar("林心如"));
? ? ? ? agent.setFans(newFans("粉絲韓丞"));
? ? ? ? agent.setCompany(newCompany("中國傳媒有限公司"));
? ? ? ? agent.meeting();
? ? ? ? agent.business();
? ? }
}//經(jīng)紀(jì)人class Agent
{
? ? private Star myStar;
? ? private Fans myFans;
? ? private Company myCompany;
? ? publicvoid setStar(Star myStar)
? ? {
? ? ? ? this.myStar=myStar;
? ? }
? ? publicvoid setFans(Fans myFans)
? ? {
? ? ? ? this.myFans=myFans;
? ? }
? ? publicvoid setCompany(Company myCompany)
? ? {
? ? ? ? this.myCompany=myCompany;
? ? }
? ? publicvoid meeting()
? ? {
? ? ? ? System.out.println(myFans.getName()+"與明星"+myStar.getName()+"見面了菊霜。");
? ? }
? ? publicvoid business()
? ? {
? ? ? ? System.out.println(myCompany.getName()+"與明星"+myStar.getName()+"洽淡業(yè)務(wù)。");
? ? }
}//明星class Star
{
? ? private String name;
? ? Star(String name)
? ? {
? ? ? ? this.name=name;
? ? }
? ? public String getName()
? ? {
? ? ? ? return name;
? ? }
}//粉絲class Fans
{
? ? private String name;
? ? Fans(String name)
? ? {
? ? ? ? this.name=name;
? ? }
? ? public String getName()
? ? {
? ? ? ? return name;
? ? }
}//媒體公司class Company
{
? ? private String name;
? ? Company(String name)
? ? {
? ? ? ? this.name=name;
? ? }
? ? public String getName()
? ? {
? ? ? ? return name;
? ? }
}
程序的運行結(jié)果如下:
粉絲韓丞與明星林心如見面了祝闻。
中國傳媒有限公司與明星林心如洽淡業(yè)務(wù)占卧。
?到此,設(shè)計模式的六大原則就講完了联喘。喜歡的朋友請點個贊加個關(guān)注支持下小編华蜒,謝謝啦。