3

冬天已经来临,北方的小伙伴们是不是感觉天气一天比一天冷了呢?从秋天过渡到冬天,冷了就穿一件毛衣,如果穿上毛衣还觉得冷可能会添一件羽绒服,如果下雪天可能就需要穿上雨衣或者带上雨伞了。在我们生活中这些衣服以拓展的方式给了你温暖,但是它们并不是你的一部分,如果到了春天那么这些衣服可能会一一的脱掉了。

在软件开发过程中,有事想用一些现存的类或者组件,这些类或者组件可能只是完成了一些核心功能。但在不改变其结构的情况下,可以动态的拓展其功能。所有这些都可以用装饰模式来实现。

什么装饰模式

装饰模式:指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。 —— 节选自百度百科

装饰模式有两种方式可以实现给一个类或者一个对象添加行为,第一种是使用继承,使用继承是给现有类添加功能的一种有效途径,通过继承一个现有类可以使得子类在拥有自身方法的同时还拥有父类的方法。但是这种方法是静态的,用户不能控制增加行为的方式和时机。第一种则是使用关联的方法,即将一个类的对象嵌入到另一个对象中,由另一个对象来决定是否调用嵌入对象的行为以便扩展自己的行为。

装饰模式优缺点

装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任,换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不需要创建更多子类的情况下,将对象的功能加以扩展。

优点
  1. 装饰模式与继承关系的目的都是扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。
  2. 可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的装饰器,从而实现不同的行为。
  3. 通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同的行为的组合。可以使用多个具体装饰类来装饰同一个对象,得到功能更为强大的对象。
  4. 具体构建类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构建类和具体装饰类,在使用其进行组合,原有代码无需改变
缺点
  1. 是以哦那个装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于他们之间的相互连接的方式有所不同,而不是他们的类或者属性值有所不同,同时还会由很多具体的装饰类。这些装饰类和小对象的产生将增加系统的复杂度,加大学习理解的难度。
  2. 这种比继承更加灵活机动平的特性,也同时意味着装饰模式比继承更加易于出错,排错也是蛮困难的,对于多次装饰的对象,调试时寻找错误可能需要逐级排场,较为繁琐,项目复杂度偏高。

示例

装饰模式的主要角色如下:

  1. 部件:声明封装器和被封装的对象的公用接口
  2. 具体部件:类是被封装对象所属的类。它定义了基础行为,但装饰类改变了这些行为
  3. 基础装饰:类拥有一个指向被封装对象的引用成员变量。该变量的类型应当被声明尾通用部件接口这样就可以引用具体的部件接口,这样他就可以引用具体的部件和装饰。
  4. 具体装饰类:定义类可动态添加到部件的额外行为。具体装饰类会重写装饰基类的方法,并在调用父类方法之前或之后进行额外的行为。
  5. 客户端:可以使用多层装饰来封装来装饰部件,只要它能使用通用接口与所有对象交互即可。

类图如下所示:

image

代码示例:

// 抽象构件
abstract class Component {
    public abstract operate() : void;
}

// 具体构件
class ConcreteComponent extends Component {
    public operate() : void {
        console.log('do something');
    }
}

// 装饰角色
abstract class Decorator extends Component {
    private component : Component = null;
    constructor(component : Component ) {
        super();
        this.component = component;
    }

    public operate() : void {
        this.component.operate();
    }
}

// 具体装饰者
class ConcreteDecoratorA extends Decorator {
    constructor(component : Component) {
        super(component);
    }

    // 定义自己的修饰方法
    private methodA() : void {
        console.log('methodA修饰');
    }

    // 重写父类方法
    public operate() : void {
        this.methodA();
        super.operate();
    }
}

class ConcreteDecoratorB extends Decorator {
    constructor(component : Component) {
        super(component);
    }

    // 定义自己的修饰方法
    private methodB() : void {
        console.log('methodB修饰');
    }

    // 重写父类方法
    public operate() : void {
        this.methodB();
        super.operate();
    }
}

function main() {
    let component : Component = new ConcreteComponent();
    // 第一次装饰
    component = new ConcreteDecoratorA(component);
    // 第二次装饰
    component = new ConcreteDecoratorB(component);
    // 装饰后运行
    component.operate();
}

main();

总结

装饰模式来实现扩展比继承更加灵活,它以对客户透明的方式动态地给一个对象附加更多的责任。装饰模式可以在不需要创造更多子类的情况下,将对象的功能加以扩展。

继承是一种耦合度较大的静态关系,无法在程序运行时动态扩展。在软件开发阶段,关联关系虽然不会比继承关系减少编码量,但是到了软件维护阶段,由于关联关系使系统具有较好的松耦合性,因此使得系统更加容易维护。


Aaron
4k 声望6.1k 粉丝

Easy life, happy elimination of bugs.