【实例学模式】一针见血装饰器模式

目录

 

什么是装饰器模式

开发案例【奶茶店自助下单系统】

设计方案

对装饰器模式的思考


什么是装饰器模式

通常给对象添加功能,要么直接修改对象添加相应的功能,要么派生对应的子类来扩展,抑或是使用对象组合的方式。显然,直接修改对应的类这种方式并不可取。在面向对象的设计中,而我们也应该尽量使用对象组合,而不是对象继承来扩展和复用功能。装饰器模式就是基于对象组合的方式,可以很灵活的给对象添加所需要的功能。装饰器模式的本质就是动态组合。动态是手段,组合才是目的。总之,装饰模式是通过把复杂的功能简单化,分散化,然后再运行期间,根据需要来动态组合的这样一个模式。其中,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。

我相信你点进这篇文章并不是想看这些概念性的文字,我也不会长篇的论述,上面这么长的一段话最重要的就是最后一句:要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例

通俗易懂的理解就是【实现并拥有】。

开发案例【奶茶店自助下单系统】

假设现在你要给自己的奶茶店设计一套自助下单系统。你的奶茶店一共有三种产品:奶茶(¥5)、咖啡(¥6)、果汁(¥7)。可以在奶茶中添加的配料有:珍珠(¥2)、糖(¥3)、布丁(¥4)。 

假设我们下单了一本奶茶,那么我可以什么也不加;可以加珍珠;可以加珍珠并且加糖;可以加珍珠、糖、布丁,还有更多的组合方式。以此为例,我们发现这是一排列组合的问题。

如果让你来设计这样一个下单系统,你会如何设计呢?

设计方案

你可能首先想到的就是组合模式。

组合模式

组合模式可以实现我们的需求但是存在以下问题:

在每个饮料的基类中放一个List<Add> 表示附加的东西。来看看这么设计的问题:

  • 如果我们规定了茶里面就是不能加糖的,这种组合无效。
  • 假设我们有一天突发奇想,发明了一种新的饮料,咖啡+茶,那么这种设计显然不够灵活。

为了解决第二个问题,我们将所有的Berverage 和 add进行组合,枚举所有的产品,这显然不可行。

于是我们有了装饰器模式。我们不区分品类和配料,统一看成奶茶店的一种商品,请看下面的UML图:

装饰类设计的核心思想就是:所有的品类和配料都是商品。同时还包含一个商品

我们本着这个核心思想来实现代码:

/**
 * @ Author     :liuyanzhi
 * @ Date       :Created in 17:30 2020-12-18
 * @ Description:饮料接口类:被装饰者
 * @ Modified By:
 * @Version: 1.0$
 */
public interface Drinks {

    /**
     * 发票打印,我们假设只打印我们的组合和价格
     * @return
     */
    String invoicePrint();

    /**
     * 价格
     * @return
     */
    int price();
}
import org.springframework.stereotype.Service;

/**
 * @ Author     :liuyanzhi
 * @ Date       :Created in 17:38 2020-12-18
 * @ Description:奶茶:具体的被装饰者
 * @ Modified By:
 * @Version: 1.0$
 */
@Service
public class MilkTea implements Drinks{

    @Override
    public String invoicePrint() {
        return "您下单了";
    }

    @Override
    public int price() {
        //单价5
        return 5;
    }
}
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * @ Author     :liuyanzhi
 * @ Date       :Created in 17:42 2020-12-18
 * @ Description:加糖装饰器
 * @ Modified By:
 * @Version: 1.0$
 */
@Service
public class AddSugarDecorator implements Drinks{

    @Resource
    private Drinks drinks;

    public AddSugarDecorator(Drinks drinks) {
        this.drinks = drinks;
    }

    @Override
    public String invoicePrint() {
        return drinks.invoicePrint() + "糖";
    }

    @Override
    public int price() {
        //糖的单价是3
        return drinks.price() + 3;
    }
}
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * @ Author     :liuyanzhi
 * @ Date       :Created in 17:49 2020-12-18
 * @ Description:加珍珠装饰器
 * @ Modified By:
 * @Version: 1.0$
 */
@Service
public class AddPearlDecorator implements Drinks {

    @Resource
    private Drinks drinks;

    public AddPearlDecorator(Drinks drinks) {
        this.drinks = drinks;
    }

    @Override
    public String invoicePrint() {
        return drinks.invoicePrint() + "珍珠";
    }

    @Override
    public int price() {
        //珍珠的单价是4
        return drinks.price() + 4;
    }
}

    @Test
    public void test(){
        Drinks drinks = new AddPearlDecorator(new AddSugarDecorator(new AddPearlDecorator(new MilkTea())));
        System.out.println(drinks.invoicePrint() + "奶茶.");
        System.out.println("您需要支付"+  drinks.price() + "元.");
    }

运行结果如下:

对装饰器模式的思考

下面我来说说我的一些思考:

1、在实际开发中装饰器模式我觉得还是少用为妙,虽然他能看成继承的一种替代,让代码更加容易扩展,更加容易对对象进行增强。但是跟类的继承一样,代码编写起来复杂度还是偏高。而且容易出现非常多的装饰类不易维护。

2、在实际应用中我们可以结合数据库来配置众多的装饰,在我看来这才是对装饰模式的优雅的应用,获取装饰模式就该这样使用:

数据库支持配置,需要增加新的装饰器的时候只需要在DB中增加配置即可,不需要新增装饰器,扩展性大大提高,而且装饰器和被装饰者的关系清晰,代码的可维护性也得到很大提升。

谢谢你耐心的看完我的文章,喜欢请素质三连!

参考:【设计模式】一文彻底搞懂装饰器(Decorator)设计模式

猜你喜欢

转载自blog.csdn.net/lyztyycode/article/details/111373549