軟件設(shè)計(jì)七大原則(上)

前言

僅供學(xué)習(xí)和參考,文章內(nèi)容來自相關(guān)書籍和搜索引擎以及一些個(gè)人的思考和心得。如有紕漏剔桨,歡迎指正讥珍。

七大原則

① 開閉原則

② 依賴倒置原則

③ 單一職責(zé)原則

④ 接口隔離原則

⑤ 迪米特法則

⑥ 里氏替換原則

⑦ 合成復(fù)用原則

開閉原則

開閉原則(Open-Closed Principle),對于一個(gè)軟件實(shí)體毁菱,應(yīng)該對拓展開放米死,對修改關(guān)閉锌历。對修改關(guān)閉即保證了軟件的一個(gè)完整性,保留了當(dāng)前版本的基本功能峦筒,在做版本更迭的時(shí)候也方便向下兼容究西。試想如果我們每次更新都直接對代碼進(jìn)行修改,那么需要新功能的用戶會進(jìn)行軟件更新勘天,但是那些不需要新功能的呢怔揩?他們也必須更新代碼,不然就會出問題脯丝。所以商膊,在保證了對修改關(guān)閉的同時(shí),也必須對拓展開放宠进,拓展性的是每個(gè)系統(tǒng)必要條件晕拆,因?yàn)槟阌肋h(yuǎn)不知道客戶會有什么需求 。
作用
1材蹬、通過擴(kuò)展已有軟件系統(tǒng),可以提供新的行為,以滿足對軟件新的需求,提高了軟件系統(tǒng)的適應(yīng)性和靈活性
2实幕、已有的軟件模塊,特別是重要的抽象層模塊不能再修改,提高了軟件系統(tǒng)的一定的穩(wěn)定性和延續(xù)性
3、這樣的設(shè)計(jì)同時(shí)也滿足了可復(fù)用性和可維護(hù)性

在實(shí)際開發(fā)中堤器,如何去實(shí)現(xiàn)開閉原則呢?

實(shí)現(xiàn)開閉原則的關(guān)鍵是抽象
1昆庇、橫向拓展
世界上的高級物種只有人類

public interface Human {
    String getName();
    String getSexLike();
    String getSex();
}

此時(shí),然后女媧首先捏了一個(gè)男娃兒

public class man implements Human{
    private String name;
    private String sexLike;
    private String sex;
    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getSexLike() {
        return sexLike;
    }

    @Override
    public String getSex() {
        return sex;
    }
}

然后女媧又捏了一個(gè)女娃兒闸溃,此時(shí)就可以進(jìn)行橫向拓展整吆,而不必修改man中的getSex()的方法讓他return "女"。因?yàn)閙an中的getSex()方法可能被其他方法調(diào)用辉川。

public class woman implements Human{
    private String name;
    private String sexLike;
    private String sex;
    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getSexLike() {
        return sexLike;
    }

    @Override
    public String getSex() {
        return sex;
    }
}

2表蝙、縱向拓展
此時(shí),女媧又捏了一個(gè)男娃兒乓旗,但是他喜歡男人府蛇。此時(shí)可以進(jìn)行縱向拓展

public class gay extends man{
    @Override
    public String getSexLike() {
        return "男";
    }
}

依賴倒置原則

