Java设计模式-装饰者模式

模拟穿衣服场景

我们来看下面一个具体的案例:每个人一天起床之后都要穿衣服(来装饰自己),这是必不可少的,这样问题就来了,穿什么?按照什么顺序穿?
如何用程序方便的模拟这个场景的,代码如下:

/**
* 程序模拟一个人穿衣服的过程
* @author: qhyuan1992
*/
// 抽象接口,用来规范将要被附加一些操作的对象
interface People{
    public void wear();
}

// 具体的对象,该对象将被附加一些额外的操作
class Jane implements People{
    public void wear() {
        System.out.println("今天该穿什么呢?");
    }
}

// 装饰者类,持有一个将要被装饰的接口对象的实例
class Decorator implements People{

    private People people;

    public Decorator(People people) {
        this.people = people;
    }
    public void wear() {
        people.wear();
    }
}

// 具体的装饰者类,负责给增加附加的操作:穿衬衫
class DecoratorShirt extends Decorator{

    public DecoratorShirt(People people) {
        super(people);
    }

    public void wear() {
        super.wear();
        System.out.println("穿个衬衫");
    }
}

// 具体的装饰者类,负责给增加附加的操作:穿西服
class DecoratorSuit extends Decorator{

    public DecoratorSuit(People people) {
        super(people);
    }

    public void wear() {
        super.wear();
        System.out.println("穿个西服");
    }
}

// 具体的装饰者类,负责给增加附加的操作:穿T-Shirt
class DecoratorTShirt extends Decorator{

    public DecoratorTShirt(People people) {
        super(people);
    }

    public void wear() {
        super.wear();
        System.out.println("穿个T-Shirt");
    }
}

// 具体的装饰者类,负责给增加附加的操作:穿裤子
class DecoratorPants extends Decorator{

    public DecoratorPants(People people) {
        super(people);
    }

    public void wear() {
        super.wear();
        System.out.println("穿裤子");
    }
}

// 具体的装饰者类,负责给增加附加的操作:穿鞋子
class DecoratorShoes extends Decorator{

    public DecoratorShoes(People people) {
        super(people);
    }

    public void wear() {
        super.wear();
        System.out.println("鞋子");
    }
}

public class DecoratorTest {

    public static void main(String[] args) {
        People p1 = new DecoratorSuit(new DecoratorShirt(new Jane()));
        p1.wear();
        System.out.println("--------------");
        People p2 = new DecoratorTShirt(new DecoratorPants(new Jane()));
        p2.wear();
        System.out.println("--------------");
        People p3 = new DecoratorTShirt(new DecoratorPants(new DecoratorShoes(new Jane())));
        p3.wear();
        System.out.println("--------------");
        People p4 = new DecoratorShoes(new DecoratorPants(new DecoratorTShirt(new Jane())));
        p4.wear();
    }
}

打印输出:

今天该穿什么呢?
穿个衬衫
穿个西服
————–
今天该穿什么呢?
穿裤子
穿个T-Shirt
————–
今天该穿什么呢?
鞋子
穿裤子
穿个T-Shirt
————–
今天该穿什么呢?
穿个T-Shirt
穿裤子
鞋子

在上面的穿衣服的例子里面:

  • People类:抽象接口,用来规范将要被附加一些操作的对象
  • Jane类:具体的对象,该对象将被附加一些额外的操作
  • Decorator类: 装饰者类,持有一个将要被装饰的接口对象的实例
  • DecoratorShirt类:具体的装饰者类,负责给增加附加的操作:穿衬衫
  • DecoratorSuit类:具体的装饰者类,负责给增加附加的操作:穿西服
  • DecoratorTShirt类:具体的装饰者类,负责给增加附加的操作:穿T-Shirt
  • DecoratorPants类:具体的装饰者类,负责给增加附加的操作:穿裤子
  • DecoratorShoes类:具体的装饰者类,负责给增加附加的操作:穿鞋子

在测试代码里面一连串的new和java I/O流里面常用的方式几乎一样,没错,这就是装饰者模式。

装饰者模式

装饰者模式介绍

