设计模式--装饰器模式

592 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第23天,点击查看活动详情

装饰器模式

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。

多用组合少用继承(不重写父类方法)

简单装饰器

当具体构建唯一的时候,如果我们想对其功能进行扩展的时候,可以使用简单装饰器模式。

角色
  • 具体构件 (需要被装饰的角色)
  • 抽象装饰器 (为装饰器定义抽象)
  • 具体装饰器 (抽象装饰器的实现)

其实若装饰器也唯一,则可以去掉抽象装饰器

实现

定义具体构件

public class BaseComponent {
    public void method(){
        System.out.println(BaseComponent.class.getName());
    }
}

定义装饰器接口(也可以使用抽象类)

其中存在一个和具体构件同名的方法,和其他装饰方法(或者叫扩展方法)

public interface IDecorator {

    /**
     * 抽象方法命名一般和具体构建方法一致
     */
    void method();

    /**
     * 装饰抽象方法
     */
    void decorateBefore();

    /**
     * 装饰抽象方法
     */
    void decorateAfter();

}

定义具体装饰器

public class Decorator1 implements IComponent, IDecorator {

    /**
     * 组合构件
     */
    private IComponent iComponent;

    public Decorator1(IComponent iComponent) {
        this.iComponent = iComponent;
    }

    @Override
    public void method() {
        this.decorateBefore();
        iComponent.method();
        this.decorateAfter();
    }

    @Override
    public void decorateBefore() {
        System.out.println(this.getClass().getName() + "<>" + "before");
    }

    @Override
    public void decorateAfter() {
        System.out.println(this.getClass().getName() + "<>" + "before");
    }
}

测试

public class DecoratorTest {
    @Test
    public void decoratorTest(){
        //具体构建
        BaseComponent baseComponent = new BaseComponent();
        //定义装饰器
        IDecorator simpleDecorator = new SimpleDecorator(baseComponent);
        simpleDecorator.method();
    }
}

image-20220615020043943.png

思考

简单装饰器模式其装饰的是一个具体构件,如果说需要在现有装饰器基础上扩展其他方法,需要重写一个装饰器且不能复用现有装饰器逻辑,使得代码冗余扩展性差。

在java中对一个类进行拓展可以采用继承的方式,同样也可以达到我们想要的目的。那为何使用装饰器模式而摒弃继承呢?对于简单装饰器确实没有太大区别,两者的扩展性都不好。但是装饰器模式是符合开闭原则的,而继承是侵入式的。


透明化装饰器

装饰器模式是对构件的拓展,不可避免的声明拓展方法。装饰器模式的透明化要求客户端声明抽象构件的引用,而不是一个具体构件。

透明化装饰器模式要求装饰器对客户端隐藏装饰实现。

要求装饰器和构件实现同一接口。

角色
  • 抽象构建 (抽象构建)
  • 具体构件 (需要被装饰的角色)
  • 抽象装饰器
  • 具体抽象器 (抽象装饰器的实现)
实现

定义抽象构件

public interface IComponent {
    /**
     * method
     */
    void method();
}

定义具体构建

public class Component01 implements IComponent {
    @Override
    public void method() {
        System.out.println(this.getClass().getName());
    }
}

定义抽象装饰器

public interface IDecorator {
    /**
     * 装饰抽象方法
     */
    void decorateBefore();

    /**
     * 装饰抽象方法
     */
    void decorateAfter();
}

定义具体装饰器

public class Decorator1 implements IComponent, IDecorator {

    /**
     * 组合构件
     */
    private IComponent iComponent;

    public Decorator1(IComponent iComponent) {
        this.iComponent = iComponent;
    }

    @Override
    public void method() {
        this.decorateBefore();
        iComponent.method();
        this.decorateAfter();
    }

    @Override
    public void decorateBefore() {
        System.out.println(this.getClass().getName() + "<>" + "before");
    }

    @Override
    public void decorateAfter() {
        System.out.println(this.getClass().getName() + "<>" + "before");
    }
}

测试 image-20220615023057863.png

由于我们这边组合的是抽象构件,所以此装饰器可以装饰一类对象

public class Component02 implements IComponent {
    @Override
    public void method() {
        System.out.println(this.getClass().getName());
    }
}

测试

/**
 * 此装饰器可以装饰实现IComponent接口的所有构件
 */
@Test
public void test02(){
    //具体构件
    IComponent component02 = new Component02();
    //视为IComponent
    IComponent decorator1 = new Decorator1(component02);
    decorator1.method();
}

image-20220615023255892.png

还有装饰器同时实现了抽象构件接口和抽象装饰器接口,所以装饰器也可以被装饰器装饰,达到代码复用、降低冗余的目的。

再定义一个装饰器:


public class Decorator2 implements IComponent, IDecorator {

    /**
     * 组合构件
     */
    private IComponent iComponent;

    public Decorator2(IComponent iComponent) {
        this.iComponent = iComponent;
    }

    @Override
    public void method() {
        this.decorateBefore();
        iComponent.method();
        this.decorateAfter();
    }

    @Override
    public void decorateBefore() {
        System.out.println(this.getClass().getName() + "<>" + "before");
    }

    @Override
    public void decorateAfter() {
        System.out.println(this.getClass().getName() + "<>" + "before");
    }
}

测试:

/**
 * 因为装饰器也实现了抽象构件接口,所以装饰器也可以装饰装饰器,达到复用目的
 */
@Test
public void test03(){
    //具体构件
    IComponent component02 = new Component02();
    //视为IComponent
    IComponent decorator1 = new Decorator1(component02);
    decorator1.method();
    System.out.println("<+++++++++++++++++++++>");
    IComponent decorator2 = new Decorator2(decorator1);
    decorator2.method();
}

这里类似一个环绕通知

image-20220615023612275.png

装饰器模式 & 继承
  • 透明化的装饰器模式完全遵守开闭原则,而继承是侵入式的,且不易扩展
  • 透明化装饰器可以动态的对一个对象做扩展,而继承是静态的