鸣谢:《大话设计模式》

简介

装饰模式(Decorator Pattern)是一种结构型设计模式,它允许你在不修改现有对象的情况下动态地添加新行为或功能。这个模式通过将对象包装在一个装饰器类中,
然后逐渐添加更多的装饰器来扩展对象的功能。

总结

当系统需要新功能的时候,需要向旧的类中新加入代码,而这些新加入的代码往往装饰了原有类的核心职责或主要行为,而这些新加入的功能往往只是为了满足一些只在某种特定情况下才会执行的特殊行为的需要。比如超时双十一突如其来的打折返现,到五一策略又会发生更改。

<注意:>
哪些是具体构建角色、哪些是具体装饰角色,尤其是后者,区分的关键就是,角色中是否持有顶层接口的引用

如果只有一个ConcreteComponent类而没有抽象的Component类,那么Decorator类可以是ConcreteComponent的一个子类。同样道理,如果只有一个ConcreteDecorator类,那么就没有必要建立一个单独的Decorator类,而可以把Decorator和ConcreteDecorator的责任合并成一个类。
例子详见4.3,点击跳转


主要组成部分:

  • 组件接口(Component Interface):定义了被装饰对象和装饰器必须遵循的共同接口或抽象类。

  • 具体组件类(Concrete Component Class):实现了组件接口,是被装饰对象的原始实现。

  • 装饰器抽象类(Decorator Abstract Class):继承自组件接口,并包含一个对组件的引用。它可以包装一个具体组件或其他装饰器。

  • 具体装饰器类(Concrete Decorator Class):继承自装饰器抽象类,并在其中添加额外的功能或行为。

工作流程

  • 首先,创建一个具体组件类,该类实现了组件接口,代表了原始对象。

  • 创建一个装饰器抽象类,该类也实现了组件接口,并包含一个对组件的引用。

  • 创建具体装饰器类,它继承自装饰器抽象类,并在其中添加额外的功能或行为。

  • 可以递归地使用多个装饰器来包装具体组件,每个装饰器都添加了不同的功能。

  • 最终,客户端使用具体组件对象来执行操作,但由于组件被包装在装饰器中,因此可以动态地添加或移除功能。

代码

结构

正常装饰模式代码

component组件接口

定义了被装饰对象和装饰器必须遵循的共同接口或抽象类。

/**
 * @author: lpy
 * @Date: 2023/09/14
 * @desc: 人物形象接口
 */
public interface ICharacter {
    /**
     * 展示接口
     */
    void show();
}

具体组件类

实现了组件接口,是被装饰对象的原始实现。

/**
 * @author: lpy
 * @Date: 2023/09/14
 */
public class Person implements ICharacter{
    private String name;

    public Person(String name){
        this.name = name;
    }

    @Override
    public void show() {
        System.out.println("装扮着"+name );
    }
}

装饰器抽象类

继承自组件接口,并包含一个对组件的引用。它可以包装一个具体组件或其他装饰器。

public class Clothes implements ICharacter{
    private ICharacter component;

    public void decorate(ICharacter component){
        this.component = component;
    }

    @Override
    public void show() {
        if (null != this.component){
            this.component.show();
        }
    }
}

具体装饰器类

继承自装饰器抽象类,并在其中添加额外的功能或行为。

public class Tshirts extends Clothes {
    @Override
    public void show(){
        System.out.print("穿着T恤");

        super.show();
    }
}
public class StrwHat extends Clothes {

    @Override
    public void show(){
        System.out.print("带着草帽");

        super.show();
    }
}
public class NoTops extends Clothes {

    @Override
    public void show() {
        System.out.print("光着膀子");

        super.show();
    }
}
/**
 * @author: lpy
 * @Date: 2023/09/14
 * @desc: 没有裤子
 */
public class NoTrousers extends Clothes {

    @Override
    public void show() {
        System.out.print("光着屁股");

        super.show();
    }
}
public class Hat extends Clothes {

    @Override
    public void show(){
        System.out.print("带着帽子");

        super.show();
    }
}
public class BlackShoes extends Clothes {

    @Override
    public void show() {
        System.out.print("穿着嘿鞋子");

        super.show();
    }
}

测试类

/**
 * @author: lpy
 * @Date: 2023/09/14
 * @desc: 装饰器模式
 */
public class DecorateModeTest {
    public static void main(String[] args) {
        Person xc = new Person("橙心");

        System.out.println("第一种装扮");
        BlackShoes blackShoes = new BlackShoes();
        blackShoes.decorate(xc);

        StrwHat strwHat = new StrwHat();
        strwHat.decorate(blackShoes);

        NoTrousers noTrousers = new NoTrousers();
        noTrousers.decorate(strwHat);

        noTrousers.show();
    }
}


变种

没有抽象的Component类

ConcreteComponent
/**
 * @author: lpy
 * @Date: 2023/10/17
 * @desc: 如果只有一个ConcreteComponent类而没有抽象的Component类
 */
public class ConcreteComponent {
    private String name;
    public void operation(){
        System.out.print("装饰的"+name);
    }

    public ConcreteComponent(String name){
        this.name = name;
    }
    public ConcreteComponent(){
    }
}
Decorator抽象装饰类
public abstract class Decorator extends ConcreteComponent{
    private ConcreteComponent concreteComponent;

