结构型模式 - 装饰器模式(不改变自身,添加新功能)

装饰器模式(Decorator Pattern)

简介

装饰器模式允许向一个现有的对象添加新的功能,同时又不改变其结构

意图

动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。

特点

主要解决:为扩展一个类经常使用继承方式,由于继承为类引入静态特征,并且随着扩展功能增多,子类会膨胀。
何时使用:不想增加很多子类的情况下扩展类。
如何解决:将具体功能职责划分,同时继承装饰者模式。

关健代码:

  1. Component类充当抽象角色,不应该具体实现。
  2. 修饰类引用和继承Component类,具体扩展类重写父类方法。

装饰器模式有个特点,就是Decorator对外却拥有与Component同样的接口,而且Component无需知道Decorator存在。装饰类主要用于修饰原有类的核心职责或行为,

装饰器模式 vs 桥接模式

桥接模式和装饰器模式,都是在原有类的基础上,为客户端提供新功能,那么它们有什么区别呢?

  1. 新功能
    桥接模式是多个维度变量独立变化,如果新增维度变量,就需要修改原来的类;
    而装饰器模式是原来的类本质(接口、属性)不变,通过修改装饰器模式添加新功能。

  2. 适用场景
    桥接模式适合新增一种多维度变量的组合,例如原来是红色矩形,颜色、形状 2个维度独立变量,现在增加绿色三角形,橙色圆形,就很适合用桥接模式;
    装饰器模式非常适合用来扩展第三方库、类,为原有类、模块添加新的非核心功能。

装饰器模式 vs 适配器模式

装饰器模式和适配器模式,都可以对第三方库进行扩展,有什么区别?
装饰器模式更倾向于在原有类基础上,为原有的类添加新功能。装饰器模式添加了新功能,而且不必关心使用者需要何种接口,更关心能装饰出何种功能。
适配器模式更倾向于使原来不能一起工作的类,改变接口,让它们一起工作。适配器模式没有添加新功能,而且不得不关注双方需要、提供何种接口。

应用场景

  1. 假设有一个String类有随机访问其中某个字符charAt()和deleteAt()等接口,但是现在需要将String当做一个栈来用,就需要实现push, pop 接口功能,就可以利用装饰器模式,在原来API基础上实现新功能而改变原有的类;
  2. 孙悟空有72变,当他变成“庙宇”后,根本还是一只猴子,但是又有庙宇的功能。
  3. 不论一幅画有没有画框,都可以挂在墙上,但是通常都是有画框的,并且实际上是画框被挂在墙上。

UML类图

实现代码

  1. 创建待扩展的接口Shape
//Shape.java
public interface Shape {
      public void draw();
}
  1. 创建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");
      }
}
  1. 创建实现了Shape接口的抽象装饰器类
// ShapeDecorator.java
public abstract class ShapeDecorator {
      protected Shape shape;
      
      public ShapeDecorator(Shape shape) {
            this.shape = shape;
      }
      
      public void draw() {
            shape.draw();
      }
}
  1. 创建扩展了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");
      }
}
  1. 使用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
posted @ 2021-01-12 15:28  明明1109  阅读(239)  评论(0编辑  收藏  举报