装饰器模式(Decorator Pattern)
简介
装饰器模式允许向一个现有的对象添加新的功能,同时又不改变其结构
意图
动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。
特点
主要解决:为扩展一个类经常使用继承方式,由于继承为类引入静态特征,并且随着扩展功能增多,子类会膨胀。
何时使用:不想增加很多子类的情况下扩展类。
如何解决:将具体功能职责划分,同时继承装饰者模式。
关健代码:
- Component类充当抽象角色,不应该具体实现。
- 修饰类引用和继承Component类,具体扩展类重写父类方法。
装饰器模式有个特点,就是Decorator对外却拥有与Component同样的接口,而且Component无需知道Decorator存在。装饰类主要用于修饰原有类的核心职责或行为,
装饰器模式 vs 桥接模式
桥接模式和装饰器模式,都是在原有类的基础上,为客户端提供新功能,那么它们有什么区别呢?
-
新功能
桥接模式是多个维度变量独立变化,如果新增维度变量,就需要修改原来的类;
而装饰器模式是原来的类本质(接口、属性)不变,通过修改装饰器模式添加新功能。 -
适用场景
桥接模式适合新增一种多维度变量的组合,例如原来是红色矩形,颜色、形状 2个维度独立变量,现在增加绿色三角形,橙色圆形,就很适合用桥接模式;
装饰器模式非常适合用来扩展第三方库、类,为原有类、模块添加新的非核心功能。
装饰器模式 vs 适配器模式
装饰器模式和适配器模式,都可以对第三方库进行扩展,有什么区别?
装饰器模式更倾向于在原有类基础上,为原有的类添加新功能。装饰器模式添加了新功能,而且不必关心使用者需要何种接口,更关心能装饰出何种功能。
适配器模式更倾向于使原来不能一起工作的类,改变接口,让它们一起工作。适配器模式没有添加新功能,而且不得不关注双方需要、提供何种接口。
应用场景
- 假设有一个String类有随机访问其中某个字符charAt()和deleteAt()等接口,但是现在需要将String当做一个栈来用,就需要实现push, pop 接口功能,就可以利用装饰器模式,在原来API基础上实现新功能而改变原有的类;
- 孙悟空有72变,当他变成“庙宇”后,根本还是一只猴子,但是又有庙宇的功能。
- 不论一幅画有没有画框,都可以挂在墙上,但是通常都是有画框的,并且实际上是画框被挂在墙上。
UML类图
实现代码
- 创建待扩展的接口Shape
//Shape.java
public interface Shape {
public void draw();
}
- 创建Shape接口的实体类Circle, Retangle
// Circle.java
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Shape: Circle");
}
}
// Retangle.java
public class Retangle implements Shape {
@Override
public void draw() {
System.out.println("Shape: Retangle");
}
}
- 创建实现了Shape接口的抽象装饰器类
// ShapeDecorator.java
public abstract class ShapeDecorator {
protected Shape shape;
public ShapeDecorator(Shape shape) {
this.shape = shape;
}
public void draw() {
shape.draw();
}
}
- 创建扩展了ShapeDecorator的实体装饰类RedShapeDecorator
// RedShapeDecorator.java
public class RedShapeDecorator extends ShapeDecorator {
public RedShapeDecorator(Shape shape) {
super(shape);
}
@Override
public void draw() {
shape.draw();
setRedBorder();
}
private void setRedBorder() {
System.out.println("Border Color: Red");
}
}
- 使用RedShapeDecorator来装饰Shape对象
// DecoratorPatternDemo.java
public class DecoratorPatternDemo {
public static void main(String[] args) {
Shape circle = new Circle();
ShapeDecorator redCircle = new RedShapeDecorator(circle);
ShapeDecorator redRetangle = new RedShapeDecorator(new Retangle());
System.out.println("Circle with normal border");
circle.draw();
System.out.println("\nCircle with red border");
redCircle.draw();
System.out.println("\nCircle with retangle border");
redRetangle.draw();
}
}
运行结果
Circle with normal border
Shape: Circle
Circle with red border
Shape: Circle
Border Color: Red
Circle with retangle border
Shape: Retangle
Border Color: Red