深入淺出設(shè)計(jì)模式(1)-策略模式
講義要解決問題
- 什么是設(shè)計(jì)模式?
- 為什么23種設(shè)計(jì)模式中沒有MVC模式
- 一些OO設(shè)計(jì)原則與使用
- 策略模式
什么設(shè)計(jì)模式佃迄?
設(shè)計(jì)模式并不直接用來完成代碼的編寫,而是描述在各種不同情況下夺饲,要怎么解決問題的一種方案装盯。面向?qū)ο?/a>設(shè)計(jì)模式通常以類別或對象來描述其中的關(guān)系和相互作用,但不涉及用來完成應(yīng)用程序的特定類別或?qū)ο笏耪馈TO(shè)計(jì)模式能使不穩(wěn)定依賴于相對穩(wěn)定养泡、具體依賴于相對抽象,避免會引起麻煩的緊耦合奈应,以增強(qiáng)軟件設(shè)計(jì)面對并適應(yīng)變化的能力澜掩。
所以簡單來說設(shè)計(jì)模式就是一個(gè)軟件設(shè)計(jì)的藍(lán)圖,而我們就是藍(lán)圖的制作者杖挣,程序猿肩榕。
那就是為什么做設(shè)計(jì),作為搬磚的程序猿惩妇,總要手里有點(diǎn)貨株汉,為什么看別人的代碼總是很舒服,為什么再改產(chǎn)品經(jīng)理的需求的時(shí)候總是那么痛苦甚至修改一點(diǎn)要牽動全身歌殃?這些問題乔妈,完全可以用設(shè)計(jì)模式來提供大部分解決方案。
為什么23種模式中并沒有MVC模式
GoF (Gang of Four氓皱,四人組路召, 《Design Patterns: Elements of Reusable Object-Oriented Software》/《設(shè)計(jì)模式》一書的作者:Erich Gamma钞楼、Richard Helm瘤载、Ralph Johnson、John Vlissides)并沒有把MVC提及為一種設(shè)計(jì)模式,而是把它當(dāng)做“一組用于構(gòu)建用戶界面的類集合”澡绩。在他們看來,它其實(shí)是其它三個(gè)經(jīng)典的設(shè)計(jì)模式的演變:觀察者模式(Observer)(Pub/Sub), 策略模式(Strategy)和組合模式(Composite)叛氨。
作者:lorio鏈接:https://www.zhihu.com/question/27738109/answer/100241918來源:知乎著作權(quán)歸作者所有达址。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處早敬。
其實(shí)MVC 也是模式忌傻,但是是多種設(shè)計(jì)模式的演變,我們實(shí)際編寫的時(shí)候可以看到有很多MVC框架搞监,是對MVC模式的一種藍(lán)圖實(shí)踐水孩。
一些OO設(shè)計(jì)原則與使用
從OO來講可能更令人容易理解,下面是一套完整的產(chǎn)品編寫代碼邏輯琐驴,可能有點(diǎn)長俘种,但是一定會發(fā)現(xiàn)開始我們寫程序的時(shí)候就是這么干的 。
下面我們用Head First 設(shè)計(jì)模式中的一篇栗子在解釋:
這是一個(gè)模擬鴨子游戲:游戲中會出現(xiàn)各種鴨子绝淡,一邊游泳宙刘,一邊叫。
這個(gè)應(yīng)用用的是OO技術(shù)有一個(gè)鴨子超類(superclass)牢酵,并讓各種鴨子繼承此類悬包。
public abstract class Duck {
public Duck() {}
abstract void display();
public void quack() {}
public void swim() {}
}
public class MallardDuck extends Duck {
//外觀是綠頭鴨
public MallardDuck() {}
public void display() {}
}
public class RedHeadDuck extends Duck {
public RedHeadDuck() {}
public void display() {}
}
但是Nathan組織了異常頭腦風(fēng)暴,然后決定要讓鴨子們會飛馍乙,我們怎么做布近?程序猿們說 很簡單于是
public class Duck {
public Duck() {}
public void quack() {}
public void swim() {}
public void fly(){}
}
所有的鴨子都會飛了,但是……丝格,他們的游戲里面有一只橡皮鴨撑瞧,Nathan攤手表示,橡皮鴨是為什么會飛的铁追。季蚂。。琅束。這時(shí)候你會怎么辦扭屁?
OO來說我們繼承覆蓋就好了
public class RubberDuck extends Duck {
public void display() {};
public void quack(){};
public void fly(){
//什么都不做
}
}
但是如果以后加入鐵鴨子怎么辦?不會飛也不會叫
那下面我們用OO中的接口如何涩禀?也就是說把fly()quack()寫成接口料滥,下面的鴨子進(jìn)行繼承。會有什么問題呢艾船?
如果是你你會怎么辦葵腹?
采用良好的00軟件設(shè)計(jì)原則:
- 原則一:
找出應(yīng)用中可能需要變化的地方高每,把它們獨(dú)立出來,不要和那些不需要變化的代碼混在一起践宴,這時(shí)候你就可以很好的修改或者擴(kuò)展這部分鲸匿,而不影響不需要變化的其他部分。
到底哪一些是Duck類變得以及不變的阻肩?
- fly()與quack()會隨著不同而改變
- 你沒有辦法開始就知道带欢,但是可以再改需求的時(shí)候慢慢修改提煉
- 原則二:
針對接口編程,而不是針對實(shí)現(xiàn)編程
這個(gè)對于我們OO來說很需要了解烤惊,我目前總是針對實(shí)現(xiàn)編程而不是接口編程乔煞。在栗子中怎么實(shí)現(xiàn)呢?那就是鴨子的行為將被放在分開的類中柒室,專門提供某行為的接口的實(shí)現(xiàn)渡贾,鴨子的類就不需要知道行為的實(shí)現(xiàn)細(xì)節(jié)
舉個(gè)栗子:
public interface FlyBehavior {
public void fly();
}
public class FlyWithWings implements FlyBehavior {
public void fly() {
System.out.println("I'm flying!!");
}
}
public class FlyNoWay implements FlyBehavior {
public void fly() {
System.out.println("I can't fly");
}
}
public interface QuackBehavior {
public void quack();
}
public class Quack implements QuackBehavior {
public void quack() {
//呱呱叫
System.out.println("Quack");
}
}
public class Squeak implements QuackBehavior {
public void quack() {
//橡皮鴨子吱吱叫
System.out.println("Squeak");
}
}
public class MuteQuack implements QuackBehavior {
public void quack() {
//什么都不做,我不會叫
System.out.println("<< Silence >>");
}
}
那這些有什么好處雄右?對了行為單獨(dú)一個(gè)類空骚,實(shí)現(xiàn)了解耦,這些行為和鴨子無關(guān)了不脯,以后Nathan要什么的鴨子府怯,我們程序猿都可以給他了。
這時(shí)候有個(gè)問題防楷,我們要在什么時(shí)候分離封裝呢?
解耦很清晰则涯,可是我們要怎么整合起來呢复局?讓鴨子更像一個(gè)鴨子,先要在duck加兩個(gè)實(shí)體變量
public abstract class Duck {
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
abstract void display();
public void performFly() {
flyBehavior.fly();
}
public void performQuack() {
quackBehavior.quack();
}
public void swim() {
System.out.println("All ducks float, even decoys!");
}
}
public class MallardDuck extends Duck {
public MallardDuck() {
//在構(gòu)造綠頭鴨的時(shí)候裝配上叫和飛的行為粟判,這個(gè)綠頭鴨就真的會飛和叫了
quackBehavior = new Quack();
flyBehavior = new FlyWithWings();
}
public void display() {
System.out.println("I'm a real Mallard duck");
}
}
編寫好了亿昏,如何測試呢?
public class MiniDuckSimulator {
public static void main(String[] args) {
MallardDuck mallard = new MallardDuck();
RubberDuck rubberDuckie = new RubberDuck();
DecoyDuck decoy = new DecoyDuck();
mallard.performQuack();
rubberDuckie.performQuack();
decoy.performQuack();
}
}
講了這么多档礁,我們從新的梳理一下角钩,到底整體的一個(gè)架構(gòu)是什么樣子的,這時(shí)候請不要把鴨子的行為說成行為呻澜,我們把行為想象成一族算法递礼,這樣行為是不是就可以遷移到很多地方了,下面我來畫UML圖羹幸,請看黑板
- 原則三
多用組合脊髓,少用繼承。
上面我們看了組合建立系統(tǒng)的具有很大的彈性栅受,可以把算法族封裝成類将硝,還可以隨意組合恭朗,只要符合正確的接口。
以后要做什么捕捉鴨子的鴨鳴器繼承一個(gè)算法就可以很快實(shí)現(xiàn)依疼。
上述其實(shí)講解OO原則的時(shí)候你已經(jīng)學(xué)會了策略模式痰腮。
策略模式
定義了算法族,分別封裝起來律罢,讓他們之間可以互相替換膀值,此模式讓算法的變化獨(dú)立與使用算法的客戶
最后一個(gè)問題:我如何使用設(shè)計(jì)模式
你再大腦中裝入了關(guān)于模式的知識,以后在新的設(shè)計(jì)中弟翘,就可以重做之前在你腦子中的舊代碼虫腋!
算法像是”單兵作戰(zhàn)和武器裝備“,設(shè)計(jì)模式像是”仗列的陣型“稀余。算法用來解決具體問題悦冀,設(shè)計(jì)模式用來合理的把算法隔離到各個(gè)正確的地方去。