Java常見設(shè)計(jì)模式

1.設(shè)計(jì)模式六大原則

  • 單一職責(zé)原則
    就一個(gè)類而言氨菇,應(yīng)該僅有一個(gè)引起它變化的原因牍陌。
    例如撬呢,在 Activity 中不應(yīng)該存在 Bean 文件吝梅、網(wǎng)絡(luò)數(shù)據(jù)處理以及列表的 Adapter虱疏。
  • 開放封閉原則
    類、模塊憔涉、函數(shù)等應(yīng)該是可以擴(kuò)展的订框,但是不可修改。
    開放封閉原則有兩個(gè)含義:一個(gè)是對于拓展是開放的兜叨,另一個(gè)是對于修改是封閉的穿扳。
  • 里氏替換原則
    所有引用基類(父類)的地方必須能夠透明地使用其子類的對象。
  • 依賴倒置原則
    高層模塊不應(yīng)該依賴低層模塊国旷,兩者都應(yīng)該依賴于抽象矛物。抽象不應(yīng)該依賴于細(xì)節(jié),細(xì)節(jié)應(yīng)該依賴于抽象跪但。
  • 迪米特原則
    一個(gè)軟件實(shí)體應(yīng)當(dāng)盡可能少地與其他實(shí)體發(fā)生相互作用履羞。
  • 接口隔離原則
    一個(gè)類對另一個(gè)類的依賴應(yīng)該建立在最小的接口上。

2. 設(shè)計(jì)模式分類

GoF 提出的設(shè)計(jì)模式總共有 23 種,根據(jù)目的準(zhǔn)則分類忆首,分為三大類爱榔。

  • 創(chuàng)建型設(shè)計(jì)模式,共 5 種:單例模式糙及、工廠方法模式详幽、抽象工廠模式、建造者模式浸锨、原型模式唇聘。
  • 結(jié)構(gòu)型設(shè)計(jì)模式,共 7 種:適配器模式柱搜、裝飾模式迟郎、代理模式、外觀模式聪蘸、橋接模式宪肖、組合模式、享元模式宇姚。
  • 行為型設(shè)計(jì)模式匈庭,共 11 種:策略模式、模板方法模式浑劳、觀察者模式阱持、迭代器模式、責(zé)任鏈模式魔熏、命令模式衷咽、備忘錄模式、狀態(tài)模式蒜绽、訪問者模式镶骗、中介者模式、解釋器模式躲雅。

另外鼎姊,隨著發(fā)展也涌現(xiàn)出很多新的設(shè)計(jì)模式:規(guī)格模式、對象池模式相赁、雇工模式相寇、黑板模式和空對象模式等。

2.1創(chuàng)建型設(shè)計(jì)模式

2.1.1 單例模式

單例模式的 7 種寫法:

http://www.reibang.com/p/e17f82e8ebc9(此鏈接純屬騙閱讀量钮科,不信你點(diǎn)開看看)

7種單例設(shè)計(jì)模式的設(shè)計(jì).png

2.1.2 工廠方法模式

定義:
定義一個(gè)用于創(chuàng)建對象的接口唤衫,讓子類決定實(shí)例化哪個(gè)類。工廠方法使一個(gè)類的實(shí)例化延遲到其子類绵脯。
結(jié)構(gòu)圖:

工廠方法模式結(jié)構(gòu)圖

角色:

  • Product:抽象產(chǎn)品類佳励。
  • ConcreteProduct:具體產(chǎn)品類休里,實(shí)現(xiàn) Product 接口。
  • Factory:抽象工廠類赃承,內(nèi)部方法返回一個(gè) Product 類型的對象妙黍。
  • ConcreteFactory:具體工廠類,返回 ConcreteProduct 實(shí)例瞧剖。

例:
(1)創(chuàng)建抽象工廠

public abstract class ComputerFactory {
    public abstract <T extends Computer> T createComputer(Class<T> clz);
}

(2)具體工廠

public class GDComputerFactory extends ComputerFactory {
    @Override
    public <T extends Computer> T createComputer(Class<T> clz) {
        Computer computer;
        String className = clz.getName();
        try {
            computer = Class.forName(className).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return (T) computer;
    }
}

2.1.3 創(chuàng)造者模式(生成器模式)

定義:
將一個(gè)復(fù)雜對象的構(gòu)建與它的表示分離废境,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示。
結(jié)構(gòu)圖:

創(chuàng)造者模式結(jié)構(gòu)圖

角色:

