天天看點

設計模式(7)之裝飾模式

  1. 什麼是裝飾模式

    裝飾( Decorator )模式又叫做包裝模式。通過一種對用戶端透明的方式來擴充對象的功能,是繼承關系的一個替換方案。

  2. 裝飾模式的結構
    設計模式(7)之裝飾模式
  3. 裝飾模式的角色和職責

    抽象元件角色: 一個抽象接口,是被裝飾類和裝飾類的父接口。

    具體元件角色:為抽象元件的實作類。

    抽象裝飾角色:包含一個元件的引用,并定義了與抽象元件一緻的接口。

    具體裝飾角色:為抽象裝飾角色的實作類。負責具體的裝飾。

以一個例子來解釋一下,如果我們現在要想造一輛車,對 ! 就是車, Car。

大家都知道,不同的車功能不同,有的車可以跑,廢話!有的車可以飛,有的車可以遊泳,有的車可以跳躍。。。别激動,我就舉個例子。

按照傳統的面向對象的思想,我們肯定先定義一個Car的父類,在這個父類中定義一些所有車都具有的功能,比如run(),然後再派生出各種子類繼承這個Car父類,然後在子類中再實作自己獨特的功能。例如,我聲明一個FlyCar繼承自Car,然後在FlyCar中實作父類的run(),在實作自己獨特的fly()方法。你是不是這麼想的,别不承認,一般人都是醬紫想的。我也是這麼想的,起初我還很自豪,因為我用到了面向對象的思想!!!

但是,如果現在我想造一個既能遊泳又能飛的車怎麼辦?難道我定義一個FlySwimCar繼承自Car,OK,當然可以。但是,随着科技的發展,車的功能越來越豐富,你難道要不停的定義各種組合功能的車嗎,你應該知道排列組合吧~~~這顯然不符合程式員偷懶的個性。

是以,就有了 裝飾模式。我們邊看代碼,邊解釋。

首先,定義一個Car基類,當然,這個必須是接口,你懂的,你不可能直接new 一個Car出來吧,誰知道這個Car是具有哪個功能的Car。但是這個接口Car中應該包含最基本的車的功能。

//Car.java
public interface Car {
    public void run();
    // 顯示車的功能
    public void show();
}
           

最普通的車不過于實作這個接口了,這個車除了能跑之外,啥都不能幹。對,這就是傳說中的 【跑車】。

//RunCar.java

public class RunCar implements Car{

    @Override
    public void run() {
        System.out.println("run...");
    }

    @Override
    public void show() {
        this.run();
    }

}
           

然後,我想造一輛能Fly的車,怎麼辦?

我們先定義一個抽象類CarDecorator,這個類幹嘛的呢?就是對車進行包裝,為啥是抽象的呢?因為你造一個能Fly的車和造一個能Swim的車,用的材料啥的肯定都不同吧,也就是說包裝的内容都不同吧,是以,這個CarDecorator中定義的是包裝類共有操作。就好比,把一輛普通的車改造之前,不論你是改裝成能Fly的車還是能Swim的車,你首先是不是獲得這個普通的車,獲得普通車這個操作就是在CarDecorator基類中定義的。

//CarDecorator.java
public abstract class CarDecorator {
    private Car car;

    public CarDecorator(Car car) {
        this.car = car;
    }
    //提供方法擷取包裝之前的Car
    //為什麼要擷取包裝之前的Car,廢話,不獲得之前的Car,你怎麼添加新的功能?
    //那麼添加功能的方法在哪?我怎麼沒瞅見,别急呀,往下看,肯定是特定的裝飾者添加特定的功能呀。
    public Car getCar() {
        return car;
    }

    public void setCar(Car car) {
        this.car = car;
    }
    //為了顯示車具有的功能,定以了這個show()方法
    public abstract void show();
}
           

好了,現在我們就可以造能飛的車了。

//FlyCarDecorator.java

public class FlyCarDecorator extends CarDecorator {

    public FlyCarDecorator(Car car) {
        super(car);
    }

    @Override
    //顯示車的功能
    public void show() {
        //先顯示原始車的功能,這下知道為啥在CarDecorator中獲得改造之前的車了吧。
        this.getCar().show();
        this.fly();//添加新的功能
    }