    public Decorator(String name) {
        super(name);
    }
    public Decorator() {
    }

    public void setComponent(ConcreteComponent c){
        this.concreteComponent = c;
    }

    @Override
    public void operation(){
        if (concreteComponent!=null){
            concreteComponent.operation();
        }
    }
}
具体装饰类
public class ConcreteDecorator1 extends Decorator{

    @Override
    public void operation(){
        System.out.print("具体装饰类——帽子+");
        super.operation();
    }
}
public class ConcreteDecorator2 extends Decorator{

    @Override
    public void operation(){
        System.out.print("具体装饰类——鞋子+");
        super.operation();
    }
}
public class ConcreteDecorator3 extends Decorator{

    @Override
    public void operation(){
        System.out.print("具体装饰类——裤子+");
        super.operation();
    }
}
测试类
public class OneComponentClassTest {
    public static void main(String[] args) {
        ConcreteComponent component = new ConcreteComponent("小米");
        ConcreteDecorator1 c1 = new ConcreteDecorator1();
        ConcreteDecorator2 c2 = new ConcreteDecorator2();
        ConcreteDecorator3 c3 = new ConcreteDecorator3();

        c1.setComponent(component);
        c2.setComponent(c1);
        c3.setComponent(c2);

        c3.operation();
    }
}

应用

  • 装饰器模式在Java体系中的经典应用是Java I/O,参考自java设计模式——装饰器模式

  • Spring 中用到的装饰器模式在类名上有两种表现:一种是类名中含有 Wrapper,另一种是类名中含有Decorator。

装饰模式优缺点

  • 优点:

    • 装饰类和被装饰类可以独立发展,而不会相互耦合。是继承的一个替代模式,换句话说,Component类无需知道Decorator类,Decorator类是从外部来扩展Component类的功能,而Decorator也不用知道具体的构件。

    • 装饰器模式是继承关系的一个替代方案。我们看装饰类Decorator,不管装饰多少层,返回的对象还是Component(因为Decorator本身就是继承自Component的),实现的还是is-a的关系。

    • 可以动态地扩展对象的功能,而不需要改变其源代码。这使得系统更加灵活,容易维护,同时也符合开闭原则(Open-Closed Principle)。

  • 缺点:

    • 复杂性增加:引入装饰器可能会增加系统的复杂性,因为它引入了许多额外的类和层次结构。如果不谨慎地设计装饰器,可能会导致类爆炸问题,使代码难以维护和理解。

    • 难以理解:对于某些开发人员来说,装饰模式的设计和实现可能不够直观,特别是在涉及多个装饰器的复杂嵌套时。这可能会增加代码的可读性和理解难度。

    • 性能开销:每个装饰器都会引入一些额外的开销,因为它需要包装组件并执行一些操作。如果应用了多层嵌套的装饰器,性能开销可能会显著增加。

    • 潜在的滥用:开发人员可能过度使用装饰模式,导致设计变得复杂而混乱。装饰模式应该用于那些需要动态添加功能的情况,而不是用于静态或永久性的功能扩展。

引入复杂性、难以理解、潜在性能开销和潜在滥用

7 大设计原则

不能全部遵守 , 也不能不遵守 , 注意平衡 功能 和 系统复杂度 , 找到最合适的一个点 ;

- 单一职责原则(Single Responsibility Principle - SRP):
这个原则规定一个类应该只有一个改变的理由,也就是说,一个类应该只有一个单一的职责。这有助于保持类的简单性和可维护性,因为每个类只需关注一个特定的功能。

- 开放-封闭原则(Open-Closed Principle - OCP):
开放-封闭原则要求系统中的软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。这意味着当需要添加新功能时,应该通过扩展现有代码来实现,而不是修改原有代码。

- 里氏替换原则(Liskov Substitution Principle - LSP):
里氏替换原则规定,子类应该能够替换其父类,而不会影响程序的正确性。也就是说,子类应该继承父类的行为,但可以扩展或修改该行为,但不应该改变父类的行为。

- 接口隔离原则(Interface Segregation Principle - ISP):
接口隔离原则要求一个类不应该强迫其客户端依赖于它们不需要的接口。应该根据客户端的需求定义小而精确的接口,而不是大而笨重的接口。

- 依赖倒置原则(Dependency Inversion Principle - DIP):
依赖倒置原则要求高级模块不应该依赖于低级模块,它们都应该依赖于抽象。具体来说,这意味着应该通过抽象接口或抽象类来定义模块之间的依赖关系,而不是直接依赖于具体实现。

- 迪米特法则(Law of Demeter - LoD):
迪米特法则也被称为最少知识原则,它要求一个对象应该只与其密切的朋友交互,而不应该暴露太多的信息给外部对象。这有助于减少对象之间的耦合度,提高系统的灵活性。

- 合成复用原则(Composite Reuse Principle - CRP):
合成复用原则鼓励通过组合(合成)已有的类来实现新功能,而不是通过继承现有的类。这样可以降低系统的耦合度,并且更加灵活。

源码位置

全部源码github.com/xs-alpha/designPattern

如有内容侵权,麻烦联系博主删除

参考文献

浙公网安备33011302000604

辽ICP备20003309号