設計模式 – 裝飾模式(Decorator)

問題的提出:一個人裝扮,可以先穿褲子,再穿衣服,再穿鞋子;也可以先穿衣服,再是褲子,再是鞋子,由於褲子、衣服、鞋子種類很多,故搭配種類就更多了。又如製作蛋糕,鮮奶油蛋糕、草莓蛋糕或生日蛋糕其實都是蛋糕,這是裝飾手法不同而已。因此,如果在程序中採用簡單的各個類順序調用的方法爲其裝飾,那麼程序就會顯得相當不靈活,其他的可維護性、可擴展性、可複用性也可想而知了。對此,我們可以採用裝飾模式:

說明:

ü  Component定義了一個對象接口,可以給這些對象動態地添加職責;

ü  ConcreteComponent定義了一個具體的對象,也可以給這個對象添加一些職責;

ü  Decorator定義了裝飾抽象類,繼承了Component,從外類來擴展Component類的

功能,但對於Component來說,無需知道Decorator的存在的,至於ConcreteDecorator就是具體的裝飾對象,起到給Component添加職責的功能;

ü  SetComponent是用來對對象進行包裝(以及再包裝的,實際中也可以使用構造函數實

),這樣每個裝飾對象的實現就和如何使用這個對象分離開了,每個裝飾對象只關心自己的功能,不需要關心如何被添加到對象鏈當中(之所以能被添加到對象鏈當中,在於每個ConcreteDecorator都實現了Decorator,而Decorator又實現了Component,故每個裝飾好的東西自然可以看作新的裝飾,然後作爲參數的形式傳進去,這樣做最大的好處在於:裝飾的順序可以隨意定,待裝飾好後再展示最後的裝飾結果”)

ü  程序中如果只有一個ConcreteComponent類而沒有抽象的Component類,那麼

Decorator類可以是ConcreteComponent的一個子類。同樣道理,如果只有一個ConcreteDecorator類,那麼就沒有必要建立一個單獨的Decorator類,而可以把DecoratorConcreteDecorator的責任合併成一個類。

實例一:外出打扮

Ø  抽象類 -- 文件Decorator.java

package com.yilong.decorator;

public abstract class Decorator {

    public abstract void show();

}

Ø  裝扮對象Person -- 文件Person.java – 最基本(起初)的裝扮對象

package com.yilong.decorator;

public class Person extends Decorator {

    private String name;

    public Person(String name) {

       this.name = name;

    }

    public void show() {

       System.out.println(this.name + " - 裝扮成功!");

    }

}

Ø  抽象類PersonDecorator – PersonDecorator.java – 各種裝扮的抽象

package com.yilong.decorator;

public abstract class PersonDecorator extends Decorator {

    Decorator decorator;

    public PersonDecorator(Decorator decorator) {

       this.decorator = decorator;

    }

    public abstract void show();

}

Ø  文件BigTrouserDisplay.java -- 添加垮褲的裝扮

package com.yilong.decorator;

public class BigTrouserDisplay extends PersonDecorator {

    public BigTrouserDisplay(Decorator decorator) {

       super(decorator);

    }

    @Override

    public void show() {

       System.out.println("穿上 垮褲");

       this.decorator.show();

    }

}

Ø  文件TShirtDisplay.java – 添加”T-Shirt”的裝扮

package com.yilong.decorator;

public class TShirtDisplay extends PersonDecorator {

    public TShirtDisplay(Decorator decorator) {

       super(decorator);

    }

    @Override

    public void show() {

       System.out.println("穿上 T-Shirt");

       this.decorator.show();

    }

}

Ø  文件Main.java – 測試類

package com.yilong.decorator;

public class Main {

    public static void main(String[] args) {

       Decorator decorator = new TShirtDisplay

(new BigTrouserDisplay(new Person("逸龍")));

       decorator.show();

    }

}

打印結果:

穿上 T-Shirt