  • Director:導(dǎo)演類筒繁,負(fù)責(zé)安排已有模塊的順序,然后通知 Builder 開始建造巴元。
  • Builder:抽象 Builder 類毡咏,規(guī)范產(chǎn)品的組建,一般由子類實(shí)現(xiàn)逮刨。
  • ConcreteBuilder:具體建造者呕缭,實(shí)現(xiàn)抽象 Builder 類定義的所有方法,并且返回一個(gè)組建好的對象修己。
  • Product:產(chǎn)品類恢总。

使用場景

  • 當(dāng)創(chuàng)建復(fù)雜對象的算法應(yīng)該獨(dú)立于該對象的組成部分以及它們的裝配方式時(shí)。
  • 相同的方法睬愤,不同的執(zhí)行順序片仿,產(chǎn)生不同的事件結(jié)果時(shí)。
  • 多個(gè)部件或零件都可以被裝配到一個(gè)對象中尤辱,但是產(chǎn)生的運(yùn)行結(jié)果又不相同時(shí)砂豌。
  • 產(chǎn)品類非常復(fù)雜,或者產(chǎn)品類種的調(diào)用順序不同產(chǎn)生了不同的效能光督。
  • 在創(chuàng)建一些復(fù)雜的對象時(shí)阳距,這些對象的內(nèi)部組成構(gòu)件間的建造順序是穩(wěn)定的,但是對象的內(nèi)部組成構(gòu)件面臨著復(fù)雜的變化结借。

優(yōu)點(diǎn)

  • 使用建造者模式可以使客戶端不必知道產(chǎn)品內(nèi)部組成的細(xì)節(jié)筐摘。
  • 具體的建造者類之間是相互獨(dú)立的,容易擴(kuò)展船老。
  • 由于具體的建造者是獨(dú)立的咖熟,因此可以對建造過程逐步細(xì)化,而不對其他的模塊產(chǎn)生任何影響努隙。

缺點(diǎn)

  • 產(chǎn)生多余的 Builde 對象以及導(dǎo)演類球恤。

2.2 結(jié)構(gòu)型設(shè)計(jì)模式

結(jié)構(gòu)型設(shè)計(jì)模式是從程序的結(jié)構(gòu)上解決模塊之間的耦合問題。

2.2.1 代理模式

定義:
為其他對象提供一種代理以控制對這個(gè)對象的訪問荸镊。
結(jié)構(gòu)圖:

代理模式結(jié)構(gòu)圖

角色:

  • Subject:抽象主題類咽斧,聲明真是主題與代理的共同接口方法堪置。
  • RealSubject:真實(shí)主題類,代理類所代表的真實(shí)主題张惹∫ㄏ牵客戶端通過代理類間接地調(diào)用真實(shí)主題類的方法。
  • Proxy:代理類宛逗,持有對真實(shí)主題類的引用坎匿,在其所實(shí)現(xiàn)的接口方法種調(diào)用真實(shí)主題類種相應(yīng)的接口方法執(zhí)行。
  • Client:客戶端類雷激。
靜態(tài)代理

(1)抽象主題類

public interface IShopping {
    void buy();
}

(2)真實(shí)主題類

public class Xiaoming implements IShopping {
    @Override
    public void buy() {
        System.out.println("小明買東西");
    }
}

(3)代理類

public class Purchasing implements IShopping {
    private IShopping mShopping;
    public Purchasing(IShopping shopping) {
        mShopping = shopping;
    }
    @Override
    public void buy() {
        mShopping.buy();
    }
}

(4)客戶端類

public class Client {
    public static void main(String[] args) {
        IShopping xiaoming = new Xiaoming();
        IShopping purchasing = new Purchasing(xiaoming);
        purchasing.buy();
    }
}
動(dòng)態(tài)代理

在代碼運(yùn)行時(shí)通過反射來動(dòng)態(tài)地生成代理類對象替蔬,并確定到底來代理誰。

public class DynamicPurchasing implements InvocationHandler {
    private Object obj;
    public DynamicPurchasing(Object obj) {
        this.obj = obj;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(obj, args);
        if("buy".equals(method.getName())) {
            System.out.println("買東西");
        }
        return result;
    }
}
public class Client {
    public static void main(String[] args) {
        IShopping xiaoming = new Xiaoming();
        DynamicPurchasing dynamicPurchasing = new DynamicPurchasing(xiaoming);
        IShopping purchasing = (IShopping) Proxy.newProxyInstance(xiaoming.getClass.getClassLoader(),
            new Class[]{IShopping.class}, dynamicPurchasing);
        purchasing.buy();
    }
}

代理模式的類型
代理模式從編碼的角度來說可以分為靜態(tài)代理和動(dòng)態(tài)代理屎暇,而從使用范圍來講則可分為以下 4 種類型:

