装饰者模式

说明:动态地将责任附加到对象上,若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

  • 装饰者和被装饰者有相同的超类型(装饰的关键)
  • 装饰者可以在所委托被装饰者的行为之前/后加上自己的行为,以达到特定的目的
  • 对象可以再运行时动态的不限量的试用装饰者进行装饰

经典用法:java中的IO
经常会对InputStream/OutputStream进行多层包装,其实使用的就是装饰者模式。

具体实现的逻辑:
首先需要定义一个抽象基类,也就是组件基类,在其中定义组件的方法。
需要有一个或多个被装饰者,使用继承的方式从基类扩展而来,可以理解为对基类的具体实现。
需要有一个装饰者的接口,它扩展自上面的组件基类。
需要有一个或多个装饰者,从装饰者类扩展而来,但是这个扩展与上面的扩展不是一个性质的,被装饰者对于基类的继承以及自己的实现其实就是以前理解的在基类的基础上加上自己的实现。两者在行为属性上是相同的,只不过扩展类具有更加具体的方法属性,即扩展了基类的行为,但是这里的扩展指的是继承的是基类的类型,而并不是行为。下面会再详细说明。

举个栗子:
在买鸡蛋灌饼的时候,通常可以要求加鸡蛋、香肠、培根等。
在这个例子中,原味的灌饼就可以作为被装饰者,而加上去的调料就属于装饰者。

来看看实现:

//抽象基类
public abstract class Bread {
    String name;
    public String getName(){
        return name;
    }
    public abstract int getPrice();
}

//被 装饰(包装) 类
public class EggBread extends Bread {
    public EggBread() {
        name="鸡蛋灌饼";
    }
    @Override
    public int getPrice() {
        return 5;
    }
}

//装饰者抽象基类
public abstract class Condiment extends Bread {
    public abstract String getName();
}

//鸡蛋包装类
public class EggDecorator extends Condiment {
    private Bread bread;
    public EggDecorator(Bread bread){
        this.bread = bread;
    }
    @Override
    public String getName() {
        return bread.getName() + "加鸡蛋";
    }
    @Override
    public int getPrice() {
        return bread.getPrice() + 1;
    }
}

//培根包装类
public class BaconicDecorator extends Condiment {
    private Bread bread;
    public BaconicDecorator(Bread bread){
        this.bread = bread;
    }
    @Override
    public String getName() {
        return bread.getName() + "加培根";
    }
    @Override
    public int getPrice() {
        return bread.getPrice() + 2;
    }
}

//测试
public class Test {
    public static void main(String[] args) {
        EggBread eggBread = new EggBread();
        Bread bread1 = new EggDecorator(eggBread);
        Bread bread2 = new BaconicDecorator(eggBread);
        Bread bread3 = new BaconicDecorator(bread1);
        Bread bread4 = new EggDecorator(new EggDecorator(new BaconicDecorator(new EggBread())));
        System.out.println("name:" + bread1.getName() + ",price:" + bread1.getPrice());
        System.out.println("name:" + bread2.getName() + ",price:" + bread2.getPrice());
        System.out.println("name:" + bread3.getName() + ",price:" + bread3.getPrice());
        System.out.println("name:" + bread4.getName() + ",price:" + bread4.getPrice());
    }
}

结果:
name:鸡蛋灌饼加鸡蛋,price:6
name:鸡蛋灌饼加培根,price:7
name:鸡蛋灌饼加鸡蛋加培根,price:8
name:鸡蛋灌饼加培根加鸡蛋加鸡蛋,price:9

上面的实现中最关键的一部分就是:调料基类继承了Bread类,也就是说,后面的鸡蛋装饰者、培根装饰者都继承了饼基类,这显然是不符合逻辑的,鸡蛋灌饼继承自饼基类没问题,因为具有相同的行为,但是这两个装饰者跟饼显然不属于同一类东西啊,这其实就是所谓的继承的是类型的实现了。
装饰者从饼基类继承的目的其实就是为了获得这个基类的类型,从而可以使得在装饰者对被装饰者进行包装之后,它的类型没有进行改变,还是原有的饼基类类型,这样就可以在被装饰过一层的基础上继续进行外层装饰。
只要所有的装饰者都采用这样的方式实现,那么只需要定义少量的几个装饰者类,就可以实现各种多层的复杂的包装。

其实就是解耦的思想,在装饰者模式之前,鸡蛋灌饼加培根需要一个类实现,加香肠又需要一个类实现,而现在就不需要定义那么多繁杂的类了,只需要定义好装饰者,然后在运行时动态的进行包装即可。
(我怎么感觉跟抽象工厂那里的解耦有一些相似)

再看上面的实现,关键就是在每一个装饰者类中都持有一个基类组件的对象,然后在基类对象的方法的基础上增加自己的方法实现,从而实现一层一层的包装。

最关键的部分就是装饰者继承组件基类的类型从而达到在其中调用了基类对象的方法并加上自己的实现之后,它本身依然是一个基类对象类型,可以再次进行包装。

还需要再好好理解~~

你可能感兴趣的:(装饰者模式)