装饰器模式的定义与特点
装饰器(Decorator)模式的定义:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。通常情况下,扩展一个类的功能会使用继承方式来实现。但继承具有静态特征,耦合度高,并且随着扩展功能的增多,子类会很膨胀。如果使用组合关系来创建一个包装对象(即装饰对象)来包裹真实对象,并在保持真实对象的类结构不变的前提下,为其提供额外的功能,这就是装饰器模式的目标。
优点
- 装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用。
- 通过使用不用装饰类及这些装饰类的排列组合,可以实现不同效果。
- 装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
- 装饰器模式完全遵守开闭原则。
缺点:
装饰器模式会增加许多子类,过度使用会增加程序得复杂性。
使用场景:
- 扩展一个类的功能。
- 动态增加功能,动态撤销。
模式的结构
装饰器模式主要包含以下角色。
- 抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象。
- 具体构件(ConcreteComponent)角色:实现抽象构件,通过装饰角色为其添加一些职责。
- 抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
- 具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。 装饰器模式的结构图如图
装饰者模式的结构UML图如下:
模式结构代码示例
public class DecoratorPattern {
public static void main(String[] args) {
Component p = new ConcreteComponent();
p.operation();
System.out.println("---------------------------------");
Component d = new ConcreteDecorator(p);
d.operation();
}
}
//抽象构件角色
interface Component {
public void operation();
}
//具体构件角色
class ConcreteComponent implements Component {
public ConcreteComponent() {
System.out.println("创建具体构件角色");
}
public void operation() {
System.out.println("调用具体构件角色的方法operation()");
}
}
//抽象装饰角色
class Decorator implements Component {
private Component component;
public Decorator(Component component) {
this.component = component;
}
public void operation() {
component.operation();
}
}
//具体装饰角色
class ConcreteDecorator extends Decorator {
public ConcreteDecorator(Component component) {
super(component);
}
public void operation() {
super.operation();
addedFunction();
}
public void addedFunction() {
System.out.println("为具体构件角色增加额外的功能addedFunction()");
}
}
示例代码:
装饰者模式解决星巴克咖啡订单
UML类图
示例代码:
1,定义抽象类:饮品
/定义抽象类-饮品
public abstract class Drink {
//描述
protected String describe;
//价格
protected float price;
//还可以有其他属性
public String getDescribe() {
return describe;
}
public void setDescribe(String describe) {
this.describe = describe;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
//计算价格
public abstract float cost();
}
2,定义实体类:咖啡
//具体的饮品
public class Coffee extends Drink {
@Override
public float cost() {
//单独点咖啡,则cost就是其价格 * 数量
return super.getPrice() ;
}
}
3,定义具体咖啡:美国咖啡和中国咖啡
//具体咖啡 -- 中国coffee
public class ChainCoffee extends Coffee {
public ChainCoffee() {
//定义描述
setDescribe("中国咖啡");
//定义价格
setPrice(50f);
}
}
//美国咖啡
public class AmericaCoffee extends Coffee {
public AmericaCoffee() {
setDescribe("美国咖啡");
setPrice(0.5f);
}
}
4,定义装饰器,并且继承Drink
public class Decorator extends Drink {
//装饰者,组合一个饮品对象
private Drink drink;
public Decorator(Drink drink) {
this.drink = drink;
}
@Override
public float cost() {
return super.getPrice() + drink.cost();
}
@Override
public String getDescribe() {
return super.describe + "--- " + drink.getDescribe();
}
}
5,定义实体装饰者类
// 牛奶装饰
public class MilkDecorator extends Decorator {
public MilkDecorator(Drink drink) {
super(drink);
setDescribe("牛奶");
setPrice(5.2f);
}
}
//糖装饰
public class SugarDecorator extends Decorator {
public SugarDecorator(Drink drink) {
super(drink);
setDescribe("唐");
setPrice(2.0f);
}
}
6,客户端调用
public class Client {
public static void main(String[] args) {
//咖啡 + 糖
Drink drink = new ChainCoffee();
drink = new SugarDecorator(drink);
System.out.println("cost ---- : " + drink.cost()); ;
}
}
装饰模式在java源码中的应用
Java的IO结构,FilterInputStream就是一个装饰者
- InputStream 是一个抽象类,类似与面示例中的Drink
- FileInputStream是InputSteam的子类,类似于上上面示例 中的ChinaCoffe等,具体的产品
- FilterInputStream 是InputStream 的子类,是装饰者,类似于上面示例中的Decorator
- DataInputStream 是FilterInputStream 的子类,是具体的装饰者,类似上面示例中的Milk等;
如下图所示:
源代码如下:
//抽象类:
public abstract class InputStream implements Closeable {
......
}
//继承InputStream
public class FileInputStream extends InputStream {
......
}
//继承InputStream,并且有 InputStream in 的引用;
public class FilterInputStream extends InputStream {
/**
* The input stream to be filtered.
*/
//被修饰者
protected volatile InputStream in;
/**
* Creates a <code>FilterInputStream</code>
* by assigning the argument <code>in</code>
* to the field <code>this.in</code> so as
* to remember it for later use.
*
* @param in the underlying input stream, or <code>null</code> if
* this instance is to be created without an underlying stream.
*/
protected FilterInputStream(InputStream in) {
this.in = in;
}
......
}
//继承FilterInputStream,是具体的修饰者
public class DataInputStream extends FilterInputStream implements DataInput {
/**
* Creates a DataInputStream that uses the specified
* underlying InputStream.
*
* @param in the specified input stream
*/
public DataInputStream(InputStream in) {
super(in);
}
......
}