設計模式——裝飾者模式 Java源代碼

裝飾者模式,可以動態地把職責附加到已有的對象上面去。又稱 Wrapper Pattern,在代碼實現的時候,確實有“包裝”的意思。

類圖

裝飾者
圖:來自《Head First Design Patterns》

可以看出,裝飾者模式裏面,有4個角色:Component抽象類,ConcreteComponent具體類,Decorator抽象類,ConcreteDecorator具體類

裝飾者模式
圖:我的Java源代碼的類圖

總共10個類
一個Component抽象類
兩個ConcreteComponent具體類
一個Decorator抽象類
五個ConcreteDecorator具體類
一個Main測試類

ConcreteDecorator 繼承了Decorator抽象類, 組合了Component抽象類。Decorator抽象類繼承了Component抽象類,這個繼承的目的是:it’s vital that the decorators have the same type as the object they are going to decorate。簡而言之,就是爲了獲得相同的類型,並不是爲了獲得行爲。組合是爲了讓所有的concreteDecorator可以“互相包裝、裝飾”


talk is cheap, show me the code

去食堂打菜,經濟套餐,假設總共只有5種食物可供選擇分別是:手撕包菜,茄子豆角,麻婆豆腐,青椒肉絲,紅燒裏脊。

一個Component抽象類

package decorator;

public abstract class Meal
{
    String description = "Unknown Meal";

    public String getDescription()
    {
        return description;
    }

    public abstract double cost();
}

兩個ConcreteComponent具體類

package decorator;

public class ChineseMeal extends Meal
{
    public ChineseMeal()
    {
        description = "Chinese Meal";
    }

    @Override
    public double cost()
    {
        return 1.0;        // 一次性餐具費用
    }

}
package decorator;

public class WesternMeal extends Meal
{
    public WesternMeal()
    {
        description = "Western meal";
    }

    @Override
    public double cost()
    {
        return 5.0;        // 不鏽鋼刀、叉費用
    }

}

一個Decorator抽象類

package decorator;

public abstract class Decorator extends Meal
{
    public abstract String getDescription();
}

五個ConcreteDecorator具體類

package decorator;

// 茄子豆角
public class EggplantBean extends Decorator
{
    Meal meal;

    public EggplantBean(Meal meal)
    {
        this.meal = meal;
    }
    @Override
    public String getDescription()
    {
        return meal.getDescription() + ", 茄子豆角";
    }

    @Override
    public double cost()
    {
        return 2 + meal.cost();
    }

}
package decorator;

// 青椒炒肉
public class GreenPepperPork extends Decorator
{
    Meal meal;

    public GreenPepperPork(Meal meal)
    {
        this.meal = meal;
    }

    @Override
    public String getDescription()
    {
        return meal.getDescription() + ", 青椒炒肉";
    }

    @Override
    public double cost()
    {
        return 3 + meal.cost();
    }

}
package decorator;

// 手撕包菜
public class HandCabbage extends Decorator
{
    Meal meal;

    public HandCabbage(Meal meal)
    {
        this.meal = meal;
    }
    @Override
    public String getDescription()
    {
        return meal.getDescription() + ", 手撕包菜";
    }

    @Override
    public double cost()
    {
        return 2 + meal.cost();
    }

}
package decorator;

// 麻婆豆腐
public class MapoTofu extends Decorator
{
    Meal meal;

    public MapoTofu(Meal meal)
    {
        this.meal = meal;
    }
    @Override
    public String getDescription()
    {
        return meal.getDescription() + ", 麻婆豆腐";
    }

    @Override
    public double cost()
    {
        return 4 + meal.cost();
    }
}
package decorator;

// 紅燒裏脊
public class BraiseTenderloin extends Decorator
{
    Meal meal;

    public BraiseTenderloin(Meal meal)
    {
        this.meal = meal;
    }
    @Override
    public String getDescription()
    {
        return meal.getDescription() + ", 紅燒裏脊";
    }

    @Override
    public double cost()
    {
        return 5 + meal.cost();
    }

}

一個Main測試類

package decorator;

public class Main
{

    public static void main(String[] args)
    {
        Meal meal = new ChineseMeal();
        meal = new GreenPepperPork(meal);
        meal = new HandCabbage(meal);
        meal = new BraiseTenderloin(meal);
        System.out.println(meal.getDescription() + "  ¥" + meal.cost());

        Meal meal2 = new ChineseMeal();
        meal2 = new EggplantBean(new MapoTofu(new BraiseTenderloin(meal2)));
        System.out.println(meal2.getDescription() + "  ¥" + meal2.cost());

    }

}

運行結果

直接從eclipse複製過來

Chinese Meal, 青椒炒肉, 手撕包菜, 紅燒裏脊  ¥11.0
Chinese Meal, 紅燒裏脊, 麻婆豆腐, 茄子豆角  ¥12.0

分析討論

去食堂打菜,經濟套餐,假設總共只有5種食物可供選擇分別是:手撕包菜,茄子豆角,麻婆豆腐,青椒肉絲,紅燒裏脊。如果可以隨意打菜,就可以有C15+C25+C35+C45+C55=251=31 種選擇。如果寫代碼把這120種選擇都分別弄成一個類,就有31個類。這樣不僅代碼重複率太高,而且如果食堂更改了上述五樣菜中一樣菜的價格,那麼31個類的代碼需要全部修改。這不符合開閉原則,不利於代碼的維護和擴展。

裝飾者模式就可以解決上述這種“類的爆炸式增長”問題,各種各樣的排列組合太多,不能在代碼中一個類一個。

裝飾者模式的特點是“繼承(inheritance) + 組合(Composition)”:繼承是爲了讓component和decorator擁有相同的類型,組合是爲了讓所有的concreteDecorator可以“互相包裝、裝飾”。裝飾者又名Wrapper大概就是這樣來的。


Java. IO

Java.IO 中應用了裝飾者模式

FileInputStream   fiStream = null;
InputStreamReader iStreamReader = null;
BufferedReader    bReader = null; 

fiStream = new FileInputStream("C:\\xxxx"); 

// InputStreamReader 是字節流通向字符流的橋樑、
iStreamReader = new InputStreamReader(fiStream); 

// 從字符輸入流中讀取文件中的內容,裝飾了一個InputStreamReader的對象
bReader = new  BufferedReader(iStreamReader("C:\\xxxx"));     
一句代碼版本,把3new放到一起
BufferedReader bReader = new BufferedReader(new InputStreamReader(new FileInputStream("C:\\xxxx"));
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章