依賴倒置原則(Dependency Inversion Principle)是指高層模塊不應(yīng)該依賴底層模塊,兩者都應(yīng)該依賴抽象屿愚,抽象不應(yīng)該依賴細(xì)節(jié)汇跨,細(xì)節(jié)應(yīng)該依賴抽象。其中:
高層模塊:調(diào)用者
底層模塊:被調(diào)用者
抽象:即是抽象類或者接口渺鹦,兩者是不能夠?qū)嵗?br> 細(xì)節(jié):即是具體的實(shí)現(xiàn)扰法,實(shí)現(xiàn)接口或者繼承抽象類的類
其實(shí)該原則更像是對開閉原則的一種具體實(shí)現(xiàn),如果高層模塊直接依賴具體實(shí)現(xiàn)毅厚,那么每一次修改的時(shí)候就必須修改具體實(shí)現(xiàn)類(耦合度太高)塞颁,這就違背了開閉原則。其實(shí)依賴倒置原則在我看來更像現(xiàn)在得房屋租賃。大多數(shù)房東不能夠直接去找到租房人員(高層模塊不應(yīng)該依賴底層模塊)祠锣,大多數(shù)租房人員也不容易找到房東酷窥,兩者都需要通過中介進(jìn)行交易(兩者都應(yīng)該依賴抽象),中介租房不需要管房子是什么樣子的伴网,只需要將它租出去(抽象不應(yīng)該依賴細(xì)節(jié))蓬推,但租房人員有需求,必須找到中介(細(xì)節(jié)應(yīng)該依賴抽象)澡腾。
但是我有一點(diǎn)不明白沸伏,這個(gè)原則為什么叫依賴倒置原則呢?"倒置"动分,不應(yīng)該是倒過來么毅糟?這更像是"中間商原則"。
作用
采用依賴倒置原則可以降低模塊之間的耦合性,提高系統(tǒng)的穩(wěn)定性,減少并行開發(fā)的風(fēng)險(xiǎn),提高代碼的可讀性和可維護(hù)性

在實(shí)際開發(fā)中澜公,如何去實(shí)現(xiàn)依賴倒置原則呢?

在我上述租房例子中姆另,房東就是高層模塊,租戶就是底層模塊坟乾,中介就是抽象迹辐,房子的類型就是細(xì)節(jié)
高層模塊在開發(fā)中就是main()方法,因?yàn)樗浅绦虻娜肟谏趼拢?fù)責(zé)調(diào)用其他明吩。

1、不使用依賴倒置原則
底層模塊——租戶

public class Renter {

    public void rentOneRoom(){
        System.out.println("租戶租到1室");
    }
    public void rentOneBadRoom(){
        System.out.println("租戶租到1室1廳");
    }
}

高層模塊——房東

public class Landlord {
    public static void main(String[] args) {
        Renter renter = new Renter();
        renter.rentOneRoom();
        renter.rentOneBadRoom();
    }
}

租戶租到1室
租戶租到1室1廳
如果此時(shí)租戶還想租兩室一廳呢殷费?我們又需要去Renter里添加租兩室一廳的方法贺喝,在landlord里增加兩室一廳的調(diào)用方法。這樣頻繁的添加代碼宗兼,會有一些風(fēng)險(xiǎn),影響系統(tǒng)的穩(wěn)定性氮采。

2殷绍、使用依賴倒置原則
定義抽象——中介

public interface Agent {
    void rent();
}

定義細(xì)節(jié)——一室一廳、一室鹊漠、兩室一廳(創(chuàng)建3個(gè)java文件主到,此處圖方便,代碼貼在一起)

public class OneBadRoom implements Agent{
    @Override
    public void rent() {
        System.out.println("租戶租到1室1廳");
    }
}

public class OneRoom implements Agent{
    @Override
    public void rent() {
        System.out.println("租戶租到1室");
    }
}

public class TwoBadRoom implements Agent{
    @Override
    public void rent() {
        System.out.println("租戶租到2室1廳");
    }
}

定義底層模塊——租戶

public class Renter {
    public void rent(Agent agent){
        agent.rent();
    }
}

定義高層模塊——房東

public class Landlord {
    public static void main(String[] args) {
        Renter renter = new Renter();
        renter.rent(new OneRoom());
        renter.rent(new OneBadRoom());
        renter.rent(new TwoBadRoom());
    }
}

租戶租到1室
租戶租到1室1廳
租戶租到2室1廳
這樣以后租戶想租新的房子只需要增加細(xì)節(jié)(房子的類型)即可躯概。而不需要修改底層代碼登钥。這種方式就叫做依賴注入。
其他依賴注入方式還有
構(gòu)造器方式
修改租戶Renter

