設(shè)計模式六大原則

一款優(yōu)秀軟件離不開一個優(yōu)秀的架構(gòu)搅荞,一個優(yōu)秀的架構(gòu)也離不開設(shè)計模式红氯,在學(xué)習(xí)設(shè)計模式之前,我們需要學(xué)習(xí)并掌握代碼設(shè)計的基本原則咕痛,打好基礎(chǔ)從設(shè)計模式的六大原則開始痢甘,六大原則如下:

  • 單一職責(zé)原則
  • 里氏替換原則
  • 依賴倒置原則
  • 接口隔離原則
  • 迪米特原則
  • 開閉原則

下面是對六大原則的具體介紹:

單一職責(zé)原則

單一職責(zé)原則的英文名稱是Single Responsibility Principe,縮寫是SRP茉贡。定義:就一個類而言塞栅,不要存在多于一個導(dǎo)致類變更的原因。通俗地講块仆,就是一個類中應(yīng)該是一組與其職責(zé)相關(guān)性很高的方法和數(shù)據(jù)的封裝构蹬,而不去承擔(dān)多余的職責(zé)。
例如一個用戶系統(tǒng)悔据,有姓名和年齡兩個屬性:

public class Person {
    private String name;
    private String age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

    public void changeAge(String age) {
        //...
    }

    public void changeName(String age) {
        //...
    }
}

在這個類中庄敛,name和age的set,get方法是是屬于數(shù)據(jù)類型也就是業(yè)務(wù)對象的相關(guān)方法科汗,但是changeAge和changeName需要跟服務(wù)器進行交互藻烤,是屬于業(yè)務(wù)邏輯相關(guān)方法,根據(jù)單一職責(zé)原則头滔,我們需要將業(yè)務(wù)和數(shù)據(jù)分開:

public class Person {
    private String name;
    private String age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }
}
public class PersonLogic {
    public void changeAge(String age) {
        //...
    }
    public void changeName(String age) {
        //...
    }
}

優(yōu)點:可以降低類的復(fù)雜度怖亭,一個類只負責(zé)一項職責(zé);提高類的可讀性坤检,提高系統(tǒng)的可維護性兴猩;當(dāng)修改一個功能時,可以顯著降低對其他功能的影響早歇。

里氏替換原則

里氏替換原則的英文名稱是Liskov Substitution Principle倾芝,縮寫是LSP。定義:所有引用基類(父類)的地方必須能透明地使用其子類的對象箭跳。通俗地講晨另,所有使用父類的地方都可以替換為子類,不會產(chǎn)生任何錯誤和異常谱姓,但反過來替換就不行了借尿,子類出現(xiàn)的地方未必能用父類替換。

根據(jù)定義我們可以這樣理解:

  • 子類必須完全實現(xiàn)父類的方法
  • 子類可以有自己的個性
  • 覆寫或?qū)崿F(xiàn)父類的方法時輸入?yún)?shù)可以寬于或等于父類參數(shù)
  • 覆寫或?qū)崿F(xiàn)父類的方法時輸出結(jié)果可以窄于或等于父類參數(shù)

看下面參數(shù)替換的例子:

public class Father {

    public void printf(ArrayList list) {
        System.out.printf("父類方法");
    }

    public List printf() {
        System.out.printf("父類方法");
        return new ArrayList();
    }

    public void printf(HashMap map) {
        System.out.printf("父類方法");
    }
}
public class Son extends Father {

    @Override
    public void printf(ArrayList list) {
        System.out.printf("子類方法--重寫父類方法");
    }

    @Override
    public ArrayList printf() {
        System.out.printf("子類方法--重寫父類方法");
        // 在子類重寫父類方法中,返回值必須窄于或等于父類返回值類型
        return new ArrayList();
    }

    // 重載方法--輸入?yún)?shù)應(yīng)該寬于或等于父類參數(shù)路翻,以免造成子類替換父類產(chǎn)生邏輯混亂
    public void printf(Map map) {
        System.out.printf("子類方法");
    }
}

優(yōu)點:提高代碼的可擴展性狈癞。

依賴倒置原則

依賴倒置原則的英文名稱是Dependence Inversion Principle,縮寫是DIP帚桩。定義:模塊間的依賴通過抽象來產(chǎn)生亿驾,實現(xiàn)類之間不直接產(chǎn)生依賴關(guān)系,他們的依賴關(guān)系是通過接口或抽象類產(chǎn)生的账嚎。通俗地講莫瞬,就是面向接口或抽象類編程。
例如郭蕉,父親給孩子講故事疼邀,需要給他一本書,或一份報紙:

