「补课」进行时:设计模式(10)——小明起床记了解装饰模式

1. 前文汇总

「补课」进行时:设计模式系列

2. 小明起床记

小明每天早晨都是起床困难大户,大冬天的太冷了,温暖的被窝紧紧的拉住小明,阻止小明每天早晨的起床。

闹钟响了一遍又一遍,如果再不起床就要迟到了,迟到了就要扣钱,扣了钱就要喝西北风了。

每天早晨督促小明起床的根本不是闹钟,而是贫穷。

起床第一件事儿是穿衣服,先传衣服,再传裤子,然后穿鞋子,最后穿上一件外套,出门上班。

首先,定义一个抽象的小明,小明是个人,所以定义一个人:

public abstract class Person {
    abstract void dress();
}

每个人早晨起床都要穿衣服,这里定义一个穿衣服的方法。

具体的小明上线:

public class Man extends Person {
    @Override
    void dress() {
        System.out.println("先穿衣服");
    }
}

接下来我们要定义一个抽象的装饰器了,小明要穿的是衣服,我们将衣服抽象成一个类:

public abstract class Clothes extends Person {
    private Person person;

    public Clothes(Person person) {
        this.person = person;
    }

    @Override
    void dress() {
        this.person.dress();
    }
}

接下来是具体的衣服:

public class Trousers extends Clothes {
    public Trousers(Person person) {
        super(person);
    }

    @Override
    void dress() {
        super.dress();
        this.dressTrousers();
    }

    private void dressTrousers() {
        System.out.println("穿上裤子啦!!!");
    }
}

public class Shoes extends Clothes {
    public Shoes(Person person) {
        super(person);
    }
    @Override
    void dress() {
        super.dress();
        this.dressShoes();
    }

    private void dressShoes() {
        System.out.println("穿上鞋子啦!!!");
    }
}

public class Coat extends Clothes {
    public Coat(Person person) {
        super(person);
    }

    @Override
    void dress() {
        super.dress();
        this.dressCoat();
    }

    private void dressCoat() {
        System.out.println("穿上外套啦!!!");
    }
}

最后是一个测试类:

public class Test1 {
    public static void main(String[] args) {
        Person person = new Man();
        person.dress();

        System.out.println("--------------");
        System.out.println("增加裤子适配器");
        person = new Trousers(person);
        person.dress();

        System.out.println("--------------");
        System.out.println("增加鞋子适配器");
        person = new Shoes(person);
        person.dress();

        System.out.println("--------------");
        System.out.println("增加外套适配器");
        person = new Coat(person);
        person.dress();
    }
}

测试结果如下:

先穿衣服
--------------
增加裤子适配器
先穿衣服
穿上裤子啦!!!
--------------
增加鞋子适配器
先穿衣服
穿上裤子啦!!!
穿上鞋子啦!!!
--------------
增加外套适配器
先穿衣服
穿上裤子啦!!!
穿上鞋子啦!!!
穿上外套啦!!!

上面这么写有点麻烦,我们可以稍微缩减一下测试类,使用装饰器嵌套,一次性直接把所有的衣服都穿好:

public class Test2 {
    public static void main(String[] args) {
        Person person = new Coat(new Shoes(new Trousers(new Man())));
        person.dress();
    }
}

3. 装饰器模式

3.1 定义

装饰模式(Decorator Pattern)是一种比较常见的模式,其定义如下:

Attachadditional responsibilities to an object dynamically keeping the sameinterface.Decorators provide a flexible alternative to subclassing forextending functionality.(动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。)

  • Component: 抽象构件,是一个接口或者是抽象类,就是定义我们最核心的对象,也就是最原始的对象。
  • ConcreteComponent: 具体构件,是最核心、最原始、最基本的接口或抽象类的实现。
  • Decorator: 通用的装饰 ConcreteComponent 的装饰器,其内部必然有一个属性指向 Component 抽象组件;其实现一般是一个抽象类,主要是为了让其子类按照其构造形式传入一个 Component 抽象组件,这是强制的通用行为(当然,如果系统中装饰逻辑单一,并不需要实现许多装饰器,那么我们可以直接省略该类,而直接实现一个 具体装饰器(ConcreteDecorator) 即可)。
  • ConcreteDecorator: Decorator 的具体实现类,理论上,每个 ConcreteDecorator 都扩展了 Component 对象的一种功能。

通用代码:

public abstract class Component {
    abstract void operate();
}

public class ConcreteComponent extends Component {
    @Override
    void operate() {
        System.out.println("do Something");
    }
}

public abstract class Decorator extends Component {
    private Component component = null;
    // 通过构造函数传递被修饰者
    public Decorator(Component component) {
        this.component = component;
    }
    // 委托给被修饰者执行
    @Override
    void operate() {
        this.component.operate();
    }
}

public class ConcreteDecorator1 extends Decorator {
    // 定义被修饰者
    public ConcreteDecorator1(Component component) {
        super(component);
    }
    private void method1() {
        System.out.println("method1 修饰");
    }

    @Override
    void operate() {
        this.method1();
        super.operate();
    }
}

public class ConcreteDecorator2 extends Decorator {
    public ConcreteDecorator2(Component component) {
        super(component);
    }
    private void method2() {
        System.out.println("method2 修饰");
    }

    @Override
    void operate() {
        super.operate();
        this.method2();
    }
}

public class Test {
    public static void main(String[] args) {
        Component component = new ConcreteComponent();
        // 第一次修饰
        component = new ConcreteDecorator1(component);
        // 第二次修饰
        component = new ConcreteDecorator2(component);
        // 修饰后运行
        component.operate();
    }
}

3.2 优点

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

3.3 缺点

对于装饰模式记住一点就足够了:多层的装饰是比较复杂的。为什么会复杂呢?想想看,就像剥洋葱一样,至于剥到了最后才发现是最里层的装饰出现了问题,想象一下工作量吧,因此,尽量减少装饰类的数量,以便降低系统的复杂度。

3.3 使用场景

  • 需要扩展一个类的功能,或给一个类增加附加功能。
  • 需要动态地给一个对象增加功能,这些功能可以再动态地撤销。
  • 需要为一批的兄弟类进行改装或加装功能,当然是首选装饰模式。