-
設(shè)計(jì)模式概述
-
定義
設(shè)計(jì)模式是一套被反復(fù)使用,多數(shù)人知曉的,經(jīng)過(guò)分類的,代碼設(shè)計(jì)經(jīng)驗(yàn)總結(jié)簡(jiǎn)單來(lái)說(shuō):經(jīng)驗(yàn)復(fù)用!
-
使用語(yǔ)言:
并不要求一定用某種語(yǔ)言,理論上適合于任何oo語(yǔ)言(面向?qū)ο缶幊陶Z(yǔ)言)(Java,C#等)
-
意義&目的:
代碼復(fù)用,增加可維護(hù)性辆沦。每種模式在現(xiàn)時(shí)中都有相應(yīng)的原理來(lái)與之對(duì)應(yīng)昼捍,每一個(gè)模式描述了一個(gè)在我們周?chē)粩嘀貜?fù)發(fā)生的問(wèn)題,以及該問(wèn)題的核心解決方案肢扯,這也是它能被廣泛應(yīng)用的原因妒茬。
-
-
策略模式概述
- 預(yù)備知識(shí):本文以java舉例,請(qǐng)先了解 類蔚晨、復(fù)用乍钻、繼承、多態(tài)铭腕、接口银择,抽象類等
- 策略模式( Strategy Pattern):定義一系列的算法,把它們一個(gè)個(gè)封裝起來(lái), 并且使它們可相互替換。本模式使得算法的變化可獨(dú)立于使用它的客戶累舷。
- (劃重點(diǎn):瓶肌!1挥)最主要用法:許多相關(guān)的類僅僅是行為有異怀挠∥龊“策略”提供了一種用多個(gè)行為中的一個(gè)行為來(lái)配置一個(gè)類的方法
正文!
用例子理解:
有一個(gè)Duck()類
鴨子會(huì)叫
所以有一個(gè)Quack()方法
因?yàn)榧t頭鴨和綠頭鴨會(huì)飛绿淋,現(xiàn)在闷畸,要新加入一種 fly()方法。
方法一:繼承
繼承是指一個(gè)對(duì)象直接使用另一對(duì)象的屬性和方法吞滞。
即直接在Duck類里面加入fly()方法佑菩,再由子類繼承。
但是2迷5钅!
不是所有的鴨子都會(huì)飛
那么一忱,對(duì)于每一個(gè)Duck子類的實(shí)例對(duì)象莲蜘,都必須覆蓋fly()方法(會(huì)飛的不會(huì)飛的,乃至用不同姿態(tài)飛的鴨子)
然而帘营,如果還有一個(gè)誘餌鴨:
不會(huì)飛也不會(huì)叫票渠。。芬迄。问顷。。禀梳。
那么杜窄,又得覆蓋Quack()方法。
代碼:
public class StrategyModel1 {
public static void main(String[] args) {
Duck1 duck=new Duck1();
System.out.println("Duck1:");
duck.fly1();
duck.Quack1();
duck.Swim1();
Duck1 rubberduck=new rubberDuck1();
System.out.println("RubberDuck1:");
rubberduck.fly1();
rubberduck.Quack1();
rubberduck.Swim1();
Duck1 Decoyduck=new DecoyDuck1();
System.out.println("DecoyDuck1:");
Decoyduck.fly1();
Decoyduck.Quack1();
Decoyduck.Swim1();
}
}
class Duck1{
public void fly1() {
System.out.println("I'm flying!");
}
public void Quack1() {
System.out.println("呱呱呱");
}
public void Swim1() {
System.out.println("All the Ducks are swimming!");
}
}
class rubberDuck1 extends Duck1{
public void fly1() {
System.out.println("I can't fly!");//point1
}
public void Quack1() {
System.out.println("吱吱吱");
}
}
/*看似可行算途,但是可能不同的類有相同的方法塞耕,直接繼承會(huì)繼承不相同不想要的方法。
比如DecoyDuck1想要用RubberDuck1的fly1方法郊艘,要么繼承Duck1荷科,再重寫(xiě)fly1方法唯咬;
要么不繼承Duck1而繼承RubberDuck1纱注,再重寫(xiě)Quack1方法。
無(wú)論哪種繼承胆胰,都會(huì)導(dǎo)致代碼在多個(gè)子類中重復(fù)狞贱。*/
class DecoyDuck1 extends Duck1{
public void fly1() {
System.out.println("I can't fly!");//point2
}
public void Quack1() {
System.out.println("呱呱呱");
}
}
結(jié)論:
1.利用繼承的方法,改動(dòng)會(huì)牽一發(fā)而動(dòng)全身蜀涨,造成其他不想要的改變
2.代碼在多個(gè)子類中重復(fù)瞎嬉。
簡(jiǎn)單來(lái)說(shuō):有新方法子類就要覆蓋父類蝎毡。覆蓋其實(shí)沒(méi)有邏輯問(wèn)題,問(wèn)題是同樣的方法在不同類里面要重寫(xiě)多次氧枣,代碼繁瑣沐兵。
方法二:接口
繼承不行。因?yàn)轼喿訒?huì)不斷更新便监,這樣每當(dāng)有新的鴨子類出現(xiàn)扎谎,就得檢查甚至覆蓋fly()和Quack()方法。
那么
我們可以把fly()從父類中取出來(lái)烧董,放到Flyalbe接口中毁靶,這樣,只有會(huì)飛的鴨子才能實(shí)現(xiàn)此接口逊移。Quackable接口同理预吆。
接口:(interface),是一系列方法的聲明胳泉,是一些方法特征的集合,一個(gè)接口只有方法的特征沒(méi)有方法的實(shí)現(xiàn)拐叉,因此這些方法可以在不同的地方被不同的類實(shí)現(xiàn),而這些實(shí)現(xiàn)可以具有不同的行為(功能)胶背。
【注意與抽象類區(qū)分巷嚣!】(抽象類是對(duì)一種事物的抽象,即對(duì)類抽象{is-a}(全部方法和屬性繼承)钳吟。而接口是對(duì)行為的抽象{has-a}廷粒。(指定方法繼承))
代碼:
public class StrategyModel2 {
public static void main(String[] args) {
Duck2 duck=new Duck2();
duck.fly2();
Duck2 rubberduck=new rubberDuck2();
rubberduck.fly2();
}
}
class rubberDuck2 extends Duck2 implements Flyable2 {
public void fly2() {
System.out.println("I don't wanna to fly!");
}
}
class Duck2 implements Flyable2{
public void fly2() {
System.out.println("I'm flying!");
}
}
interface Flyable2{
public abstract void fly2();
}
//Flyalbe2導(dǎo)致fly2完全不能復(fù)用
現(xiàn)在雖然Flyable接口可以解決會(huì)飛的橡皮鴨問(wèn)題,
但是:烨摇0泳ァ!
這是一種更蠢的方法暇番,只有方法聲明嗤放,沒(méi)有方法實(shí)現(xiàn)!
代碼將完全無(wú)法復(fù)用壁酬,無(wú)論方法是否一樣次酌,每次必須重寫(xiě)。
方法三:策略模式舆乔!
繼承不能解決問(wèn)題岳服,因?yàn)轼喿拥男袨樵谧宇惱锊粩喔淖儯易屗凶宇惗加羞@些行為是不恰當(dāng)?shù)摹?/p>
接口似乎可行希俩,只有會(huì)飛的鴨子繼承Flyable接口吊宋,但是Java接口不具有實(shí)現(xiàn)代碼,繼承接口無(wú)法使代碼復(fù)用颜武。
有一個(gè)設(shè)計(jì)原則璃搜,恰好適用于此:(第一個(gè)設(shè)計(jì)原則拖吼!)
找出變化之處,把它獨(dú)立出來(lái)这吻,與無(wú)需變化的代碼隔離
這個(gè)概念很簡(jiǎn)單吊档,
幾乎是每個(gè)設(shè)計(jì)模式背后的精神所在!
哪些可變哪些不可變唾糯?
每次新的需求一來(lái)籍铁,都會(huì)使某方面的代碼發(fā)生變化,那么這部分代碼就要被抽出來(lái)趾断,區(qū)分與其他穩(wěn)定代碼拒名。
Fly()和Quack()會(huì)隨著鴨子的類不同而改變。
我們把它們從Duck類中取出芋酌,新建一組類來(lái)代表每個(gè)行為
第二個(gè)設(shè)計(jì)原則:針對(duì)接口編程增显!而不是針對(duì)實(shí)現(xiàn)編程
我們利用接口代表每個(gè)行為,比方說(shuō)脐帝,F(xiàn)lyBehavior同云,而行為的每個(gè)實(shí)現(xiàn)都將實(shí)現(xiàn)其中的一個(gè)接口。
這樣的做法迥異于以往堵腹,以前的做法是:行為來(lái)自Duck父類的具體實(shí)現(xiàn)炸站,或是繼承某個(gè)接口并由子類自行實(shí)現(xiàn)而來(lái)。這兩種做法都是依賴于“實(shí)現(xiàn)”疚顷,我們被實(shí)現(xiàn)綁得死死的旱易,沒(méi)辦法更改行為(除非寫(xiě)更多代碼)。
在我們的新設(shè)計(jì)中腿堤,鴨子的子類將使用接口FlyBehavior所表示的行為阀坏,所以實(shí)際的“實(shí)現(xiàn)”不會(huì)被綁死在鴨子的子類中。
換句話說(shuō)笆檀,特定的具體行為編寫(xiě)在實(shí)現(xiàn)了FlyBehavior的類中忌堂。
這樣設(shè)計(jì),甚至可以讓fly被其他對(duì)象復(fù)用酗洒,因?yàn)檫@個(gè)fly動(dòng)作已經(jīng)與鴨子類無(wú)關(guān)了士修。
代碼:
public class StrategyModel3 {
public static void main(String[] args) {
Duck3 duck=new Duck3();
duck.Swim3();
Duck3 rubberduck=new RubberDuck3();
rubberduck.Swim3();
rubberduck.PerformFly();
Duck3 decoyduck=new DecoyDuck3();
decoyduck.PerformFly();
}
}
class Duck3{
public FlyBehavior flybehavior;
public void Swim3() {
System.out.println("All the ducks are swimming!");
}
public void PerformFly() {
flybehavior.fly3();
}
}
class RubberDuck3 extends Duck3{
public RubberDuck3() {
flybehavior=new FlyWithWings();
}
}
class DecoyDuck3 extends Duck3{
public DecoyDuck3() {
flybehavior=new FlyNoWay();
}
}
interface FlyBehavior{
public void fly3();
}
class FlyWithWings implements FlyBehavior{
public void fly3() {
System.out.println("I'm flying!");
}
}
class FlyNoWay implements FlyBehavior{
public void fly3() {
System.out.println("I can't fly!");
}
}
(Quack同理)
將兩個(gè)類結(jié)合起來(lái)使用,如同本例樱衷,就是組合棋嘲。這種做法和“繼承”不同的地方在于,鴨子的行為不是繼承來(lái)的箫老,而是和適當(dāng)?shù)男袨閷?duì)象組合來(lái)的封字。
第三個(gè)設(shè)計(jì)原則:多用組合黔州,少用繼承耍鬓!
復(fù)習(xí):
策略模式:定義一系列的算法,把它們一個(gè)個(gè)封裝起來(lái), 并且使它們可相互替換阔籽。本模式使得算法的變化可獨(dú)立于使用它的客戶。
概念圖示:
結(jié)束
------By@YYSir