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)開看看)
2.1.2 工廠方法模式
定義:
定義一個(gè)用于創(chuàng)建對象的接口唤衫,讓子類決定實(shí)例化哪個(gè)類。工廠方法使一個(gè)類的實(shí)例化延遲到其子類绵脯。
結(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)圖:
角色:
- 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)圖:
角色:
- 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)圖
角色
- 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)圖
角色
- 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)圖
角色
- 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)圖
角色
- 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)圖
角色
- 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)圖
角色
- 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ì)采用異步方式。