穿上 垮褲

逸龍 - 裝扮成功!

說明:程序中完全可以實現先穿垮褲,再穿T-Shirt,只要改變順序即可。

實例二:修飾字符串

Ø  文件Main.java

package com.yilong.decorator.string;

public class Main {

    public static void main(String[] args) {

       Display d1 = new StringDisplay("Hello World!");

        System.out.println("展示方式一:");

       d1.show();

       Display d2 = new SideBorder(d1, '#');

       System.out.println("展示方式二:");

       d2.show();

       Display d3 = new FullBorder(d2);

       System.out.println("展示方式三:");

       d3.show();

       Display d4 = new FullBorder(new SideBorder(d3, '*'));

       System.out.println("展示方式四:");

       d4.show();

    }

}

Ø  文件Display.java

package com.yilong.decorator.string;

public abstract class Display {

    public abstract int getColumns();

    public abstract int getRows();

    public abstract String getRowText(int row);

   

    public void show() {

       for(int i=0; i<getRows(); i++) {

           System.out.println(getRowText(i));

       }

    }

}

Ø  文件StringDisplay.java – 基本的字符串顯示

package com.yilong.decorator.string;

public class StringDisplay extends Display {

    private String string;

    public StringDisplay(String string) {

       this.string = string;

    }

    @Override

    public int getColumns() {

       //return string.getBytes().length;//返回字符串字節數

       return string.length();//返回字符串實際長度

    }

    @Override

    public String getRowText(int row) {

       if(row == 0) {

           return string;

       } else {

           return null;

       }

    }

    @Override

    public int getRows() {

       return 1;

    }

}

Ø  文件Border.java – 爲字符串添加裝飾的抽象類

package com.yilong.decorator.string;

public abstract class Border extends Display {

    protected Display display;

   

    public Border(Display display) {

       this.display = display;

    }

}

Ø  文件SideBorder.java – 爲字符串兩邊添加標誌字符顯示

package com.yilong.decorator.string;

public class SideBorder extends Border {

    private char borderChar;

    public SideBorder(Display display, char borderChar) {

       super(display);

        this.borderChar = borderChar;

    }

    @Override

    public int getColumns() {

       return this.display.getColumns() + 2;

    }

    @Override

    public String getRowText(int row) {

       return borderChar + this.display.getRowText(row) + borderChar;

    }

 

    @Override

    public int getRows() {

       return this.display.getRows();

    }

}

Ø  文件FullBorder.java – 爲字符串添加邊框顯示的類

package com.yilong.decorator.string;

public class FullBorder extends Border {

    public FullBorder(Display display) {

       super(display);

    }

    @Override

    public int getColumns() {

       return this.display.getColumns() + 2;

    }

    @Override

    public String getRowText(int row) {

       if(row == 0 || row == display.getRows()+1) {

           return "+" + makeLine('-', getColumns()) + "+";

       } else {

           return "|" + this.display.getRowText(row-1) + "|";

        }

    }

    @Override

    public int getRows() {

       return display.getRows() + 2;

    }

    public StringBuffer makeLine(char ch, int count) {

       StringBuffer strb = new StringBuffer();

       for(int i=0; i<count; i++) {

           strb.append("-");

       }

       return strb;

    }

}

打印結果:

展示方式一:

Hello World!

展示方式二:

#Hello World!#

展示方式三:

+----------------+

|#Hello World!#|

+----------------+

展示方式四:

+--------------------+

|*+----------------+*|

|*|#Hello World!#|*|

|*+----------------+*|

+--------------------+

    總結:裝飾模式是爲已有功能動態地添加更多功能的一種方式;裝飾模式把每個要裝飾的功能放在單獨的類中,並讓這個類包裝它所要裝飾的對象,因此,當需要執行特殊行爲時,客戶代碼就可以在運行時根據需要有選擇地、按順序地使用裝飾功能包裝對象。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章