class Book{  
    public String get(){  
        return "書";  
    }  
}  

 class NewsPaper{  
    public String get(){  
        return "報紙";  
    }  
}  

class Father{  
    public void read(Book book){  
        System.out.println("爸爸讀"+book.get());  
    }  
public void read(NewsPaper news){  
        System.out.println("爸爸讀"+news.get());  
    }  
}  

public class Client {  
    public static void main(String[] args){  
        Father f = new Father();  
        f.read(new Book()); 
         f.read(new NewsPaper());   
    }  
}

如果再新增一個可讀的東西召锈,如Magazine(雜志)旁振,那我們要重新寫一個Magazine類,同時在Father類中還要新增一個read方法涨岁。所以我們可以這樣優(yōu)化一下:

interface IReader{  
    public String get();  
}

然后讓Book Newspaper Magazine類都實現(xiàn)這個接口拐袜,父類則可簡化為:

class Father{  
    public void read(IReader reader){  
        System.out.println("爸爸讀"+reader.get());  
    }  
}

優(yōu)點:提高代碼的可變性,簡化代碼邏輯梢薪。

接口隔離原則

接口隔離原則的英文名稱是Interface Segregation Principle蹬铺,縮寫是ISP昼捍。定義:一個類對另一個類的依賴應(yīng)該建立在最小的接口上污桦。通俗地講,盡量建立單一細化的接口阱持,接口中的方法盡量少琐馆,而不要建立龐大臃腫的接口规阀,也就是說我們要為各個類建立專用的接口,而不要試圖建立一個很龐大的接口供所有依賴它的類調(diào)用瘦麸,采樣接口隔離原則對接口進行約束谁撼,在一個類去實現(xiàn)接口的時候,不應(yīng)該去實現(xiàn)他不需要的方法滋饲。

常用作法:
1.為依賴接口的類提供定制服務(wù)彤敛,只暴露給類它需要的方法,不需要的方法則隱藏起來了赌。
2.提交內(nèi)聚,減少對外交互玄糟,接口方法盡量少用public修飾勿她,接口是對外的承諾,方法越少對變更產(chǎn)生的風(fēng)險越小阵翎。

interface I {  
    public void method1();  
    public void method2();  
    public void method3();  
    public void method4();  
    public void method5();  
}  

class A{  
    public void depend1(I i){  
        i.method1();  
    }  
    public void depend2(I i){  
        i.method2();  
    }  
    public void depend3(I i){  
        i.method3();  
    }  
}  

class B implements I{  
    public void method1() {  
        System.out.println("類B實現(xiàn)接口I的方法1");  
    }  
    public void method2() {  
        System.out.println("類B實現(xiàn)接口I的方法2");  
    }  
    public void method3() {  
        System.out.println("類B實現(xiàn)接口I的方法3");  
    }  
    public void method4() {}  
    public void method5() {}  
}  

class C{  
    public void depend1(I i){  
        i.method1();  
    }  
    public void depend2(I i){  
        i.method4();  
    }  
    public void depend3(I i){  
        i.method5();  
    }  
}  

class D implements I{  
    public void method1() {  
        System.out.println("類D實現(xiàn)接口I的方法1");  
    }  
    //對于類D來說逢并,method2和method3不是必需的之剧,但是由于接口A中有這兩個方法,  
    //所以在實現(xiàn)過程中即使這兩個方法的方法體為空砍聊,也要將這兩個沒有作用的方法進行實現(xiàn)背稼。  
    public void method2() {}  
    public void method3() {}  

    public void method4() {  
        System.out.println("類D實現(xiàn)接口I的方法4");  
    }  
    public void method5() {  
        System.out.println("類D實現(xiàn)接口I的方法5");  
    }  
}  

public class Client{  
    public static void main(String[] args){  
        A a = new A();  
        a.depend1(new B());  
        a.depend2(new B());  
        a.depend3(new B());  

        C c = new C();  
        c.depend1(new D());  
        c.depend2(new D());  
        c.depend3(new D());  
    }  
}

可以看到,如果接口過于臃腫玻蝌,只要接口中出現(xiàn)的方法蟹肘,不管對依賴于它的類有沒有用處,實現(xiàn)類中都必須去實現(xiàn)這些方法俯树,這顯然不是好的設(shè)計帘腹。如果將這個設(shè)計修改為符合接口隔離原則,就必須對接口I進行拆分许饿。在這里我們將原有的接口I按合理的方式拆分為三個接口阳欲。

interface I1 {  
    public void method1();  
}  

interface I2 {  
    public void method2();  
    public void method3();  
}  

