设计模式-java

设计模式之装饰模式(结构型)--- 10

2019-07-21  本文已影响0人  auzqy
  • 导语
  • 怎么用
    1.样例背景
    2.UML类图
    3.代码示例
        3.1 不用设计模式时,以及此时存在的问题
        3.2 使用设计模式后的情况
  • 优缺点
  • 使用场景
    1.概括描述
    2.现存知名产品中的使用示例
  • 与其他设计模式的对比
  • 参考

导语

装饰(者)模式(Decorator,也叫包装器模式),在不改变原有对象的基础之上,动态的将功能附加到对象上。
提供了比继承更有弹性的替代方案(扩展原有对象的功能)

如果比较全的话要有以下几个角色:
1.抽象被装饰者
2.实际被装饰者
3.抽象装饰者
4.实际装饰者

特别注意:装饰模式的装饰顺序很重要,比如加密数据过滤词汇都可以是数据持久化之前的装饰功能,但若是先加密了数据后过滤词汇就会出问题,最理想的情况,是保证装饰类之间彼此独立,这样他们就可以以任意顺序组合了。

怎么用

共有2个示例,代码详见访问链接
下面以example1举例说明

1. 样例背景

吃早点,买煎饼
有的人愿意加个鸡蛋,有的人愿意加两个鸡蛋,有的人愿意加火腿,有的人……

2. UML类图

AbstractBatterCake -------------- 抽象被装饰者
BatterCake ---------------- 实际被装饰者
BatterCakeDecorator ------ 抽象装饰者(这里只是没有定义成抽象类)
SausageDecorator ----------------- 实际装饰者
EggDecorator ----------------- 实际装饰者

example1 使用装饰模式后 UML类图

3. 代码示例

3.1 使用装饰模式之前

/**
 * @Description: 煎饼
 */
public class BatterCake {
    protected String getDesc(){
        return "煎饼";
    }
    protected int cost(){
        return 8;
    }
}

/**
 * @Description: 加了鸡蛋的煎饼
 */
public class BatterCakeWithEgg extends BatterCake {
    @Override
    protected String getDesc() {
        return super.getDesc() + "加了一个鸡蛋";
    }

    @Override
    protected int cost() {
        return super.cost() + 2;
    }
}

/**
 * @Description: 加了鸡蛋和火腿的煎饼
 */
public class BatterCakeWithEggSausage extends BatterCake {
    @Override
    protected String getDesc() {
        return super.getDesc() + "加了一个鸡蛋,又加了一个火腿";
    }

    @Override
    protected int cost() {
        return super.cost() + 4;
    }
}

/**
 * @Description: 测试类
 */
public class Test {
    public static void main(String[] args) {
        beforeDecorator();
    }

    /**
     * @Description: 使用装饰者模式之前
     *          在现有条件下只能满足这两种需求
     *          如果客户还有别的需求怎么办?比如,需要加5个火腿2个鸡蛋
     */
    public static void beforeDecorator(){
        // 要一个加鸡蛋的煎饼
        BatterCake batterCake1 = new BatterCakeWithEgg();
        System.out.println(batterCake1.getDesc() + " 销售价格:" + batterCake1.cost());

        // 要一个加鸡蛋和一个火腿的煎饼
        BatterCake batterCake2 = new BatterCakeWithEggSausage();
        System.out.println(batterCake2.getDesc() + " 销售价格:" + batterCake2.cost());
    }
}

3.2 使用装饰模式之后

/**
 * @Description: 煎饼
 */
public abstract class AbstractBatterCake {
    protected abstract String getDesc();
    protected abstract int cost();
}

/**
 * @Description: 具体煎饼(具体的被装饰类)          
 */
public class BatterCake extends AbstractBatterCake{
    @Override
    protected String getDesc(){
        return "煎饼";
    }
    @Override
    protected int cost(){
        return 8;
    }
}

/**
 * @Description: 抽象的装饰者类
 *          因为你不知道将来的客户到底需要什么样的煎饼
 *
 *          如果 那些具体的装饰类中,有一些基础的共性的动作,那么可以把该类定义为抽象类,
 *          并添加基础公共的方法
 */
public class BatterCakeDecorator extends AbstractBatterCake{
    private AbstractBatterCake abstractBatterCake;

    public BatterCakeDecorator(AbstractBatterCake abstractBatterCake) {
        this.abstractBatterCake = abstractBatterCake;
    }

    @Override
    protected String getDesc() {
        return abstractBatterCake.getDesc();
    }