    public void fly(){
        System.out.println("fly...");
    }
}
           

同理,我們可以造一個能Swim的車子。

//SwimCarDecorator.java

public class SwimCarDecorator extends CarDecorator{

    public SwimCarDecorator(Car car) {
        super(car);
    }

    @Override
    public void show() {
        this.getCar().show();
        this.swim();
    }

    public void swim(){
        System.out.println("swim...");
    }
}
           

醬紫的話,主函數就會變得很清爽。

//MainClass.java
public class MainClass {

    public static void main(String[] args) {

        Car car = new RunCar();
        car.show();

        System.out.println("--------------------");

        SwimCarDecorator swimCar = new SwimCarDecorator(car);
        swimCar.show();

        System.out.println("--------------------");
        FlyCarDecorator flyCar = new FlyCarDecorator(car);
        flyCar.show();

        System.out.println("--------------------");

    }
}
           

運作結果:

run...
--------------------
run...
swim...
--------------------
run...
fly...
--------------------
           

呵呵呵,是不是很清爽。但是你有木有發現一個問題,你說造一個能Fly能Swim的車,現在卻隻造了一個能run和Siwm,能run和Fly的車,還要不要臉了。。。

各位看官,别激動!!!馬上造。馬上造。

正常人的思維是不是醬紫的,我有一個car,隻能run,跑車嘛!!!現在改造(裝飾)一下,能遊泳了,然後我希望既能遊泳也能飛,怎麼辦?廢話,當然是把能遊泳的車加一個翅膀啥的。是不是?嗯,這次你想對了。我也是怎麼想的。

現在我們改我們的main函數:

//MainClass.java

public class MainClass {

    public static void main(String[] args) {

        Car car = new RunCar();
        car.show();

        System.out.println("--------------------");
        //1
        Car swimCar = new SwimCarDecorator(car);
        swimCar.show();

        System.out.println("--------------------");
        //2                                3   
        Car flyCar = new FlyCarDecorator(swimCar);
        flyCar.show();

        System.out.println("--------------------");

    }
}
           

改了三處,和原來的main函數比較下,看見沒有。為什麼這麼改,你們不都是怎麼想的嗎???忘了?往上看看。

為了适應main函數這麼改,我們修改其他的檔案呢。我們看一下:

原來FlyCarDecorator的構造方法中傳遞的是Car,你現在傳遞的是swimCar,swimCar在沒改之前是啥類型來着,對,是SwimCarDecorator類型。辣麼,你現在知道怎麼改了吧。

我們讓CarDecorator實作Car接口不就完了嗎?

設計模式(7)之裝飾模式

然後分别在FlyDecorator中實作Car中沒有實作的run方法

設計模式(7)之裝飾模式
設計模式(7)之裝飾模式

好了,代碼應該不報錯了吧。。。

現在看下運作結果:

設計模式(7)之裝飾模式

怎麼樣,我沒食言吧,說給你造個能fly能swim的就給你造了。關鍵代碼寫得還是辣麼的優雅!!!嘿嘿嘿,代碼不是我寫的,我隻是分析下。

但是,有人可能會問,你說改這就改這,說改那就改那,到底改的合不合理呢?唉,你别說,這麼一改,比之前還真的合理。

首先,是main方法中的調用邏輯,之前已經說過了,這肯定是符合大多人的思考習慣的。

然後,我們看下CarDecorator實作Car接口合不合理,我們看他的子類FlyDecorator,他繼承自CarDecorator,CarDecorator實作了Car接口,是以,FlyDecorator要實作Car中的接口,Car中有兩個方法一個是show()和run(),show()方法在CarDecorator中已經定義為抽象方法了,已經被FlyDecorator實作了,現在就剩下run()方法沒有實作了,是以,FlyDecorator要實作run()方法,你想想,FlyDecorator實作run方法合理嗎???我覺得非常合理,你給車裝個翅膀讓他飛,他就不能跑了?或許人家跑的更快了呢,或許人家跑的姿勢更優雅了呢?是以你也得給FlyDecorator改造車的同時修改原有功能的機會,是不是?是以我覺得很合理。同理,SwimCarsDecorator中的也要實作Car接口中的run()方法。

繼續閱讀