装饰者模式(Decorator)

目的

动态地给一个对象添加一些额为的职责。对此,装饰者模式比直接继承父类增加功能更加灵活。

类图

«interface»
Componet
operate1()
operate2()
ConcreteComponent
operate1()
operate2()
Decorator
operate1()
operate2()
ConcreteDecoratorA
-Componet componet;
operate1()
operate2()
operateA1()
ConcreteDecoratorB
-Componet componet;
operate1()
operate2()
operateB1()

参与者

  • Component(VisualComponent)
    定义一个对象接口,可以给这些对象动态的添加职责。
  • ConcreteComponent
    定义一个对象,可以给这个对象添加一些职责。
  • Decorator
    维持一个指向Component对象的指针,并定义一个与Component接口一致的接口。
  • ConcreteDecorator
    向组件添加职责。

示例 (引用自:知乎 仅用于说明用法)

秋天到了,女朋友非要喝秋天的第一杯奶茶,到了“蜜雪冰城”奶茶店后,给女朋友点了一杯珍珠芒果奶茶,加了珍珠、芒果等配料,给自己点了一杯加冰柠檬水,加了冰块、柠檬片等配料,这时候就可以使用装饰器模式。

抽象构件(Component)角色:
奶茶

public interface IMilktea {
    void addDosing();
}

具体构件(ConcreteComponent)角色:

  1. 珍珠奶茶
public class PearlMilktea implements IMilktea{
    @Override
    public void addDosing() {
        System.out.println("开始制作:珍珠奶茶");
    }
}
  1. 柠檬水
public class LemonMilktea implements IMilktea{
    @Override
    public void addDosing() {
        System.out.println("开始制作:柠檬水");
    }
}

装饰者(Decorator)角色:
配料

public abstract  class Dosing implements IMilktea{

    IMilktea iMilktea;

    public Dosing(IMilktea iMilktea){
        this.iMilktea = iMilktea;
    }

    @Override
    public void addDosing() {
        this.iMilktea.addDosing();
    }
}

具体装饰(ConcreteDecorator)角色:
加珍珠

public class Pearl extends Dosing {

    public Pearl(IMilktea iMilktea) {
        super(iMilktea);
    }

    @Override
    public void addDosing() {
        super.addDosing();
        System.out.println("制作中:加珍珠");
    }
}

加芒果

public class Mango extends Dosing {

    public Mango(IMilktea iMilktea) {
        super(iMilktea);
    }

    @Override
    public void addDosing() {
        super.addDosing();
        System.out.println("制作中:加芒果");
    }
}

加宁檬

public class Lemon extends Dosing {
    public Lemon(IMilktea iMilktea) {
        super(iMilktea);
    }

    @Override
    public void addDosing() {
        super.addDosing();
        System.out.println("制作中:加柠檬");
    }
}

加冰

public class Ice extends Dosing {

    public Ice(IMilktea iMilktea) {
        super(iMilktea);
    }

    @Override
    public void addDosing() {
        super.addDosing();
        System.out.println("制作中:加冰");
    }
}

客户端

public class Client {
    public static void main(String[] args) {
        System.out.println("服务员:你好,需要点什么呀?");
        System.out.println("我: 一杯加芒果、加珍珠的珍珠奶茶,一杯加柠檬、加冰的柠檬水");
        System.out.println("服务员:好的。");
        System.out.println("---------------------------");
        PearlMilktea pearlMilktea = new PearlMilktea();
        Pearl pearl = new Pearl(pearlMilktea);
        Mango mango = new Mango(pearl);
        Ice ice = new Ice(mango);
        ice.addDosing();
        System.out.println("-------第一杯制作完成----------");
        LemonMilktea lemonMilktea = new LemonMilktea();
        Lemon lemon = new Lemon(lemonMilktea);
        Ice ice1 = new Ice(lemon);
        ice1.addDosing();
        System.out.println("-------第二杯制作完成----------");

        System.out.println("我:珍珠奶茶怎么加冰了?");
        System.out.println("服务员:对不起,珍珠奶茶做错了,重新给您做。");
        System.out.println("---------------------------");
        mango.addDosing();
        System.out.println("-------不加冰的珍珠奶茶制作完成----------");
        System.out.println("我:好的,谢谢!");
    }
}

输出结果

服务员:你好,需要点什么呀?
我: 一杯加芒果、加珍珠的珍珠奶茶,一杯加柠檬、加冰的柠檬水
服务员:好的。
---------------------------
开始制作:珍珠奶茶
制作中:加珍珠
制作中:加芒果
制作中:加冰
-------第一杯制作完成----------
开始制作:柠檬水
制作中:加柠檬
制作中:加冰
-------第二杯制作完成----------
我:珍珠奶茶怎么加冰了?
服务员:对不起,珍珠奶茶做错了,重新给您做。
---------------------------
开始制作:珍珠奶茶
制作中:加珍珠
制作中:加芒果
-------不加冰的珍珠奶茶制作完成----------
我:好的,谢谢!

真实使用场景:Java I/O

大家在用java.io的类的时候是不是经常混淆,其中的类实在是太多了。反正我没了解装饰者模式之前,用起这些类来是真的痛苦。直到我知道这些类其中很多都是装饰者,用起来才得心应手。

java.io类结构简图

InputStream
FIleInputStream
StingBufferInputStream
ByteArrayInputStream
FilterInputStream
PushBackInputStream
BufferedInputStream
DataInputStream
LineNumberInputStream

从Java I/O中能看出装饰者模式的缺点:装饰者模式的使用常常造成设计中有大量的小类,在一定程度上造成使用此API程序员的困扰。但是,现在你已经了解了装饰者模式的工作原理,以后使用别人大量装饰的API时,就可以很容易的辨别出他们的装饰者时如何组织的。

编写自己的Java I/O 装饰者

编写一个装饰者,把输入流内的所有大写字符转成小写。例如:“Talk Is Cheap! Show Me Your Code!” 转成 “talk is cheap! show me your code!”:

public class LowerCaseInputStream extends FilterInputStream{
	public LowerCaseInputStream(InputSteam 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)Charator.toLowerCase((char)b[i]);
		}
		return result;
	}
}

效果

优点

  • 比静态继承更灵活:如果依赖继承,那么类的行为只能在编译时静态决定。而装饰者使利用类组合,可以在运行时指定。
  • 避免在层次结构高层的类有太多的特征:装饰者模式提供了一种“即用即付”的方法来添加职责。它并不试图在一个复杂的可定制的类中支持所有可预见的特征,相反,你可以定义一个简单的类,并使用Decorator类给它逐渐添加功能。可以从简单的部件组合出复杂的功能。

缺点

  • 装饰者(Decorator)的对象和它的被装饰者(Component)对象不一样 Decorator是一个透明的包装。如果我们从对象标识的观点出发,一个呗装饰了的组件与这个组件还是有差别的。因此,使用装饰时不应该依赖对象标识。
  • 会产生很多小对象 如java I/O 那样

参考自

  • 《设计模式 可复用面向对象软件的基础》》
  • 《Head_First 设计模式》

你可能感兴趣的:(设计模式,java,开发语言,设计模式,装饰器模式)