设计模式之装饰者模式

 情景:

你是一个咖啡店老板,你会做很多种咖啡。

首先有一个超类,饮料类。

咖啡可以加很多种调料,就假设是A,B,C……

那么你会有AB型的,AC型,BC型,A型的,B型,C型,ABC型的咖啡,那么你需要有六种具体子类。

如果有n种调料,你就要有(2^n-1)种具体子类有木有啊!!!

同时,如果,你添加了一种调料或者改变一种调料!!!会死人呐有木有!!!

怎么办?? 可以考虑使用组合(compisition)和委托(delegation)。

设计原则:类应该对扩展开放,对修改关闭。

装饰者模式

以饮料为主体,然后在运行时,以调料来装饰(decorate)饮料。

设饮料价格是X,加了调料A,可以用A把X包装起来A(X),又加了调料B,再用B把A(x)包装起来,B(A(X))。

  • 装饰着和被装饰者具有相同的超类型。
  • 你可以用一个或多个装饰者包装一个对象。
  • 既然装饰者和被装饰者对象具有相同的超类型,所以在任何需要原始对象(被包装的)的场合,都可以用装饰过的对象代替它。
  • 装饰者可以在所委托被装饰者的行为之前/或之后,加上自己的行为,已达到特定的目的。
  • 对象可以在任何时候被装饰,可以在运行时动态地,不限量地用你喜欢的装饰者来装饰对象。

装饰者模式:动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

类图:

解决最开始的咖啡问题~~

首先饮料类:

public abstract class Beverage {
    String description = "Unknown Beverage";
    
    public String getDescription() {
        return description;
    }
    
    public abstract double cost();
}

然后是两种具体的饮料

public class Espresso extends Beverage {
    public Espresso() {
        description = "Espresso";
    }
    @Override
    public double cost() {
        return 1.99;
    }
}
public class HouseBlend extends Beverage {
    public HouseBlend() {
        description = "House Blend Coffee";
    }
    @Override
    public double cost() {
        return 0.89;
    }
}

调料抽象类:

public abstract class CondimentDecorator extends Beverage {
    public abstract String getDescription();
}

具体调料类(包装类):

摩卡

public class Mocha extends CondimentDecorator {
    
    Beverage beverage;        // 被装饰者
    
    public Mocha(Beverage beverage) {
        this.beverage = beverage;
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + ", Mocha";
    }

    @Override
    public double cost() {
        return .20 + beverage.cost();
    }
    
}

豆浆

public class Soy extends CondimentDecorator {
    
    Beverage beverage;        // 被装饰者
    
    public Soy(Beverage beverage) {
        this.beverage = beverage;
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + ", Soy";
    }

    @Override
    public double cost() {
        return .15 + beverage.cost();
    }
    
}

好了,测试一下:

public class StarBuzzCoffee {
    public static void main(String[] args) {
        Beverage beverage = new Espresso();     // 一杯Espresso 不加调料
        System.out.println(beverage.getDescription() + " $" + beverage.cost());
        
        Beverage beverage2 = new HouseBlend();    // 一杯HouseBlend 加摩卡  加豆浆
        beverage2 = new Mocha(beverage2);
        beverage2 = new Soy(beverage2);
        System.out.println(beverage2.getDescription() + " $" + beverage2.cost());
    }
}

输出:

Espresso $1.99
House Blend Coffee, Mocha, Soy $1.24

Java的io包中使用了装饰者模式。

 

 编写自己的Java IO装饰者

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

public class LowerCaseInputStream extends FilterInputStream {
    public LowerCaseInputStream(InputStream in) {
        super(in);
    }
    
    public int read() throws IOException {
        int c = super.read();
        return (c == -1 ? c : Character.toLowerCase((char)c));
    }
    
    public int read(byte[] b, int offset, int len) throws IOException {
        int result = super.read(b, offset, len);
        for (int i = offset; i < offset + result; i++) {
            b[i] = (byte)Character.toLowerCase((char)b[i]);
        }
        return result;
    }
}

测试一下:(使用了try-with-resources)

public class InputTest {
    public static void main(String[] args) {
        int c;
        try (
            InputStream in = new LowerCaseInputStream(
                    new LowerCaseInputStream(
                            new FileInputStream("test.txt")));
        ) {
            while ((c = in.read()) > 0) {
                System.out.print((char)c);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

test.txt内容: I know the Decorator Pattern therefore I RULE! 

输出: i know the decorator pattern therefore i rule! 

装饰者模式的缺点:

会产生大量的类。实例化的时候会很复杂。

原文地址:https://www.cnblogs.com/wenruo/p/6536570.html