装饰模式又名包装(Wrapper)模式。装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案。就增加功能来说,Decorator模式比生成子类更为灵活。
装饰者模式的类图结构如下所示
装饰者模式类图
装饰者模式中类或接口的作用:

  • 抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。
  • 具体构件(ConcreteComponent)角色:定义一个将要接收附加责任的类。
  • 装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。
  • 具体装饰(ConcreteDecorator)角色:负责给构件对象“贴上”附加的责任。

下面是模拟穿衣服程序的类图
穿衣服程序的类图

为什么使用装饰者而不使用继承?

我们知道要实现上面的穿衣服的例子,其实不一定非要使用装饰者模式,使用集成一样可以解决,装饰者和集成一样都是要扩展对象的功能。那么为什么选用装饰者模式呢?

装饰模式可以提供比继承更多的灵活性。装饰模式允许动态增加或删除一个装饰的功能。继承需要在此之前就要确定好对应的类。
我们知道在上面模拟的例子里面一个人穿什么衣服,按照什么顺序来穿都是不定的,换句话说有很多种排列组合的方式,如果使用继承要考虑这么多情况就需要很多的类,然而使用装饰者就可以很方便的在使用的时候动态的创造组合不同的行为。

装饰者模式的使用

简化装饰者模式

只有一个具体构件角色

如果具体构件(ConcreteComponent)角色只有一个,那么可以去掉抽象的Component接口,把Decorator作为ConcreteComponent的子类。
只有一个People实例时的简化装饰者类图
代码:

class Jane{
    public void wear(){
        System.out.println("今天该穿什么呢?");
    }
}

class Decorator extends Jane{

    private Jane Jane;

    public Decorator(Jane Jane) {
        this.Jane = Jane;
    }

    public void wear() {
        Jane.wear();
    }
}

class DecoratorShirt extends Decorator{

    public DecoratorShirt(Jane Jane) {
        super(Jane);
    }

    public void wear() {
        super.wear();
        System.out.println("穿个衬衫");
    }
}

class DecoratorSuit extends Decorator{

    public DecoratorSuit(Jane Jane) {
        super(Jane);
    }

    public void wear() {
        super.wear();
        System.out.println("穿个西服");
    }
}

class DecoratorTShirt extends Decorator{

    public DecoratorTShirt(Jane Jane) {
        super(Jane);
    }

    public void wear() {
        super.wear();
        System.out.println("穿个T-Shirt");
    }
}

class DecoratorPants extends Decorator{

    public DecoratorPants(Jane Jane) {
        super(Jane);
    }

    public void wear() {
        super.wear();
        System.out.println("穿裤子");
    }
}

class DecoratorShoes extends Decorator{

    public DecoratorShoes(Jane Jane) {
        super(Jane);
    }

    public void wear() {
        super.wear();
        System.out.println("穿鞋子");
    }
}

public class DecoratorTest {
    public static void main(String[] args) {
        Jane p1 = new DecoratorSuit(new DecoratorShirt(new Jane()));
        p1.wear();
        System.out.println("==============");
        Jane p2 = new DecoratorTShirt(new DecoratorPants(new Jane()));
        p2.wear();
        System.out.println("==============");
        Jane p3 = new DecoratorTShirt(new DecoratorPants(new DecoratorShoes(new Jane())));
        p3.wear();
        System.out.println("==============");
        Jane p4 = new DecoratorShoes(new DecoratorPants(new DecoratorTShirt(new Jane())));
        p4.wear();
    }
}

只有一个装饰角色

如果具体装饰(ConcreteDecorator)角色只有一个,那么可以去掉Decorator类。
只有一个Decorator实例时的简化装饰者类图
代码:

interface People{
    public void wear();
}

class Jane implements People{
    public void wear() {
        System.out.println("Jane:今天该穿什么呢?");
    }
}
class LiMing implements People{
    public void wear() {
        System.out.println("LiMing:今天该穿什么呢?");
    }
}
class DecoratorShoes implements People{

    private People people;

    public DecoratorShoes(People people) {
        this.people = people;
    }

    public void wear() {
        people.wear();
        System.out.println("穿鞋子");
    }
}

public class DecoratorTest {

    public static void main(String[] args) {
        People p1 = new DecoratorShoes(new Jane());
        p1.wear();
        People p2 = new DecoratorShoes(new LiMing());
        p2.wear();
    }
}

输出:

Jane:今天该穿什么呢?
穿鞋子
LiMing:今天该穿什么呢?
穿鞋子