  • 遠(yuǎn)程代理:為一個(gè)對象在不同的地址空間提供局部代表承桥,這樣系統(tǒng)可以將 Server 部分的實(shí)現(xiàn)隱藏。
  • 虛擬代理:使用一個(gè)代理對象表示一個(gè)十分耗費(fèi)資源的對象根悼,并在真正需要時(shí)才創(chuàng)建凶异。
  • 安全代理:用來控制真實(shí)對象訪問時(shí)的權(quán)限。一般用于真實(shí)對象有不同的訪問權(quán)限時(shí)挤巡。
  • 智能指引:當(dāng)調(diào)用真實(shí)的對象時(shí)剩彬,代理處理另外一些事,比如計(jì)算真實(shí)對象的引用計(jì)數(shù)矿卑,當(dāng)該對象沒有引用時(shí)喉恋,可以自動(dòng)釋放它;或者訪問一個(gè)實(shí)際對象時(shí)粪摘,檢查是否已經(jīng)能夠鎖定它瀑晒,以確保其他對象不能改變它。

優(yōu)點(diǎn)

  • 真實(shí)主題類就是實(shí)現(xiàn)實(shí)際的業(yè)務(wù)邏輯徘意,不用關(guān)心其他非本職的工作苔悦。
  • 真實(shí)主題類隨時(shí)都會(huì)發(fā)生變化,但是因?yàn)樗鼘?shí)現(xiàn)了公共的接口椎咧,所有代理類可以不做任何修改就能夠使用玖详。

2.2.2 裝飾模式

定義
動(dòng)態(tài)地給一個(gè)對象添加一些額外的職責(zé),就增加功能來說勤讽,裝飾模式比生成子類更為靈活蟋座。
結(jié)構(gòu)圖

裝飾模式結(jié)構(gòu)圖

角色

  • Component:抽象組件,可以是接口或者抽象類脚牍,被裝飾的最原始的對象向臀。
  • ConcreteComponent:組件具體實(shí)現(xiàn)類。Component 的具體實(shí)現(xiàn)類诸狭,被裝飾的具體對象券膀。
  • Decorator:抽象裝飾著君纫,從外類來拓展 Component 類的功能,但對于 Component 來說無須知道 Decorator 的存在芹彬。在它的屬性種必然有一個(gè) private 變量指向 Component 抽象組件蓄髓。
  • ConcreteDecorator:裝飾者的具體實(shí)現(xiàn)類。

實(shí)現(xiàn)
楊過本身就會(huì)全真劍法舒帮,又有兩位前輩洪七公和歐陽鋒向楊過傳授打狗棒法和蛤蟆功会喝,這樣楊過除了會(huì)全真劍法,還會(huì)打狗棒法和蛤蟆功玩郊。洪七公和歐陽鋒就起到了“裝飾”楊過的作用肢执。
(1)抽象組件

public abstract class Swordman {
    public abstract void attackMagic();
}

(2)組件具體實(shí)現(xiàn)類

public class YangGuo extends Swordman {
    @Override
    public void attackMagic() {
        System.out.println("楊過使用全真劍法");
    }
}

(3)抽象裝飾者

public abstract class Master extends Swordman {
    private Swordman mSwordman;
    public Master(Swordman swordman) {
        this.mSwordman = swordman;
    }
    @Override
    public void attackMagic() {
        mSwordman.attackMagic();
    }
}

(4)裝飾者具體實(shí)現(xiàn)類

public class HongQigong extends Master {
    public HongQigong(Swordman swordman) {
        super(swordman);
    }
    public void teachAttackMagin() {
        System.out.println("洪七公教授打狗棒法");
        System.out.println("楊過使用打狗棒法");
    }
    @Override
    public void attackMagic() {
        super.attackMagic();
        teachAttackMagin();
    }
}

public class OuYangFeng extends Master {
    public OuYangFeng(Swordman swordman) {
        super(swordman);
    }
    public void teachAttackMagin() {
        System.out.println("歐陽鋒教授蛤蟆功");
        System.out.println("楊過使用蛤蟆功");
    }
    @Override
    public void attackMagic() {
        super.attackMagic();
        teachAttackMagin();
    }
}

(5)客戶端調(diào)用

public class Client {
    public static void main(String[] args) {
        YangGuo yangguo = new YangGuo();
        HongQigong hongqigong = new HongQigong(yangguo);
        hongqigong.attackMagic();
        OuYangFeng ouyangfeng = new OuYangFeng(yangguo);
        ouyangfeng.attackMagic();
    }
}

