目录
什么是装饰器模式
通常给对象添加功能,要么直接修改对象添加相应的功能,要么派生对应的子类来扩展,抑或是使用对象组合的方式。显然,直接修改对应的类这种方式并不可取。在面向对象的设计中,而我们也应该尽量使用对象组合,而不是对象继承来扩展和复用功能。装饰器模式就是基于对象组合的方式,可以很灵活的给对象添加所需要的功能。装饰器模式的本质就是动态组合。动态是手段,组合才是目的。总之,装饰模式是通过把复杂的功能简单化,分散化,然后再运行期间,根据需要来动态组合的这样一个模式。其中,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。
我相信你点进这篇文章并不是想看这些概念性的文字,我也不会长篇的论述,上面这么长的一段话最重要的就是最后一句:要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。
通俗易懂的理解就是【实现并拥有】。
开发案例【奶茶店自助下单系统】
假设现在你要给自己的奶茶店设计一套自助下单系统。你的奶茶店一共有三种产品:奶茶(¥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中增加配置即可,不需要新增装饰器,扩展性大大提高,而且装饰器和被装饰者的关系清晰,代码的可维护性也得到很大提升。
谢谢你耐心的看完我的文章,喜欢请素质三连!