具体构件角色和具体装饰角色都只有一个

极端地,具体构件(ConcreteComponent)角色只有一个,具体装饰(ConcreteDecorator)角色也只有一个。
只有一个People实例也只有一个Decorator实例时的简化装饰者类图

class Jane {
    public void wear() {
        System.out.println("Jane:今天该穿什么呢?");
    }
}

class DecoratorShoes extends Jane{

    private Jane jane;

    public DecoratorShoes(Jane jane) {
        this.jane = jane;
    }

    public void wear() {
        jane.wear();
        System.out.println("穿鞋子");
    }
}

public class DecoratorTest {

    public static void main(String[] args) {
        Jane j = new DecoratorShoes(new Jane());
        j.wear();
    }
}


此时完全退化成了最简单的继承形式。如下:

class Jane {

    public void wear() {
        System.out.println("Jane:今天该穿什么呢?");
    }
}

class DecoratorShoes extends Jane{

    public void wear() {
        super.wear();
        System.out.println("穿鞋子");
    }
}

public class DecoratorTest {

    public static void main(String[] args) {
        DecoratorShoes j = new DecoratorShoes();
        j.wear();
    }
}

半透明装饰者

装饰者模式中装饰(Decorator)角色持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。也就是说不允许Decorator类扩展Component类的中接口。然而实际情况中很可能在Decorator中定义了一些新的方法。这时的装饰者成为半透明的装饰者模式,相应的上面的标准的装饰者模式成为透明的装饰者模式。
比如Decorator类里面增加eat方法,该方法在People类里面并没有.
Decorator类增加了People类没有的方法

良好的程序设计方式告诉我们声明变量尽量要使用顶层的超类,利用多态达到同样的代码实现不同的效果,在装饰者模式中也一样,要求要声明一个Component类型的对象,而不要声明一个Decorator更不要是ConcreteComponent类型的对象。

比如在下面的示例代码中:

// 尽量不要这样声明
DecoratorSuit decoratorSuit = new DecoratorSuit(new DecoratorShirt(new Jane()));
// 而应该这样声明
People p1 = new DecoratorSuit(new DecoratorShirt(new Jane()));

示例代码:

interface People{
    public void wear();
}

class Jane implements People{
    public void wear() {
        System.out.println("今天该穿什么呢?");
    }
}

class Decorator implements People{

    private People people;

    public Decorator(People people) {
        this.people = people;
    }
    public void wear() {
        people.wear();
    }

    public void eat() {
    }
}

class DecoratorShirt extends Decorator{

    public DecoratorShirt(People people) {
        super(people);
    }

    public void wear() {
        super.wear();
        System.out.println("穿个衬衫");
    }
    public void eat() {
        System.out.println("衬衫-去吃大餐");
    }
}

class DecoratorSuit extends Decorator{

    public DecoratorSuit(People people) {
        super(people);
    }

    public void wear() {
        super.wear();
        System.out.println("穿个西服");
    }

    public void eat() {
        System.out.println("西服-去吃大餐");
    }
}

public class DecoratorTest {

    public static void main(String[] args) {
        // 尽量不要这样声明
        DecoratorSuit decoratorSuit = new DecoratorSuit(new DecoratorShirt(new Jane()));
        decoratorSuit.wear();
        decoratorSuit.eat();
        /*----------------------------------------------------------*/
        // 要这样声明
        People p1 = new DecoratorSuit(new DecoratorShirt(new Jane()));
        p1.wear();
        //p.eat();// error
        /*----------------------------------------------------------*/
        People p2 = new DecoratorSuit(new DecoratorShirt(new Jane()));
        ((DecoratorSuit)p2).eat();
    }
}

输出

今天该穿什么呢?
穿个衬衫
穿个西服
西服-去吃大餐
今天该穿什么呢?
穿个衬衫
穿个西服
西服-去吃大餐

上面的代码在Decorator类里面提供了新的方法eat,而该方法在People类里面是没有的,因此如果像上面的代码里面在声明对象的时候生命成超类,也就是People类(真正的类型是DecoratorSuit类),当不调用eat方法的时候没有问题,但是必须要调用该方法的话,就必须要向下转型。

原文地址:https://www.cnblogs.com/qhyuan1992/p/5385293.html