使用場景

  • 在不影響其他對象的情況下,以動(dòng)態(tài)译红、透明的方式給單個(gè)對象添加職責(zé)蔚万。
  • 需要?jiǎng)討B(tài)地給一個(gè)對象增加功能,這些功能可以動(dòng)態(tài)地撤銷临庇。
  • 當(dāng)不能采用繼承的方式對系統(tǒng)進(jìn)行擴(kuò)充或者采用繼承不利于系統(tǒng)擴(kuò)展和維護(hù)時(shí)。

優(yōu)點(diǎn)

  • 通過組合而非繼承的方式昵慌,動(dòng)態(tài)地?cái)U(kuò)展一個(gè)對象的功能假夺,在運(yùn)行時(shí)選擇不同的裝飾器,從而實(shí)現(xiàn)不同的行為斋攀。
  • 有效避免了使用繼承的方式擴(kuò)展對象功能而帶來的靈活性差已卷、子類無限制擴(kuò)張的問題。
  • 具體組件類與具體裝飾類可以獨(dú)立變化淳蔼,用戶可以根據(jù)需要增加新的具體組件類和具體裝飾類侧蘸,在使用時(shí)再對其進(jìn)行組合,原有代碼無需改變鹉梨,符合“開放封閉原則”讳癌。

缺點(diǎn)

  • 因?yàn)樗袑ο缶^承自 Component,所以如果 Component 內(nèi)部結(jié)構(gòu)發(fā)生改變存皂,則不可避免地影響所有子類(裝飾者和被裝飾者)晌坤。如果基類改變,則勢必影響對象的內(nèi)部旦袋。
  • 比繼承更加靈活機(jī)動(dòng)的特性骤菠,也同時(shí)意味著裝飾模式比繼承更加易于出錯(cuò),排錯(cuò)也很困難疤孕。對于多次裝飾的對象商乎,調(diào)試時(shí)尋找錯(cuò)誤可能需要逐級排查,較為繁瑣祭阀。所以鹉戚,只在必要時(shí)使用裝飾模式鲜戒。
  • 裝飾層數(shù)不能過多,否則會(huì)影響效率崩瓤。

2.2.3 外觀模式(門面模式)

定義
要求一個(gè)子系統(tǒng)的外部與內(nèi)部的通信必須通過一個(gè)統(tǒng)一的對象進(jìn)行袍啡。此模式提供一個(gè)高層的接口,使得子系統(tǒng)更易于使用却桶。
結(jié)構(gòu)圖

外觀模式結(jié)構(gòu)圖

角色

  • Facade:外觀類境输,知道哪些子系統(tǒng)類負(fù)責(zé)處理請求,將客戶端的請求代理給適當(dāng)?shù)淖酉到y(tǒng)對象颖系。
  • Subsystem:子系統(tǒng)類嗅剖,可以有一個(gè)或者多個(gè)子系統(tǒng)。實(shí)現(xiàn)子系統(tǒng)的功能嘁扼,處理外觀類指派的任務(wù)信粮,注意子系統(tǒng)不含有外觀類的引用。
    實(shí)現(xiàn)
    (1)子系統(tǒng)類
/**
 * 子系統(tǒng)招式
 */
public class ZhaoShi {
    public void TaiJiQuan() {
        System.out.println("使用招式太極拳");
    }
    public void QiShangQuan() {
        System.out.println("使用招式七傷拳");
    }
    public void ShengHuo() {
        System.out.println("使用招式圣火令");
    }
}

/**
 * 子系統(tǒng)內(nèi)功
 */
public class NeiGong {
    public void JiuYang() {
        System.out.println("使用九陽神功");
    }
    public void QianKun() {
        System.out.println("使用乾坤大挪移");
    }
}

/**
 * 子系統(tǒng)經(jīng)脈
 */
public class JingMai {
    public void jingmai() {
        System.out.println("開啟經(jīng)脈");
    }
}

(2)外觀類

/**
 * 外觀類張無忌
 */
public class ZhangWuJi {
    private JingMai jingmai;
    private ZhaoShi zhaoshi;
    private NeiGong neigong;
    public ZhangWuJi() {
        jingmai = new JingMai();
        zhaoshi = new ZhaoShi();
        neigong = new NeiGong();
    }
    public void QianKun() {
        jingmai.jingmai();
        neigong.QianKun();
    }
    public void QiShang() {
        jingmai.jingmai();
        neigong.JiuYang();
        zhaoshi.QiShangQuan();
    }
}

