这篇文章主要是讲解,分析23种设计模式中的装饰者模式,本文还是以一个吃货的角度 为大家讲解装饰者模式、希望与大家共同进步,如观者另有高见,欢迎大家拍砖、指点;我会不胜感激。
一、装饰者模式:
概念:
在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
故事场景:
满满的工作一周了,周末风雨交加十分寒冷,心血来潮打算吃份米线暖暖身子,随便一走看见一家新开的米线店,感觉不错,那么我们的就进去走走吧~
我:老板,米线怎么卖?
老板:先生您好,我们这底锅分为三种:清汤套餐是10元,微辣套餐是11元,超辣套餐是12元;
荤类:肥牛8元,羊肉片9元;
素菜类:菠菜3元,油菜4元;
点餐规则:底锅一定要点一份,其他随意选择。
我:一份微辣底锅 + 一份肥牛 +一份油菜;
老板:好嘞~
小伙伴们面对这样的一份套餐要计算money的流程怎么实现呢?
哼~不就是算钱嘛,这还不是小菜一碟?代码走起:
锅底实体:
/**
* Created by Sunrui on 2017/5/13.
* 锅底实体类
*/
public class RiceNoodle {
//清汤
private String clearsoup = "清汤";
//微辣
private String minhotsoup = "微辣";
//超辣
private String maxhotsoup = "超辣";
public String getClearsoup() {
return this.clearsoup;
}
public String getMinhotsoup() {
return this.minhotsoup;
}
public String getMaxhotsoup() {
return this.maxhotsoup;
}
public double getSoupPrice(String soupType){
if("清汤".equals(soupType)){
return 10.00;
}else if("微辣".equals(soupType)){
return 11.00;
}else if("超辣".equals(soupType)){
return 12.00;
}else{
throw new RuntimeException("没有您选择的锅底类型");
}
}
}
肉类实体:
肉类实体:
/**
* Created by Sunrui on 2017/5/13.
* 肉类实体
*/
public class Meat {
//肥牛
private String beef = "牛肉";
//羊肉
private String sheep = "羊肉";
public String getBeef(){
return this.beef;
}
public String getSheep(){
return this.sheep;
}
public double getMeatPrice(String meatType){
if("牛肉".equals(meatType)){
return 8.00;
}else if("羊肉".equals(meatType)){
return 9.00;
}else{
throw new RuntimeException("没有您选择的肉品类型");
}
}
}
蔬菜实体:
/**
* Created by Sunrui on 2017/5/13.
* 蔬菜实体
*/
public class Vegetables {
//菠菜
private String spinach = "菠菜";
//油菜
private String rape = "油菜";
public String getSpinach() {
return this.spinach;
}
public String getRape() {
return this.rape;
}
public double getVegetablesPrice(String vegetableType){
if("菠菜".equals(vegetableType)){
return 3.00;
}else if("油菜".equals(vegetableType)){
return 4.00;
}else{
throw new RuntimeException("没有您选择的蔬菜类型");
}
}
}
消费者:
/**
* Created by Sunrui on 2017/5/14.
* 消费者
*/
public class SimpleCustomer {
public static void main(String[] args) {
//锅底
RiceNoodle riceNoodle = new RiceNoodle();
//菜
Vegetables vegetables = new Vegetables();
//肉
Meat meat = new Meat();
//总钱数:
double total = riceNoodle.getSoupPrice(riceNoodle.getMinhotsoup()) +
vegetables.getVegetablesPrice(vegetables.getRape()) +
meat.getMeatPrice(meat.getBeef());
System.out.println("套餐类型:" + riceNoodle.getMinhotsoup() +
meat.getBeef() + vegetables.getRape()+ " 共" + total + "元");
}
}
输出结果:
套餐类型:微辣牛肉油菜 共23.0元。
这样设计的确达到了基本的业务需求,但仔细想想,如果店多提供了一种荤菜、素菜是不是我们又要修改原始的类?聪明的你可能已经想到了,没错,这样做违背了开闭原则,那么有没有更好的方法来对这样的扩展有更灵活的方式呢?
-----------------------------------------------华丽的分割线------------------------------------------
让我们看下怎么用新的办法对原来的进行改装:
角色:
Component: 抽象构件。是定义一个对象接口,可以给这些对象动态地添加职责。
ConcreteComponent:具体构件。是定义了一个具体的对象,也可以给这个对象添加一些职责。
Decorator: 抽象装饰类。是装饰抽象类,继承了Component,从外类来扩展Component类的功能,但对于Component来说,是无需知道Decorator存在的。
ConcreteDecorator:具体装饰类,起到给Component添加职责的功能。
抽象构件:
/**
* Created by Sunrui on 2017/5/14.
* 清汤底料实体类
*/
public class ClearSoup implements RiceNoodle{
@Override
public String description() {
return "清汤";
}
public double caculatePrice() {
return 10.00;
}
}
具体构件:
清汤:
/**
* Created by Sunrui on 2017/5/14.
* 清汤底料实体类
*/
public class ClearSoup implements RiceNoodle{
@Override
public String description() {
return "清汤";
}
public double caculatePrice() {
return 10.00;
}
}
微辣:
/**
* Created by Sunrui on 2017/5/14.
* 微辣底料实体类
*/
public class MinHotSoup implements RiceNoodle{
@Override
public String description() {
return "微辣";
}
@Override
public double caculatePrice() {
return 11.00;
}
}
/**
* Created by Sunrui on 2017/5/14.
* 超辣底料实体类
*/
public class MaxHotSoup implements RiceNoodle{
@Override
public String description() {
return "超辣";
}
@Override
public double caculatePrice() {
return 12.00;
}
}
抽象装饰类:
/**
* Created by Sunrui on 2017/5/14.
* 荤菜超类
*/
public interface RiceNoodleAddMeat extends RiceNoodle{
public abstract String getMeatDescription();
}
/**
* Created by Sunrui on 2017/5/14.
* 素菜超类
*/
public interface RiceNoodleAddVegetables extends RiceNoodle{
public abstract String getVegetableDescription();
}
具体装饰类:
/**
* Created by Sunrui on 2017/5/14.
* 具体装饰类(加了牛肉)
*/
public class RiceNoodleAddBeaf implements RiceNoodleAddMeat{
private RiceNoodle riceNoodle;
public RiceNoodleAddBeaf (RiceNoodle riceNoodle) {
this.riceNoodle = riceNoodle;
}
@Override
public String getMeatDescription() {
return "多了牛肉味道";
}
@Override
public String description() {
return riceNoodle.description() + "+ 牛肉";
}
@Override
public double caculatePrice() {
return riceNoodle.caculatePrice() + 8.00;
}
}
/**
* Created by Sunrui on 2017/5/14.
* 具体装饰类(加了羊肉)
*/
public class RiceNoodleAddSheep implements RiceNoodleAddMeat{
private RiceNoodle riceNoodle;
public RiceNoodleAddSheep (RiceNoodle riceNoodle) {
this.riceNoodle = riceNoodle;
}
@Override
public String getMeatDescription() {
return "多了羊肉味道";
}
@Override
public String description() {
return riceNoodle.description() + "+ 羊肉";
}
@Override
public double caculatePrice() {
return riceNoodle.caculatePrice() + 9.00;
}
}
/**
* Created by Sunrui on 2017/5/14.
* 蔬菜具体装饰类(加了油菜)
*/
public class RiceNoodleAddRape implements RiceNoodleAddVegetables{
private RiceNoodle riceNoodle;
public RiceNoodleAddRape (RiceNoodle riceNoodle) {
this.riceNoodle = riceNoodle;
}
@Override
public String getVegetableDescription() {
return "多了油菜味道";
}
@Override
public String description() {
return riceNoodle.description() + "+ 油菜";
}
@Override
public double caculatePrice() {
return riceNoodle.caculatePrice() + 4.00;
}
}
/**
* Created by Sunrui on 2017/5/14.
* 具体装饰类(加了菠菜)
*/
public class RiceNoodleAddSpinach implements RiceNoodleAddVegetables{
private RiceNoodle riceNoodle;
public RiceNoodleAddSpinach (RiceNoodle riceNoodle) {
this.riceNoodle = riceNoodle;
}
@Override
public String description() {
return riceNoodle.description() + "+ 波菜";
}
@Override
public String getVegetableDescription() {
return "多了菠菜味道";
}
@Override
public double caculatePrice() {
return riceNoodle.caculatePrice() + 3.00;
}
}
好了,到此为止,我们已经实现了需求的功能了,是不是每个类都很清晰加简单,下面看测试:
/**
* Created by Sunrui on 2017/5/14.
* 消费者
*/
public class DecorationCustomer {
public static void main(String[] args) {
RiceNoodleAddBeaf riceNoodleAddBeaf = new RiceNoodleAddBeaf(new RiceNoodleAddRape(new MinHotSoup()));
System.out.println("套餐类型:" + riceNoodleAddBeaf.description() +
" 共" + riceNoodleAddBeaf.caculatePrice() + "元");
}
}
结果:套餐类型:微辣+油菜+ 牛肉 共23.0元
怎么样,大家感受到了装饰着模式的好处了嘛?就算多几种蔬菜,多几种荤菜,我们只需加相应的品类即可,对之前的具象类完全不用修改,十分方便,而且加几份同样的蔬菜或者肉类也都可以随便加入,不必跟原来的实体类产生很多聚合,(比如我想再加入一份牛肉,就再包裹一个new RiceNoodleAddBead()即可)是不是方便了许多?
装饰者模式的优缺点:
优点:
1、装饰者模式可以提供比继承更多的灵活性
2、可以通过一种动态的方式来扩展一个对象的功能,在运行时选择不同的装饰器,从而实现不同的行为。
3、通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。
4、具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”。
缺点:
1、会产生很多的小对象,增加了系统的复杂性
2、这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。
装饰者模式的适用场景:
1、在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
2、需要动态地给一个对象增加功能,这些功能也可以动态地被撤销。 当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。
个人理解装饰者模式的精髓就在于:
1、装饰者(decorator)和被装饰(扩展)的对象有着相同的超类(supertype)。2、我们可以用装饰过的对象替换代码中的原对象,而不会出问题(因为他们有相同的超类)。