public class Renter {
    private Agent agent;
    public Renter(Agent agent){
        this.agent = agent;
    }
    public void rent(){
        agent.rent();
    }
}

修改房東Loadlord

public class Landlord {
    public static void main(String[] args) {
        Renter renter = new Renter(new OneRoom());
        renter.rent();
    }
}

租戶租到1室
setter方式
修改租戶Renter

public class Renter {
    private Agent agent;

    public void setAgent(Agent agent){
        this.agent = agent;
    }
    public void rent(){
        agent.rent();
    }
}

修改房東Loadlord

public class Landlord {
    public static void main(String[] args) {
        Renter renter = new Renter();
        renter.setAgent(new OneRoom());
        renter.rent();
    }
}

租戶租到1室

單一職責(zé)原則

單一職責(zé)原則(Simple Responsibility Principle)娶靡,顧名思義牧牢,就是專人干專事。反過來想,如果系統(tǒng)出了問題塔鳍,有多種原因要去改一個(gè)類伯铣,那就說明這個(gè)類有多種職責(zé)。而且單一職責(zé)并不是單一功能轮纫。
作用
將功能分類腔寡,模塊劃分明確,修改一個(gè)模塊不會造成其他模塊的修改掌唾,降低模塊之間的耦合度
比如查找一個(gè)商品信息放前,前臺查找商品信息用于展示,后臺查詢商品信息用于管理糯彬。但是如下代碼既承擔(dān)了前臺查詢的職責(zé)凭语,又承擔(dān)了后臺查詢的職責(zé)。那么前臺需要展示的信息需要改變時(shí)情连,會對后臺也產(chǎn)生影響
1叽粹、不遵循單一職責(zé)原則

@Repository
public class Repository {
    @Select("<script>" +
            "select feild1,field2,field3 from table")
    List<XXX> getSomeThing();
}

2、遵循單一職責(zé)原則

@Repository
public class Repository {
    @Select("<script>" +
            "select feild1,field2,field3 from table")
    List<XXX> getSomeThingForProtal();

    @Select("<script>" +
            "select feild1,field4,field5 from table")
    List<XXX> getSomeThingForBackend();
}

創(chuàng)建兩個(gè)方法分別服務(wù)前臺后臺却舀,這就滿足了粗粒度的單一職責(zé)原則虫几。
我們常說的高內(nèi)聚低耦合原則其實(shí)就是單一職責(zé)的具體體現(xiàn)。單一職責(zé)原則是最簡單也非常難實(shí)現(xiàn)的原則

接口隔離原則

接口隔離原則(interface Segregation Principle)是指用多個(gè)專門的接口挽拔,而不使用單一的總接口辆脸,客戶端不應(yīng)該只依賴它不需要的接口。這個(gè)原則是在接口設(shè)計(jì)時(shí)的規(guī)范:
1螃诅、一個(gè)類對一類的依賴應(yīng)該建立在最小的接口之上啡氢。
2、建立單一接口术裸,不要建立龐大臃腫的接口倘是。
3、盡量細(xì)化接口袭艺,接口中的方法盡量少(不是越少越好搀崭,一定要適度)。
作用
防止龐大猾编,臃腫的接口瘤睹,避免接口污染,提高程序設(shè)計(jì)要求的細(xì)致劃分性答倡。降低大面積維護(hù)成本轰传,一旦出現(xiàn)接口污染,會造成實(shí)現(xiàn)類中存在大量的不相關(guān)不需要去重寫的方法

接口隔離原則符合我們常說的高內(nèi)聚低耦合的設(shè)計(jì)思想瘪撇,從而使得類具有很好的可讀性获茬、可擴(kuò)展性和可維護(hù)性港庄。我們在設(shè)計(jì)接口的時(shí)候,要多花時(shí)間去思考锦茁,要考慮業(yè)務(wù)模型攘轩,包括以后有可能發(fā)生變更的地方還要做一些預(yù)判。所以码俩,對于抽象度帮,對業(yè)務(wù)模型的理解是非常重要的。
1稿存、不遵循接口隔離原則
定義一個(gè)Animal類