(3)客戶端調(diào)用

public class Client {
    public static void main(String[] args) {
        ZhangWuJi zhangwuji = new ZhangWuJi();
        zhangwuji.QianKun();
        zhangwuji.QiShang();
    }
}

使用場景

  • 構(gòu)建一個(gè)有層次結(jié)構(gòu)的子系統(tǒng)時(shí)趁啸,使用外觀模式定義子系統(tǒng)中每層的入口點(diǎn)强缘。如果子系統(tǒng)之間是相互依賴的,則可以讓其通過外觀接口進(jìn)行通信不傅,減少子系統(tǒng)之間的依賴關(guān)系旅掂。
  • 子系統(tǒng)往往會(huì)因?yàn)椴粩嗟刂貥?gòu)演化而變得越來越復(fù)雜,大多數(shù)的模式使用時(shí)也會(huì)產(chǎn)生很多很小的類访娶,這給外部調(diào)用它們的用戶程序帶來了使用上的困難商虐。我們可以使用外觀類提供一個(gè)簡單的接口,對外隱藏子系統(tǒng)的具體實(shí)現(xiàn)并隔離變化崖疤。
  • 當(dāng)維護(hù)一個(gè)遺留的大型系統(tǒng)時(shí)秘车,可能這個(gè)系統(tǒng)已經(jīng)非常難以維護(hù)和擴(kuò)展,但因?yàn)樗兄匾墓δ芙俸撸孕碌男枨蟊仨氁蕾囁E俊_@時(shí)可以使用外觀類,為設(shè)計(jì)粗糙或者復(fù)雜的遺留代碼提供一個(gè)簡單的接口权烧,讓新系統(tǒng)和外觀類交互疫向,而外觀類負(fù)責(zé)與遺留的代碼進(jìn)行交互。

優(yōu)點(diǎn)

  • 減少系統(tǒng)的相互依賴豪嚎,所有的依賴都是對外觀類的依賴搔驼,與子系統(tǒng)無關(guān)。
  • 對用戶隱藏了子系統(tǒng)的具體實(shí)現(xiàn)侈询,減少用戶對子系統(tǒng)的耦合舌涨。這樣即使具體的子系統(tǒng)發(fā)生了變化,用戶也不會(huì)感知到扔字。
  • 加強(qiáng)了安全性囊嘉,子系統(tǒng)種的方法如果不在外觀類種開通温技,就無法訪問到子系統(tǒng)種的方法。

缺點(diǎn)

  • 不符合開放封閉原則扭粱。如果業(yè)務(wù)出現(xiàn)變更舵鳞,則可能要直接修改外觀類。

2.2.4 享元模式

定義
使用共享對象有效地支持大量細(xì)粒度的對象琢蛤。
結(jié)構(gòu)圖

享元模式結(jié)構(gòu)圖

角色

  • Flyweight:抽象享元角色蜓堕,同時(shí)定義出對象的外部狀態(tài)和內(nèi)部狀態(tài)的接口或者實(shí)現(xiàn)。
  • ConcreteFlyweight:具體享元角色博其,實(shí)現(xiàn)抽象享元角色定義的業(yè)務(wù)套才。
  • FlyweightFactory:享元工廠,負(fù)責(zé)管理對象池和創(chuàng)建享元對象慕淡。

實(shí)現(xiàn)
“雙11“促銷中的商品信息背伴。
(1)抽象享元角色

public interface IGoods {
    public void showGoodsPrice(String name);
}

(2)具體享元角色

public class Goods implements IGoods {
    private String name;
    private String version;
    public Goods(String name) {
        this.name = name;
    }
    @Override
    public void showGoodsPrice(String version) {
        if("32G".equals(version)) {
            System.out.println("價(jià)格為5199元");
        } else if("128G".equals(version)) {
            System.out.println("價(jià)格為5999元");
        }
    }
}

(3)享元工廠

public class GoodsFactory {
    private static Map<String, Goods> pool = new HashMap<String, Goods>();
    public static Goods getGoods(String name) {
        if(pool.containsKey(name)) {
            System.out.println("使用緩存,key 為:" + name);
            return pool.get(name);
        } else {
            Goods goods = new Goods(name);
            pool.put(name, goods);
            System.out.println("創(chuàng)建商品峰髓,key 為:" + name);
            return goods;
        }
    }
}

(4)客戶端調(diào)用