    @Override
    protected int cost() {
        return abstractBatterCake.cost();
    }
}

/**
 * @Description: 加了鸡蛋的装饰类(具体的装饰类)
 */
public class EggDecorator extends BatterCakeDecorator {

    public EggDecorator(AbstractBatterCake abstractBatterCake) {
        super(abstractBatterCake);
    }

    @Override
    protected String getDesc() {
        return super.getDesc() + " 加了一个鸡蛋";
    }

    @Override
    protected int cost() {
        return super.cost() + 2;
    }
}

/**
 * @Description: 加了火腿的装饰类(具体的装饰类)
 */
public class SausageDecorator extends BatterCakeDecorator {
    public SausageDecorator(AbstractBatterCake abstractBatterCake) {
        super(abstractBatterCake);
    }

    @Override
    protected String getDesc() {
        return super.getDesc() + " 加了一个火腿";
    }

    @Override
    protected int cost() {
        return super.cost() + 2;
    }
}

/**
 * @Description: 测试类
 * @Author: zqy
 * @CreateTime: 2019-07-20 19:55
 */
public class Test {
    public static void main(String[] args) {
        afterDecorator();
    }

    /**
     * @Description: 采用装饰者模式后
     *              以不变应万变
     */
    public static void afterDecorator(){

        AbstractBatterCake batterCakeBase = new BatterCake();

        // 要一个加鸡蛋的煎饼
        AbstractBatterCake batterCake1 = new EggDecorator(batterCakeBase);
        System.out.println(batterCake1.getDesc() + " 销售价格:" + batterCake1.cost());

        // 要一个加鸡蛋和一个火腿的煎饼
        AbstractBatterCake batterCake2 = new SausageDecorator(
                new EggDecorator(batterCakeBase));
        System.out.println(batterCake2.getDesc() + " 销售价格:" + batterCake2.cost());

        // 要二个加鸡蛋和一个火腿的煎饼
        AbstractBatterCake batterCake3 = new SausageDecorator(
                new EggDecorator(
                        new EggDecorator(batterCakeBase)));
        System.out.println(batterCake3.getDesc() + " 销售价格:" + batterCake3.cost());
    }
}

优缺点

使用场景

1. 概括描述

2. 现存知名产品中的使用示例 todo

2.1 java.io.BufferedReader (jdk)

2.2 java.io.BufferedInputStream(jdk)

2.3 org.springframework.cache.transaction.TransactionAwareCacheDecorator

(spring)

2.4 org.apache.ibatis.cache.Cache (mybatis)

相关设计模式

  1. 装饰模式和代理模式
    装饰模式关注在一个对象上动态的添加方法;
    代理模式关注于控制对对象的访问;
    使用上二者的不同:
    代理模式中的代理类,可以对他的客户隐藏一个对象的具体信息,我们通常在使用代理模式的时候,常常在一个代理类中创建一个对象的实例,而当我们使用装饰模式的时候,我们通常会把原始对象作为一个参数,传给装饰者的构造器,这个是使用上的一些不同。
    从结构上来看和装饰模式类似,但代理模式是控制,更像是一种对功能的限制,而装饰模式是增加职责。

  2. 装饰模式和适配器模式
    他们都可以叫做包装模式(wrapper)
    装饰者和被装饰者可以实现相同的接口,或者装饰者是被装饰者的子类
    在适配器模式中,适配器和被适配的类具有不同的接口,当然也有可能是有部分接口是重合的。
    (如果在深入一点,装饰者模式还可以退化成半装饰者模式,也就是说,一个装饰者除了提供被装饰类的接口外,还提供了其他的方法,那就变成了一个半透明的装饰者,如果应用层代码想使用这个提供了特殊功能的方法的话,就要使用具体的装饰者类了。半装饰者在实际的场景中应用的比较少,重点关注装饰者就可以了。)

参考

  1. https://coding.imooc.com/learn/list/270.html(强烈推荐)
  2. https://en.wikipedia.org/wiki/Design_Patterns
  3. 大话设计模式,电子版下载链接,https://pan.baidu.com/s/17WOI3Bvp-JUoQXvaomHISg 密码:vw05
    (作者博客,https://www.cnblogs.com/cj723/archive/2007/12/30/1021314.html)
  4. https://www.cnblogs.com/geek6/p/3951677.html
  5. https://mp.weixin.qq.com/s/eAlPRqScG3-Acvi3HwYK3A
上一篇 下一篇

猜你喜欢

热点阅读