interface I3 {  
    public void method4();  
    public void method5();  
}  

class A{  
    public void depend1(I1 i){  
        i.method1();  
    }  
    public void depend2(I2 i){  
        i.method2();  
    }  
    public void depend3(I2 i){  
        i.method3();  
    }  
}  

class B implements I1, I2{  
    public void method1() {  
        System.out.println("類B實現(xiàn)接口I1的方法1");  
    }  
    public void method2() {  
        System.out.println("類B實現(xiàn)接口I2的方法2");  
    }  
    public void method3() {  
        System.out.println("類B實現(xiàn)接口I2的方法3");  
    }  
}  

class C{  
    public void depend1(I1 i){  
        i.method1();  
    }  
    public void depend2(I3 i){  
        i.method4();  
    }  
    public void depend3(I3 i){  
        i.method5();  
    }  
}  

class D implements I1, I3{  
    public void method1() {  
        System.out.println("類D實現(xiàn)接口I1的方法1");  
    }  
    public void method4() {  
        System.out.println("類D實現(xiàn)接口I3的方法4");  
    }  
    public void method5() {  
        System.out.println("類D實現(xiàn)接口I3的方法5");  
    }  
}

優(yōu)點:系統(tǒng)解耦和,有利于代碼的重構(gòu)陋率、更改和重新部署球化。

迪米特原則

迪米特原則的英文全稱是Law of Demeter,縮寫是LOD瓦糟,也稱為最少知識原則(Least Knowledge Principle)筒愚。定義:一個軟件實體應(yīng)當(dāng)盡可能少地與其他實體發(fā)生相互作用。通俗地講狸页,就是盡量減少對象之間的交互锨能,如果兩個對象之間不必直接通信,那么這兩個對象就不應(yīng)該發(fā)生任何直接的相互作用芍耘。

例如址遇,一個人要租房,要求了房間的面積和租金斋竞,中介將符合我們要求的房間提供給我們就可以:

/**
 * 房間
 */
public class Room {
    public float area;
    public float price;

    public Room(float area, float price){
        this.area = area;
        this.price = price;
    }

    @Override
    public String toString() {
        return "Room{" + "area=" + area + ", price=" + price + '}';
    }
}

/**
 * 中介
 */
public class Mediator {
    private List<Room> mRooms = new ArrayList<>();

    public Mediator() {
        for (int i = 0; i < 5; i++) {
            mRooms.add(new Room(10 + i, (10 + i) * 150));
        }
    }

    public List<Room> getAllRooms() {
        return mRooms;
    }
}

/**
 * 租戶
 */
public class Tenant {
    public float minArea  = 20f;
    public float maxArea  = 100f;
    public float minPrice = 100f;
    public float maxPrice = 1000f;

    public void rentRoom(Mediator mediator) {
        List<Room> rooms = mediator.getAllRooms();
        for (Room room : rooms) {
            if (isSuitable(room)) {
                System.out.print("租到房子啦:" + room);
                break;
            }
        }
    }

    private boolean isSuitable(Room room) {
        return room.price > minPrice && room.price < maxPrice && room.area > minArea && room.area < minArea;
    }
}

從上面可以看出倔约,Tenant不僅依賴了Mediator類,還頻繁地與Room類打交道坝初,當(dāng)Room變化時Tenant也必須跟著變化浸剩,而我們只是要通過中介租房罷了,所以我們真正的“朋友”就只有Mediator鳄袍,修改后如下:

/**
 * 中介
 */
public class Mediator {
    private List<Room> mRooms = new ArrayList<>();

    public Mediator() {
        for (int i = 0; i < 5; i++) {
            mRooms.add(new Room(14 + i, (14 + i) * 150));
        }
    }

    public Room rentOut(float minArea, float maxArea, float minPrice, float maxPrice) {
        for (Room room : mRooms) {
            if (isSuitable(room, minArea, maxArea, minPrice, maxPrice)) {
                return room;
            }
        }
        return null;
    }

    private boolean isSuitable(Room room, float minArea, float maxArea, float minPrice, float maxPrice) {
        return room.price > minPrice && room.price < maxPrice && room.area > minArea && room.area < minArea;
    }
}

/**
 * 租戶
 */
public class Tenant {
    public float minArea  = 20f;
    public float maxArea  = 100f;
    public float minPrice = 500f;
    public float maxPrice = 1000f;

    public void rentRoom(Mediator mediator){
        System.out.print("租到房子啦:"+mediator.rentOut(minArea, maxArea, minPrice, maxPrice));
    }
}

優(yōu)點:降低耦合绢要,提高可擴展性。

開閉原則