public class Client {
    public static void main(String[] args) {
        Goods goods1 = GoodsFactory.getGoods("iPhone7");
        goods.showGoodsPrice("32G");
        Goods goods2 = GoodsFactory.getGoods("iPhone7");
        goods.showGoodsPrice("128G");
    }
}

使用場景

  • 系統(tǒng)種存在大量的相似對象傻寂。
  • 需要緩沖池的場景。

優(yōu)點(diǎn)

  • 享元模式是池技術(shù)的重要實(shí)現(xiàn)方式携兵,它可以減少應(yīng)用程序創(chuàng)建的對象數(shù)量崎逃,降低程序內(nèi)存的占用,提高程序的性能眉孩。

2.3 行為型設(shè)計(jì)模式

行為性模式主要處理類或?qū)ο笕绾谓换ヒ约叭绾畏峙渎氊?zé)。

2.3.1 策略模式

定義
定義一系列的算法勒葱,把每一個(gè)算法封裝起來浪汪,并且使它們可相互替換。策略模式使得算法可獨(dú)立于使用它的客戶而獨(dú)立變化凛虽。
結(jié)構(gòu)圖

策略模式結(jié)構(gòu)圖

角色

  • Context:上下文角色死遭,用來操作策略的上下文環(huán)境,起到承上啟下的作用凯旋,屏蔽高層模塊對策略呀潭、算法的直接訪問。
  • Strategy:抽象策略角色至非,策略钠署、算法的抽象,通常為接口荒椭。
  • ConcreteStrategy:具體的策略實(shí)現(xiàn)谐鼎。

實(shí)現(xiàn)

/**
 * 戰(zhàn)斗策略
 */
public interface FightingStrategy {
    public void fighting();
}

(2)具體策略實(shí)現(xiàn)

public class WeakRivalStrategy implements FightingStrategy {
    @Override
    public void fighting() {
        System.out.println("遇到了較弱的對手,張無忌使用太極劍");
    }
}

public class CommonRivalStrategy implements FightingStrategy {
    @Override
    public void fighting() {
        System.out.println("遇到了普通的對手趣惠,張無忌使用圣火令神功");
    }
}


public class StrongRivalStrategy implements FightingStrategy {
    @Override
    public void fighting() {
        System.out.println("遇到了強(qiáng)大的對手狸棍,張無忌使用乾坤大挪移");
    }
}

(3)上下文角色

public class Context {
    private FightingStrategy fightingStrategy;
    public Context(FightingStrategy fightingStrategy) {
        this.fightingStrategy = fightingStrategy;
    }
    public void fighting() {
        fightingStrategy.fighting();
    }
}

(4)客戶端調(diào)用

public class ZhangWuJi {
    public static void main(String[] args) {
        Context context = new Context(new WeakRivalStrategy());
        context.fighting();
    }
}

使用場景

  • 對客戶隱藏具體策略(算法)的實(shí)現(xiàn)細(xì)節(jié)身害,彼此完全獨(dú)立。
  • 針對同一類型問題的多種處理方式草戈,僅僅是具體行為有差別時(shí)塌鸯。
  • 在一個(gè)類中定義了很多行為,而且這些行為在這個(gè)類里的操作以多個(gè)條件語句的形式出現(xiàn)唐片,策略模式將相關(guān)的條件分支移入它們各自的 Strategy 類中丙猬,以替代這些條件語句。

優(yōu)點(diǎn)

  • 使用策略模式可以避免使用多重條件語句牵触。多重條件語句不易維護(hù)淮悼,而且易出錯(cuò)。
  • 易于擴(kuò)展揽思。當(dāng)需要添加一個(gè)策略時(shí)袜腥,只需要實(shí)現(xiàn)接口就可以了。

缺點(diǎn)

  • 每一個(gè)策略都是一個(gè)類钉汗,復(fù)用性小羹令。如果策略過多,類的數(shù)量會(huì)增多损痰。
  • 上層模塊必須知道有哪些策略福侈,才能夠使用這些策略,這與迪米特原則相違背卢未。

2.3.2 模板方法模式

定義
定義一個(gè)操作中的算法框架肪凛,而將一些步驟延遲到子類中,使得子類不改變一個(gè)算法的結(jié)構(gòu)即可定義算法的某些特定步驟辽社。
結(jié)構(gòu)圖

模板方法模式結(jié)構(gòu)圖

角色

  • AbstractClass:抽象類伟墙,定義了一套算法框架。
  • ConcreteClass:具體實(shí)現(xiàn)類滴铅。

實(shí)現(xiàn)
(1)創(chuàng)建抽象類戳葵,定義算法框架