public interface Animal {
    void eat();
    void fly();
    void swim();
}

鳥類實(shí)現(xiàn)類

public class Bird implements Animal{
    @Override
    public void eat() {}
    @Override
    public void fly() {}
    @Override
    public void swim() {}
}

狗類實(shí)現(xiàn)類

public class Dog implements Animal{
    @Override
    public void eat() {}
    @Override
    public void fly() {}
    @Override
    public void swim() {}
}

可以看出笨篷,Bird 的 swim()方法可能只能空著,Dog 的 fly()方法顯然不可能的瓣履。
2率翅、遵循接口隔離原則
這時(shí)候,我們針對不同動(dòng)物行為來設(shè)計(jì)不同的接口袖迎,分別設(shè)計(jì) IEatAnimal冕臭,IFlyAnimal 和ISwimAnimal 接口

public interface EatAnimal {
    void eat();
}
public interface FlyAnimal {
    void fly();
}
public interface SwimAnimal {
    void swim();
}

Dog 只實(shí)現(xiàn) IEatAnimal 和 ISwimAnimal 接口

public class Dog implements EatAnimal, SwimAnimal{
    @Override
    public void eat() {}
    @Override
    public void swim() {}
}

這樣拆分下來,就清晰很多燕锥。接口隔離原則也是高內(nèi)聚低耦合的思想辜贵,那么它跟接口單一職責(zé)有什么區(qū)別呢?
單一職責(zé)和接口隔離辨析
1归形、單一職責(zé)原則側(cè)重職責(zé),接口隔離側(cè)重對接口的依賴的隔離
2托慨、單一職責(zé)原則側(cè)重約束類,其次是接口,針對程序中實(shí)現(xiàn)的細(xì)節(jié)
3、接口隔離原則側(cè)重約束接口,主要針對抽象需求,針對程序的整體框架的構(gòu)建

轉(zhuǎn)載請注明出處
http://www.reibang.com/p/4615cfb77e97

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末暇榴,一起剝皮案震驚了整個(gè)濱河市厚棵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蔼紧,老刑警劉巖婆硬,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異奸例,居然都是意外死亡柿祈,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進(jìn)店門哩至,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蜜自,你說我怎么就攤上這事菩貌。” “怎么了重荠?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵箭阶,是天一觀的道長。 經(jīng)常有香客問我,道長仇参,這世上最難降的妖魔是什么嘹叫? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮诈乒,結(jié)果婚禮上罩扇,老公的妹妹穿的比我還像新娘。我一直安慰自己怕磨,他們只是感情好喂饥,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著肠鲫,像睡著了一般员帮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上导饲,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天捞高,我揣著相機(jī)與錄音,去河邊找鬼渣锦。 笑死硝岗,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的泡挺。 我是一名探鬼主播辈讶,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼娄猫!你這毒婦竟也來了贱除?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤媳溺,失蹤者是張志新(化名)和其女友劉穎月幌,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體悬蔽,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡扯躺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蝎困。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片录语。...
    茶點(diǎn)故事閱讀 40,137評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖禾乘,靈堂內(nèi)的尸體忽然破棺而出澎埠,到底是詐尸還是另有隱情,我是刑警寧澤始藕,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布蒲稳,位于F島的核電站氮趋,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏江耀。R本人自食惡果不足惜剩胁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望祥国。 院中可真熱鬧昵观,春花似錦、人聲如沸系宫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽扩借。三九已至椒惨,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間潮罪,已是汗流浹背康谆。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嫉到,地道東北人沃暗。 一個(gè)月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像何恶,于是被迫代替她去往敵國和親孽锥。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評論 2 355