開閉原則的英文名稱是Open Close Principle拗小,縮寫是OCP重罪。定義:軟件中的對象(類、模塊、函數(shù)等)對于擴展是允許的剿配,但是對于修改是不允許的搅幅。通俗地講,如果有新的需求呼胚,不能去修改實現(xiàn)類的具體實現(xiàn)茄唐,而應(yīng)該是新增創(chuàng)建一個實現(xiàn)了其公共接口的子類,在新的子類中做新需求的實現(xiàn)蝇更。

例如如下常見例子:

public interface ICar {
    public String getName();
    public float getPrice();
}

public class Car implements ICar{
    private String name;
    private float price;

    public Car(String name,float price){
        this.name = name;
        this.price = price;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public float getPrice() {
        return price;
    }
}

當(dāng)有一天我們獲取車的價格需要打折時沪编,可以重新寫一個類SaleCar:

public class SaleCar extends Car{
    public SaleCar(String name, float price) {
        super(name, price);
    }

    @Override
    public float getPrice() {
        return super.getPrice()*8/10;
    }
}

優(yōu)點:讓程序更穩(wěn)定,更靈活簿寂,當(dāng)有新功能出現(xiàn)的時候漾抬,可以在不修改原有的邏輯的基礎(chǔ)上,實現(xiàn)一個新的類常遂,這樣原有的邏輯沒有變纳令,新的需求也實現(xiàn)了。當(dāng)有一天出現(xiàn)bug了克胳,可以直接修改這一個類就可以平绩。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市漠另,隨后出現(xiàn)的幾起案子捏雌,更是在濱河造成了極大的恐慌,老刑警劉巖笆搓,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件性湿,死亡現(xiàn)場離奇詭異,居然都是意外死亡满败,警方通過查閱死者的電腦和手機肤频,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來算墨,“玉大人宵荒,你說我怎么就攤上這事【秽郑” “怎么了报咳?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長挖藏。 經(jīng)常有香客問我暑刃,道長,這世上最難降的妖魔是什么膜眠? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任岩臣,我火速辦了婚禮袁翁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘婿脸。我一直安慰自己,他們只是感情好柄驻,可當(dāng)我...
    茶點故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布狐树。 她就那樣靜靜地躺著,像睡著了一般鸿脓。 火紅的嫁衣襯著肌膚如雪抑钟。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天野哭,我揣著相機與錄音在塔,去河邊找鬼。 笑死拨黔,一個胖子當(dāng)著我的面吹牛蛔溃,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播篱蝇,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼贺待,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了零截?” 一聲冷哼從身側(cè)響起麸塞,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎涧衙,沒想到半個月后哪工,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡弧哎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年雁比,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片傻铣。...
    茶點故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡章贞,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出非洲,到底是詐尸還是另有隱情鸭限,我是刑警寧澤,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布两踏,位于F島的核電站败京,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏梦染。R本人自食惡果不足惜赡麦,卻給世界環(huán)境...
    茶點故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一朴皆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧泛粹,春花似錦遂铡、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至们衙,卻和暖如春钾怔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蒙挑。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工宗侦, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人忆蚀。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓矾利,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蜓谋。 傳聞我的和親對象是個殘疾皇子梦皮,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,440評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 設(shè)計模式之六大原則(轉(zhuǎn)載) 關(guān)于設(shè)計模式的六大設(shè)計原則的資料網(wǎng)上很多...
    霄霄霄霄閱讀 898評論 0 1
  • 前言 設(shè)計模式六大原則網(wǎng)上資料比較多比較亂,本文將網(wǎng)上的一些好的資料做一下整理桃焕,以便隨時翻閱剑肯。友情提示,設(shè)計模式雖...
    簡單的土豆閱讀 1,430評論 0 10
  • 什么是設(shè)計模式观堂?設(shè)計模式(Design Pattern)是一套被反復(fù)使用让网、多數(shù)人知曉的、經(jīng)過分類的师痕、代碼設(shè)計經(jīng)驗的...
    星星_點燈閱讀 395評論 0 0
  • 設(shè)計模式六大原則 設(shè)計模式六大原則(1):單一職責(zé)原則 定義:不要存在多于一個導(dǎo)致類變更的原因溃睹。通俗的說,即一個類...
    viva158閱讀 764評論 0 1
  • 本文參加#感悟三下鄉(xiāng)胰坟,青春筑夢行#活動因篇,本人承諾,文章內(nèi)容為原創(chuàng)笔横,且未在其他平臺發(fā)表過竞滓。 7月10日,我來到了德州...
    wuli恬恬閱讀 391評論 8 14