public abstract class AbstractSwordman {
    public final void fighting() {
        neigong();
        meridian();
        if(hasWeapons()) {
            weapons();
        }
        moves();
        hook();
    }
    protected void hook() {};
    protected abstract void neigong();
    protected abstract void weapons();
    protected abstract void moves();
    protected void merdian() {
        System.out.println("開啟正經(jīng)與奇經(jīng)");
    }
    protected boolean hasWeapons() {
        return true;
    }
}

(2)具體實(shí)現(xiàn)類

public class ZhangWuJi extends AbstractSwordman {
    @Override
    protected void neigong() {
        System.out.println("運(yùn)行九陽神功");
    }
    @Override
    protected void weapons() {}
    @Override
    protected void moves() {
        System.out.println("使用乾坤大挪移");
    }
    @Override
    protected boolean hasWeapons() {
        return false;
    }
}

(3)客戶端調(diào)用

public class Client {
    public static void main(String[] args) {
        ZhangWuJi zhangwuji = new ZhangWuJi();
        zhangwuji.fighting();
    }
}

使用場景

  • 多個(gè)子類有共有的方法,并且邏輯基本相同時(shí)汉匙。
  • 面對重要拱烁、復(fù)雜的算法,可以把核心算法設(shè)計(jì)為模板方法噩翠,周邊相關(guān)細(xì)節(jié)功能則由各個(gè)子類實(shí)現(xiàn)戏自。
  • 需要通過子類來決定父類算法種的某個(gè)步驟是否執(zhí)行,實(shí)現(xiàn)子類對父類的反向控制伤锚。

優(yōu)點(diǎn)

  • 模板方法模式通過把不變的行為搬移到超類浦妄,去除了子類中重復(fù)代碼。
  • 子類實(shí)現(xiàn)算法的某些細(xì)節(jié),有助于算法的擴(kuò)展剂娄。

缺點(diǎn)

  • 每個(gè)不同的實(shí)現(xiàn)都需要定義一個(gè)子類蠢涝,這會(huì)導(dǎo)致類的個(gè)數(shù)的增加,設(shè)計(jì)更加抽象阅懦。

2.3.3 觀察者模式(發(fā)布-訂閱模式)

定義
定義對象間一種一對多的依賴關(guān)系和二,每當(dāng)一個(gè)對象改變狀態(tài)時(shí),則所有依賴于它的對象都會(huì)得到通知并被自動(dòng)更新耳胎。
結(jié)構(gòu)圖

觀察者模式結(jié)構(gòu)圖

角色

  • Subject:抽象主題(抽象被觀察者)惯吕。抽象主題角色把所有觀察者對象保存在一個(gè)集合里,每個(gè)主題都可以有任意數(shù)量的觀察者怕午。抽象主題提供接口废登,可以添加或者刪除觀察者對象。
  • ConcreteSubject:具體主題(具體被觀察者)郁惜。該角色將有關(guān)狀態(tài)存入具體觀察者對象堡距,在具體主題的內(nèi)部狀態(tài)發(fā)生改變時(shí),給所有注冊過的觀察者發(fā)送通知兆蕉。
  • Observer:抽象觀察者羽戒,是觀察者的抽象類。它定義了一個(gè)更新接口虎韵,使得在得到主題更改通知時(shí)更新自己易稠。
  • ConcreteObserver:具體觀察者,實(shí)現(xiàn)抽象觀察者定義的更新接口包蓝,以便在得到主題更改通知時(shí)更新自身的狀態(tài)驶社。

實(shí)現(xiàn)
微信某個(gè)公眾號的更新通知。
(1)抽象觀察者

public interface Observer {
    public void update(String message);
}

(2)具體觀察者

public class WeixinUser implements Observer {
    private String name;
    public WeixinUser(String name) {
        this.name = name;
    }
    @Override
     public void update(String message) {
        System.out.println(name + "-" + message);
     }
}

(3)抽象被觀察者

public interface Subject {
    public void attach(Observer observer);
    public void detach(Observer observer);
    public void notify(String message);
}

(4)具體被觀察者

public class SubscriptionSubject implements Subject {
    private List<Observer> weixinUserList = new ArrayList<Observer>();
    @Override
    public void attach(Observer observer) {
        weixinUserList.add(observer);
    }
    @Override
    public void detach(Observer observer) {
        weixinUserList.remove(observer);
    }
    @Override
    public void notify(String message) {
        for(Observer observer : weixinUserList) {
            observer.update(message);
        }
    }
}

(5)客戶端調(diào)用

