1)概述
装饰模式又称为包装模式。装饰模式以对客户透明的方式给一个对象动态添加一些额外的职责,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式是继承关系的一个替代方案,可以在不使用创造更多子类的情况下,将对象的功能加以扩展;与生成子类相比,它更具有灵活性。
场景:
1.家兴喜欢穿各种各样的衣服,每一种的选择都有不同的展示,效果都不一样;但是无论什么,都是本人穿的衣服,至于怎么搭配看个人怎么去实现。
2.家兴在网上商城看中一款手机,这个手机有几个套餐,分别是标准套餐,套餐一,套餐二,套餐三,套餐四等;他们的价格也会不一样,但是他们都是在一台手机的基础上增加其他配件的,每一种配件的价格不一样,套餐的总价就等于裸机加上各种配件的价格。
2)提出问题?
如上面商城卖手机的场景,如果后来为了适应市场的需求,需要为手机减价或者是添加新的配件,这些又该如何去设计呢?
3)解决方案
因为商城上的物品是随时都在变化的,今天可能加一个配件,明天可能对某个配件加价或减价。所以在代码这方面必须要求是很灵活,同时要满足开闭原则,达到灵活性要求则要满足依赖倒转原则。所以根据这一需求,我们使用装饰模式来进行处理。
4)结构
Phone :手机接口,规范准备接口附加责任的对象。具体组件和装饰的公共父类型。
Earphone:耳机组件,实现Phone接口,定义一个要接收耳机套餐的类。可以通过给它添加装饰来增加新的功能。
Cell-phoneCase:手机套组件,实现Phone接口,定一个要接收手机套组件。通过装饰来增加新的功能。
SelfieStick:手机自拍杆组件,实现Phone接口,定义一个含有自拍刚的套餐。
PhoneDecorator:手机装饰类,实现Phone接口,定义抽象类,手机的具体装饰类的公共父类型。
SmallMemory:小内存版本手机具体装饰类,继承PhoneDecorator类,实现手机的其中一个类型。
MiddleMemory:中内存版本手机具体装饰类,继承PhoneDecorator类,实现其中一个类型功能。
LargeMemory:大内存版本手机具体装饰类,继承PhoneDecorator类,表示其中一个类型功能。
5)装饰模式XML图
6)代码实现
//接口类Phone
public interface Phone {
public double cost();
public String GetDesc();
}
//手机装饰类
public abstract class PhoneDecorator implements Phone{
public double cost(){
return 0;
}
}
//具体组件1
public class SmallMemory implements Phone{
@Override
public double cost() {
// TODO Auto-generated method stub
return 2288.00;
}
@Override
public String GetDesc() {
// TODO Auto-generated method stub
return "荣耀P9 16G\n";
}
}
//具体组件2
@Override
public double cost() {
// TODO Auto-generated method stub
return 2588.00;
}
@Override
public String GetDesc() {
// TODO Auto-generated method stub
return "荣耀P9 32G\n";
}
}
//具体组件3
public class LargeMemory implements Phone{
@Override
public double cost() {
// TODO Auto-generated method stub
return 2888.00;
}
@Override
public String GetDesc() {
// TODO Auto-generated method stub
return "荣耀P9 64G\n";
}
}
//具体装饰类1
public class Earphone extends PhoneDecorator{
Phone phone;
public Earphone(Phone phone){
this.phone = phone;
}
@Override
public double cost() {
// TODO Auto-generated method stub
return 88.00+phone.cost();
}
public String GetDesc() {
// TODO Auto-generated method stub
return phone.GetDesc()+"荣耀e31耳机\n";
}
}
//具体装饰类2
public class CellphoneCase extends PhoneDecorator{
private Phone phone;
public CellphoneCase(Phone phone){
this.phone = phone;
}
@Override
public String GetDesc() {
// TODO Auto-generated method stub
return phone.GetDesc()+"荣耀手机套P9\n";
}
@Override
public double cost() {
// TODO Auto-generated method stub
return 38.00+phone.cost();
}
}
//具体装饰类3
public class SelfieStick extends PhoneDecorator{
private Phone phone;
public SelfieStick(Phone phone){
this.phone = phone;
}
@Override
public String GetDesc() {
// TODO Auto-generated method stub
return phone.GetDesc()+"荣耀自拍无线蓝牙自拍杆\n";
}
@Override
public double cost() {
// TODO Auto-generated method stub;
return 58.00+phone.cost();
}
}
//客户端
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
//16G标配
System.out.println("16G标配");
Phone phone = new SmallMemory();
System.out.println(phone.GetDesc()+phone.cost()+"¥\n");
//32G套餐一
System.out.println("32G套餐一");
Phone phone1 = new MiddleMemory();
phone1 = new Earphone(phone1);
System.out.println(phone1.GetDesc()+phone1.cost()+"¥\n");
//64G套餐二
System.out.println("64G套餐三");
Phone phone2 = new LargeMemory();
phone2 = new Earphone(phone2);
phone2 = new CellphoneCase(phone2);
phone2 = new SelfieStick(phone2);
System.out.println(phone2.GetDesc()+phone2.cost()+"¥\n");
}
}
程序输出效果:
16G标配
荣耀P9 16G
2288.0¥
32G套餐一
荣耀P9 32G
荣耀e31耳机
2676.0¥
64G套餐三
荣耀P9 64G
荣耀e31耳机
荣耀手机套P9
荣耀自拍无线蓝牙自拍杆
3072.0¥
通过这些结果我们可以看出来,我们可以很灵活的组合我们想要组成的组合,把我们想要增加的行为放在被包装的对象上。
7)使用场所
1.当系统需要新的功能的时候,是向旧的类中添加新的代码。这些新的代码通常装饰了原有的核心职责或主要行为,而这些新加入的东西仅仅是为了满足一些只在某种特定情况下才会被执行的特殊行为的需要。而装饰模式却提供了了一个非常好的解决方案,它把每个要装饰的功能放在单独的类中,并让这个类中包装它所需要装饰的对象,因此当执行特殊行为的时候,客户代码就可以在执行时有选择地,按顺序地使用装饰功能包装对象了。
装饰模式的好处就是有效地把类的核心职责和装饰工能区分开了。而且还可以去除相关类中重复的装饰逻辑。