设计模式之装饰设计模式

之前在web开发时,在做一个全局编码的过滤器时,用到了装饰设计模式,当时理解不是很深刻。特重新学习。。。

装饰者模式具有的一些特征

1,装饰者(decorator)和被装饰(扩展)的对象有着相同的超类(supertype)。

2,我们可以用多个装饰者去装饰一个对象。

3,我们可以用装饰过的对象替换代码中的原对象,而不会出问题(因为他们有相同的超类)。

4,装饰者可以在委托(delegate,即调用被装饰的类的成员完成一些工作)被装饰者的行为完成之前或之后加上他自己的行为。

5,一个对象能在任何时候被装饰,甚至是运行时。

装饰者模式的基本结构

我们来看一张《Head first 设计模式》里的图

(图中的英文为书中对这个结构的解释与说明,下面的解释并不是对图中英文的直接翻译,而是博主自己的稍稍结合书中其他内容的总结)

Component:一般是一个抽象类(也有可能不是),是一组有着某种用途类的基类,包含着这些类最基本的特性。

ConcreteComponent:继承自Component,一般是一个有实际用途的类,这个类就是我们以后要装饰的对象。

Decorator:继承自Component,装饰者需要共同实现的接口(也可以是抽象类),用来保证装饰者和被装饰者有共同的超类,并保证每一个装饰者都有一些必须具有的性质,如每一个装饰者都有一个实例变量(instance  variable)用来保存某个Component类型的类的引用。

ConcreteDecorator:继承自Decorator,用来装饰Component类型的类(不能装饰抽象类),为其添加新的特性,可以在委托被装饰者的行为完成之前或之后的任意时候。

 

装饰者模式简述

装饰者模式通过组合的方式扩展对象的特性,这种方式允许我们在任何时候对对象的功能进行扩展甚至是运行时扩展,而若我们用继承来完成对类的扩展则只能在编译阶段实现,所以在某些时候装饰者模式比继承(inheritance)要更加灵活。

例子:公司门口有一个小摊卖手抓饼和肉夹馍的,有时候中午不想吃饭就会去光顾一下那个小摊,点了手抓饼之后往往还可以在这个基础之上增加一些配料,例如煎蛋,火腿片等等,每个配料的价格都不一样,不管你怎么配配料,最终价格是手抓饼基础价加上每一种所选配料价格的总和。小摊的价格单如下:

如何使用一种设计模式来处理价格计算的问题呢,或许我们可以试试装饰者模式,因为在这里,主体是手抓饼和肉夹馍,而配料则是装饰者,我先用UML类图来描述一下类之间的协作关系。

小编思考:小编在昨天学习桥接设计模式,异常的发现这个例子貌似能用桥接设计模式。但仔细的想了下还是不能。

装饰模式和桥接模式的区别:

两个模式都是为了解决过多子类对象问题。但他们的诱因不一样。桥接模式是对象自身现有机制沿着多个维度变化,是既有部分不稳定。装饰模式是为了扩展新的功能。

 

再来看看具体java代码是怎么实现装饰设计模式的:

/* Component类*/
public abstract class Pancake {
    
    public String desc = "我不是一个具体的煎饼";

    public String getDesc() {
        return desc;
    }
    
    public abstract double price();

}
/* ConcreteComponent类*/
public class TornCake extends Pancake {

    public TornCake() {
        desc = "手抓饼";
    }

    @Override
    public double price() {
        return 4;
    }

}
public class Roujiamo extends Pancake {

    public Roujiamo() {
        desc = "肉夹馍";
    }

    @Override
    public double price() {
        return 6;
    }

}
/* Decorator 类*/
public abstract class Condiment extends Pancake {

    public abstract String getDesc();

}
/* ConcreteDecorator 类 */
public class FiredEgg extends Condiment {
    private Pancake pancake;
    
    public FiredEgg(Pancake pancake) {
        this.pancake = pancake;
    }

    @Override
    public String getDesc() {
        return pancake.getDesc() + ", 煎蛋";
    }

    @Override
    public double price() {
        return pancake.price() + 2;
    }

}
public class Ham extends Condiment {
    private Pancake pancake;
    
    public Ham(Pancake pancake) {
        this.pancake = pancake;
    }

    @Override
    public String getDesc() {
        return pancake.getDesc() + ", 火腿片";
    }

    @Override
    public double price() {
        return pancake.price() + 1.5;
    }

}
public class MyTest {
    
    @Test
    public void test() {
        Pancake tornCake = new TornCake();
        //手抓饼基础价
        System.out.println(String.format("%s ¥%s", tornCake.getDesc(), tornCake.price()));
        
        Pancake roujiamo = new Roujiamo();
        roujiamo = new FiredEgg(roujiamo);
        roujiamo = new FiredEgg(roujiamo);
        roujiamo = new Ham(roujiamo);
        roujiamo = new MeatFloss(roujiamo);
        roujiamo = new Cucumber(roujiamo);
        //我好饿
        System.out.println(String.format("%s ¥%s", roujiamo.getDesc(), roujiamo.price()));
    }

}

 

优点:

  • 扩展对象功能,比继承灵活,不会导致类个数急剧增加
  • 可以对一个对象进行多次装饰,创造出不同行为的组合,得到功能更加强大的对象
  • 具体构建类和具体装饰类可以独立变化,用户可以根据需要自己增加新的具体构件子类和具体装饰子类

  缺点:

  • 产生很多小对象。大量小对象占据内存,一定程度上影响性能
  • 装饰模式易于出错,调试排查比较麻烦
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值