public class Client {
    public static void main(String[] args) {
        SubscriptionSubject subject = new SubscriptionSubject();
        WeixinUser user1 = new WeixinUser("xiaoming");
        WeixinUser user2 = new WeixinUser("xiaohua");
        subject.attach(user1);
        subject.attach(user2);
        subject.notify("專欄更新");
    }
}

使用場景

  • 關(guān)聯(lián)行為場景测萎。需要注意的是亡电,關(guān)聯(lián)行為是可拆分的,而不是“組合”關(guān)系绳泉。
  • 事件多級觸發(fā)場景。
  • 跨系統(tǒng)的消息交換場景姆泻,如消息隊(duì)列零酪、事件總線的處理機(jī)制。

優(yōu)點(diǎn)

  • 觀察者和被觀察者之間是抽象耦合拇勃,容易擴(kuò)展四苇。
  • 方便建立一套觸發(fā)機(jī)制。

缺點(diǎn)

  • 在應(yīng)用觀察者模式時(shí)需要考慮一下開發(fā)效率和運(yùn)行效率的問題方咆。程序中包括一個(gè)被觀察者月腋、多個(gè)觀察者,開發(fā)、調(diào)試內(nèi)容比較復(fù)雜榆骚,而且在 Java 中消息的通知一般是順序執(zhí)行的片拍,那么一個(gè)觀察者卡頓,會(huì)影響整體的執(zhí)行效率妓肢,在這種情況下捌省,一般會(huì)采用異步方式。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末碉钠,一起剝皮案震驚了整個(gè)濱河市纲缓,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌喊废,老刑警劉巖祝高,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異污筷,居然都是意外死亡工闺,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門颓屑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來斤寂,“玉大人,你說我怎么就攤上這事揪惦”楦悖” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵器腋,是天一觀的道長溪猿。 經(jīng)常有香客問我,道長纫塌,這世上最難降的妖魔是什么诊县? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮措左,結(jié)果婚禮上依痊,老公的妹妹穿的比我還像新娘。我一直安慰自己怎披,他們只是感情好胸嘁,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著凉逛,像睡著了一般性宏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上状飞,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天毫胜,我揣著相機(jī)與錄音书斜,去河邊找鬼。 笑死酵使,一個(gè)胖子當(dāng)著我的面吹牛荐吉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播凝化,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼稍坯,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了搓劫?” 一聲冷哼從身側(cè)響起瞧哟,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎枪向,沒想到半個(gè)月后勤揩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡秘蛔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年陨亡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片深员。...
    茶點(diǎn)故事閱讀 38,137評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡负蠕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出倦畅,到底是詐尸還是另有隱情遮糖,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布叠赐,位于F島的核電站欲账,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏芭概。R本人自食惡果不足惜赛不,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望罢洲。 院中可真熱鬧踢故,春花似錦、人聲如沸惹苗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鸽粉。三九已至斜脂,卻和暖如春抓艳,著一層夾襖步出監(jiān)牢的瞬間触机,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留儡首,地道東北人片任。 一個(gè)月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像蔬胯,于是被迫代替她去往敵國和親对供。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評論 2 345

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

  • 設(shè)計(jì)模式匯總 一氛濒、基礎(chǔ)知識 1. 設(shè)計(jì)模式概述 定義:設(shè)計(jì)模式(Design Pattern)是一套被反復(fù)使用产场、多...
    MinoyJet閱讀 3,906評論 1 15
  • Java中常見的設(shè)計(jì)模式主要有23種,根據(jù)行為結(jié)構(gòu)的不同舞竿,又分為創(chuàng)建型模式京景、結(jié)構(gòu)型模式和行為型模式三種。 創(chuàng)建型模...
    勿念及時(shí)雨閱讀 317評論 0 2
  • 鏈接:https://github.com/WiKi123/DesignPattern作者: WiKi123(gi...
    樹懶啊樹懶閱讀 3,464評論 0 2
  • 六點(diǎn)多起床骗奖,去公園晨跑确徙。 炮臺灣濕地公園很美,麻雀执桌、白頭鵯鄙皇、烏鶇及很多不知名的鳥伴著風(fēng)箏,飛于天空仰挣。鳥的叫聲和著早...
    榕樹家的故事閱讀 254評論 0 1
  • 深情不及久伴 戀愛之初伴逸,分分鐘都想跟對方黏在一起。 可是相處久了以后椎木,有時(shí)會(huì)慢慢倦怠违柏,開始挑剔彼此的各種缺點(diǎn)。沒有...
    尚層國際家居杭州閱讀 614評論 0 0