基本定义
装饰者模式属于结构型模式,它可以动态的将新功能附加到对象上,同时又不改变其结构。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(OCP)。
模式结构
装饰者和被装饰者有相同的超类型,因为装饰者和被装饰者必须是一样的类型,利用继承是为了达到类型的匹配,而不是利用继承获取行为
- Component:装饰者和被装饰者共同的父类,是一个接口或者抽象类,用来定义基本行为
- ConcreteComponent:定义具体对象,即被装饰者
- Decorator:抽象装饰者,继承自 Component,从外类来扩展 ConcreteComponent。对于 ConcreteComponent来说,不需要知道Decorator的存在,Decorator 是一个接口或抽象类
- ConcreteDecorator:具体装饰者,用于扩展 ConcreteComponent
举例说明
在咖啡店客人想点一杯加两份糖一份牛奶的摩卡咖啡,各个商品的价格如下,我们需要根据用户点的咖啡、加的配料,动态的计算价格
商品 | 价格 |
---|---|
拿铁咖啡(LatteCoffee) | 4.5 |
摩卡咖啡(MochaCoffe) | 5.5 |
糖(Sugar) | 1.0 |
牛奶(Milk) | 2.0 |
「实体类」 Coffee
public abstract class Coffee{
public String des = "咖啡"; //描述
private float price = 0.0f; //价格
protected abstract float cost(); //计算费用
//省略getter setter方法
}
「被装饰者」LatteCoffee
public class LatteCoffee extends Coffee{
public LatteCoffee() {
setDes("拿铁咖啡");
setPrice(4.5f);
}
@Override
protected float cost() {
return getPrice();
}
}
「被装饰者」MochaCoffee
public class MochaCoffee extends Coffee {
public MochaCoffee() {
setDes("摩卡咖啡");
setPrice(5.5f);
}
@Override
protected float cost() {
return getPrice();
}
}
「抽象装饰者」Decorator
public class Decorator extends Coffee {
private Coffee coffee;
public Decorator(Coffee drink) {
this.coffee = drink;
}
@Override
protected float cost() {
return getPrice() + coffee.cost();
}
@Override
public String getDes() {
return coffee.getDes() + "加" + super.getDes();
}
}
「具体装饰者」SugarDecorator
public class SugarDecorator extends Decorator{
public SugarDecorator(Coffee coffee) {
super(coffee);
setDes("糖");
setPrice(1.0f);
}
}
「具体装饰者」MilkDecorator
public class MilkDecorator extends Decorator{
public MilkDecorator(Coffee coffee) {
super(coffee);
setDes("牛奶");
setPrice(2.0f);
}
}
「测试类」Client
public class Client {
/**
* 点一杯 加两份糖一份牛奶的摩卡咖啡
*/
@Test
public void test01() {
Coffee order = new MochaCoffee();
System.out.println(order.getDes() + ",价格:" + order.cost());
//加两份糖
order = new SugarDecorator(new SugarDecorator(order));
System.out.println(order.getDes() + ",价格:" + order.cost());
//加一份牛奶
order = new MilkDecorator(order);
System.out.println(order.getDes() + ",价格:" + order.cost());
}
}
「结果」result
摩卡咖啡,价格:5.5
摩卡咖啡加糖加糖,价格:7.5
摩卡咖啡加糖加糖加牛奶,价格:9.5
在 Java IO 流中的应用
在上图所示的关系中
- 实体类是 InputStream
- 被装饰者是FileInputStream、StringBufferInputStream、ByteArrayInputStream
- 抽象装饰者是FilterInputStream
- 具体装饰者是BufferInputStream、DataInputStream、LineNumberInputStream
具体使用如下:
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("G:\\a.txt"));
装饰者模式总结
1、利用继承设计子类,只能在编译时静态决定,并且所有子类都会继承相同的行为;利用组合扩展对象,就可以在运行时动态的进行扩展。
2、装饰者和被装饰者对象有相同的超类型,所以在任何需要原始对象(被装饰者)的场合,都可以用装饰过的对象代替原始对象。
3、可以用一个或多个装饰者包装一个对象(被装饰者)。
4、装饰者可以在所委托的装饰者行为之前或之后加上自己的行为,以达到特定的目的。
5、被装饰者可以在任何时候被装饰,所以可以在运行时动态地、不限量地用你喜欢的装饰者来装饰对象。
6、装饰者会导致出现很多小对象,如果过度使用,会让程序变得复杂。