1. 程式人生 > >Java設計模式之裝飾者模式

Java設計模式之裝飾者模式

一、概念

      裝飾者模式:動態的將責任附加到物件上,若要擴充套件功能,裝飾者提供了比繼承更有彈性的替代方案。

       關係圖:


說明:

  • 裝飾者和被裝飾者物件有相同的超類
  • 你可以擁有一個和多個裝飾者包裝一個物件
  • 由於第一點,在任何需要原始物件的場合都可以使用裝飾過後的的物件代替它
  • 裝飾者可以在所委託被裝飾者的行為之前或之後加上自己的行為,以達到特定的目的
  • 物件可以在任何時候被裝飾,可以在執行時動態的、不限量的用你喜歡的裝飾者來裝飾物件
二、應用例項 1.咖啡店訂單系統        顧客點飲料和調料之後,計算價格,列印描述等。 關係圖:
實現程式碼: Beverage類
package com.hanxin.decorator;

/**
 * 抽象基類,相當於Component
 * Created by hanxin on 2017/11/12.
 */
public abstract class Beverage {
    String description = "Unknown Berverage";
    public String getDescription(){
        return description;
    }
    //抽象方法,必須在子類中實現
    public abstract double cost();
}
CondimentDecorator類
package com.hanxin.decorator;

/**
 * 調味料父類,擴充套件自Beverage
 * Created by hanxin on 2017/11/12.
 */
public abstract class CondimentDecorator extends Beverage {
    //調味料要重新實現該方法
    public abstract String getDescription();
}
DarkRoast類
package com.hanxin.decorator;

/**
 * Created by hanxin on 2017/11/12.
 */
public class DarkRoast extends Beverage {
    public  DarkRoast(){
        this.description = "DarkRoast";
    }
    @Override
    public double cost() {
        return 1.05;
    }
}
Decaf類
package com.hanxin.decorator;

/**
 * Created by hanxin on 2017/11/12.
 */
public class Decaf extends Beverage {
    public Decaf(){
        this.description = "Decaf";
    }
    @Override
    public double cost() {
        return 0.99;
    }
}
Espresso類
package com.hanxin.decorator;

/**
 * Created by hanxin on 2017/11/12.
 */
public class Espresso extends Beverage {

    public Espresso(){
        this.description = "Espresso";
    }

    @Override
    public double cost() {
        return 1.99;
    }
}
HourseBlend類
package com.hanxin.decorator;

/**
 * Created by hanxin on 2017/11/12.
 */
public class HouseBlend extends Beverage {
    public HouseBlend(){
        this.description = "HouseBlend";
    }
    @Override
    public double cost() {
        return 0.89;
    }
}
Mocha類
package com.hanxin.decorator;

/**
 * Created by hanxin on 2017/11/12.
 */
public class Mocha extends CondimentDecorator {
    //用一個引用記錄飲料(就是被裝飾者)
    private Beverage beverage;
    //初始化,並將被裝飾者引用傳進來
    public Mocha(Beverage beverage){
        this.beverage = beverage;
    }
    @Override
    public String getDescription() {
        return beverage.getDescription()+",Mocha";
    }
    /*
        計算價格,呼叫被裝飾者方法計算後,再加上調料價格
     */
    @Override
    public double cost() {
        return beverage.cost()+0.2;
    }
}
Milk類
package com.hanxin.decorator;

/**
 * Created by hanxin on 2017/11/12.
 */
public class Milk extends CondimentDecorator {
    private Beverage beverage;
    public Milk(Beverage beverage){
        this.beverage = beverage;
    }
    @Override
    public String getDescription() {
        return beverage.getDescription()+",Milk";
    }

    @Override
    public double cost() {
        return beverage.cost()+0.1;
    }
}
Soy類
package com.hanxin.decorator;

/**
 * Created by hanxin on 2017/11/12.
 */
public class Soy extends CondimentDecorator {
    private Beverage beverage;
    public Soy(Beverage beverage){
        this.beverage = beverage;
    }
    @Override
    public String getDescription() {
        return beverage.getDescription()+",Soy";
    }

    @Override
    public double cost() {
        return beverage.cost()+0.15;
    }
}
Whip類
package com.hanxin.decorator;

/**
 * Created by hanxin on 2017/11/12.
 */
public class Whip extends CondimentDecorator {
    private Beverage beverage;
    public Whip(Beverage beverage){
        this.beverage = beverage;
    }
    @Override
    public String getDescription() {
        return beverage.getDescription()+",Whip";
    }

    @Override
    public double cost() {
        return beverage.cost()+0.1;
    }
}
測試類
package com.hanxin.decorator;

/**
 * Created by hanxin on 2017/11/12.
 */
public class Test {
    public static void main(String[] args) {
        Beverage beverage = new Espresso();
        System.out.println(beverage.getDescription()+" $"+beverage.cost());

        Beverage beverage1 = new DarkRoast();
        beverage1 = new Mocha(beverage1);
        beverage1 = new Mocha(beverage1);
        beverage1 = new Whip(beverage1);
        System.out.println(beverage1.getDescription()+" $"+beverage1.cost());

        Beverage beverage2 = new HouseBlend();
        beverage2 = new Soy(beverage2);
        beverage2 = new Mocha(beverage2);
        beverage2 = new Whip(beverage2);
        System.out.println(beverage2.getDescription()+" $"+beverage2.cost());

    }
}

2.自定義IO類
實現程式碼: LowerCaeInputStream類
package com.hanxin.decorator.javaAPI;

import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * Java IO 包中的流採用的是裝飾者模式,以下是自定義大小寫轉換流
 * Created by hanxin on 2017/11/12.
 */
public class LowerCaseInputStream extends FilterInputStream {
    public LowerCaseInputStream(InputStream in){
        super(in);
    }

    @Override
    public int read() throws IOException {
        int c = super.read();
        return (c==-1? c:Character.toLowerCase(c));
    }

    @Override
    public int read(byte[] b,int offset,int len) throws IOException {
        int result = super.read(b,offset,len);
        for(int i=offset;i<offset+len;i++){
            b[i] = (byte)Character.toLowerCase((char)b[i]);
        }
        return result;
    }
}

測試類
package com.hanxin.decorator.javaAPI;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.InputStream;

/**
 * Created by hanxin on 2017/11/12.
 */
public class Test {
    public static void main(String[] args) {
        int c;
        try{
            InputStream in = new LowerCaseInputStream(new BufferedInputStream(new FileInputStream("D:/test.txt")));
            while((c=in.read())>0){
                System.out.println((char)c);
            }
            in.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

三、裝飾者模式優缺點 優點:
  • 裝飾者模式可以提供比繼承跟多的靈活性
  • 可以通過一種動態的方式來擴充套件一個物件的功能,在執行時選擇不同的裝飾器,實現不同的功能
  • 通過使用不同的具體裝飾類以及這些裝飾類的排列組合,可以創造出不同的行為組合,可裝用多個裝飾器類來裝飾物件,得到功能強大的物件
  • 具體構建類與具體裝飾類可以獨立化,使用者可以根據需要增加新的具體構件類和具體裝飾類,在使用時在對其進行組合,原有程式碼無需改變(開閉原則)
缺點:
  • 會產生很多的小物件,增加了系統的複雜性
  • 比繼承更加易於出錯,排錯也很困難