##需求
具体应用场景有很多,比如穿衣打扮等需要各种组合的场景。这里以食堂点餐举例。
很多人肯定吃过食堂里米饭+各种菜组合的盖饭,菜包括宫保鸡丁、鱼香肉丝和番茄鸡蛋等等。不同的菜有不同的价格,那么如何在程序里得到具体点了什么菜和最终的价格呢?
第一时间想到肯定是继承。
- 定义米饭的父类。
- 对于不同的菜分别继承米饭这个父类,例如宫保鸡丁米饭,鱼香肉丝米饭等等。
- 对于组合的菜再分别继承米饭这个父类,例如宫保鸡丁和鱼香肉丝米饭,宫保鸡丁和番茄鸡蛋米饭甚至点四五个菜组合的等等。
这样虽然可以实现功能,但是我们会发现如果菜品够多的话,我们需要组合各种菜去继承父类米饭。同时,如果新增了一道菜,需要去组合其他每一道菜,增加好多庞大的子类。这时候我们可以考虑学习使用装饰模式。
##定义
装饰模式(Decorator),动态地给一个对象添加一些额外的功能和职责。就增加功能来说,装饰模式比生成子类更为灵活。
##UML
- Component定义了一个对象接口,可以给这些对象动态地添加一些功能。
- ConcreteComponent是定义了一个具体的对象,也可以给这个添加一些职责。
- Decorator,装饰抽象类,继承了Component,从外类来扩展Component功能,但对于Component 来说,是无需知道Decorator 的存在的。
- ConcreteDecorator,具体装饰对象,起到给Component添加职责的功能。
##实现
就上面点餐的场景根据UML进行设计
1.定义一个Rice(Component)的接口
/**
* 米饭接口 Component
* @Description TODO
* @ClassName Rice
* @Author Lay
*/
public interface Rice {
/**
* 获取价格
* @return
*/
public int getPrice();
/**
* 获取名称
* @return
*/
public String getName();
}
2.实现一个具体的BaseRice(ConcreteComponent)实现类,实现普通的米饭
/**
* 基本米饭类,实现米饭接口
* @Description TODO
* @ClassName BaseRice
* @Author Lay
* */
public class BaseRice implements Rice {
@Override
public int getPrice() {
return 5;//米饭价格为5
}
@Override
public String getName() {
return "米饭";//返回名称
}
}
3.定义米饭的装饰抽象类RiceDecorator,实现Rice接口
/**
* 米饭装饰类,继承了Rice基类
* @Description TODO
* @ClassName RiceDecorator
* @Author Lay
*/
public abstract class RiceDecorator implements Rice {
Rice mRice;
//定义Rice基类的对象,通过构造器引用传递进来的对象
public RiceDecorator(Rice mRice) {
this.mRice = mRice;
}
//将具体的行为(方法)放到子类中去实现
@Override
public abstract int getPrice();
@Override
public abstract String getName();
}
4.实现具体的装饰类GBJDRiceDecorator(concreteDecorator),宫保鸡丁装饰类
/**
* 宫保鸡丁装饰类,继承抽象装饰类RiceDecorator,具体去实现
* @Description TODO
* @ClassName GBJDRiceDecorator
* @Author Lay
*/
public class GBJDRiceDecorator extends RiceDecorator {
/**
* 传递引用对象,实现类的包装
* @param mRice
*/
public GBJDRiceDecorator(Rice mRice) {
super(mRice);
}
/**
* 核心:对功能的拓展和装饰(对价格和名称进行累积)
*/
@Override
public int getPrice() {
int price = 5;//宫保鸡丁价格为5
int totalPrice = mRice.getPrice() + price;//叠加价格(装饰)
return totalPrice;
}
@Override
public String getName() {
return "宫保鸡丁 " + mRice.getName();//组合饭名
}
}
其他具体的装饰类如下:
YXRSRiceDecorator(鱼香肉丝)
/**
* 鱼香肉丝装饰类,继承抽象装饰类RiceDecorator,具体去实现
* @Description TODO
* @ClassName GBJDRiceDecorator
* @Author Lay
*/
public class YXRSRiceDecorator extends RiceDecorator {
public YXRSRiceDecorator(Rice mRice) {
super(mRice);
}
@Override
public int getPrice() {
return mRice.getPrice() + 4;//鱼香肉丝价格是4
}
@Override
public String getName() {
return "鱼香肉丝 " + mRice.getName();//组合饭名
}
}
FQJDRiceDecorator(番茄鸡蛋)
//番茄鸡蛋装饰类
public class FQJDRiceDecorator extends RiceDecorator {
public FQJDRiceDecorator(Rice mRice) {
super(mRice);
}
@Override
public int getPrice() {
return mRice.getPrice() + 3;//番茄鸡蛋价格是3
}
@Override
public String getName() {
return "番茄鸡蛋 " + mRice.getName();//组合饭名
}
}
5.main函数
(这里测试是点了以上全部三个菜,具体可以进行不同组合包装)
public class TestMain {
public static void main(String[] args) {
Rice mRice = new BaseRice();//一碗白米饭
mRice = new GBJDRiceDecorator(mRice);//对米饭进行加菜-宫保鸡丁(装饰)
mRice = new YXRSRiceDecorator(mRice);//继续对米饭进行加菜-鱼香肉丝(装饰)
mRice = new FQJDRiceDecorator(mRice);//继续加菜-番茄鸡蛋
System.out.println("饭名:" + mRice.getName() + "\t价格:" + mRice.getPrice());
}
}
6.结果
##总结
1.装饰模式是为**已有功能动态地添加更多功能**的一种方式。
2.对于之前的继承,当系统需要新功能的时候,是向旧的类中添加新的代码。这些新加的代码通常装饰了原有类的核心职责或主要行为,这种做法的问题在于,它们在主类中加入了新的字段,新的方法和逻辑,从而增加了主类的复杂度,而这些新加入的东西仅仅是为了满足一些只在某种特定情况下才会执行的特殊行为的需求。而装饰模式却提供了一个非常好的解决方案,它把每个要装饰的功能单独放在单独的类中,并让这个类包装它所要装饰的对象,因此,当需要执行特殊行为时,客户代码就可以在运行时根据需要有选择地、按顺序的使用装饰功能包装对象了。
##优点:
1.把类中的装饰功能从类中搬移去除,这样可以简化原有的类。
2.有效地把类的核心职责和装饰功能**区分开了。而且可以去除相关类中重复的装饰逻辑。