定义
23种设计模式之一,英文叫Decorator Pattern,又叫包装模式。装饰模式是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。
特点
- 装饰对象和真实对象有相同的接口。
- 装饰对象包含一个真实对象的引用。
- 装饰对象接受所有来自客户端的请求。它把这些请求转发给真实的对象
- 装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行时,不用修改给定对象的结构就可以在外部增加附加的功能。在面向对象的设计中,通常是通过继承来实现对给定类的功能扩展。
场景模拟
模拟冰淇淋店订单系统的案例:
冰淇淋种类:MilkCream、ChocolateCream、StrawberryCream、MixCream
配料种类:Candy、Fruit、Jam
一、利用oop思想设计方案:
1. 为所用冰淇淋类抽象出一个父类:IceCream,类中有私有成员变量description与getDescription()方法,抽象方法cost(),分别作为获取冰淇淋描述与价格的方法。
2. 所有冰淇淋类继承IceCream,分别定义其description与cost()方法。
3. 继续定义配料与冰淇淋组合的类:MilkCream&&Candy、MilkCream&&Fruit、MilkCream&&Candy&&Fruit…. 重新定义冰淇淋描述与价格的方法。
总结:设计到这里发现问题,若只需冰淇淋单品类的话可以接受,一旦配合配料一起设计类,发现需要创建的类too many…进而利用装设者模式改进此问题。
二、改进设计方案:
- 为所用冰淇淋类抽象出一个父类:IceCream,类中有私有成员变量description,与私有成员变量candy、fruit、jam,描述配料的信息。
有成员方法cost()与私有成员变量的set(),get()方法,在getDescription()中判断candy、fruit、jam的状态 - 所有冰淇淋类继承IceCream
总结:此种方案可以通过判断candy、fruit、jam的状态来获取是否加入该配料的信息,例如-1代表未条件该配料,正数代表添加了几分,解决了上个方案类爆炸的问题,但仍存在隐患,例如新加入一种配料的话,需要在已经设计好的类中继续修改程序,违反了程序设计中的开放-关闭原则,仍不理想。
三、利用装饰者设计模式设计方案:
- 设计所有类的超类Ice:
/*
* 所有类的超类,冰淇淋类配料类均继承此类
*/
public abstract class Ice {
//冰激凌或配料的描述
private String description = "";
//价格
private double price;
public String getDescription() {
return description+" 价格:"+this.price;
}
public void setDescription(String description) {
this.description = description;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public abstract double cost();
}
2.由于所有冰淇淋类中的cost()方法均为返回此价格,进而继续为所有冰淇淋类再次抽象出一个父类IceCream
public class IceCream extends Ice {
@Override
public double cost() {
return this.getPrice();
}
}
3.创建各个冰淇淋类
/*
* 牛奶冰淇淋!
*/
public class MilkCream extends IceCream {
public MilkCream(){
super.setDescription("牛奶冰淇淋!");
super.setPrice(3.5);
}
}
/*
* 巧克力冰淇淋!
*/
public class ChocolateCream extends IceCream {
ChocolateCream() {
super.setDescription("巧克力冰淇淋!");
super.setPrice(5);
}
}
/*
* 草莓冰淇淋!
*/
public class StrawberryCream extends IceCream {
StrawberryCream() {
super.setDescription("草莓冰淇淋!");
super.setPrice(4);
}
}
/*
* 什锦冰淇淋
*/
public class MixCream extends IceCream {
public MixCream(){
super.setDescription("什锦冰淇淋");
super.setPrice(6);
}
}
4.创建一个配料类Seasoning
public class Seasoning extends Ice {
private Ice Obj; //待装饰的对象
public Seasoning(Ice Obj){
this.Obj=Obj;
};
@Override
public double cost() {
return super.getPrice()+Obj.cost();
}
@Override
public String getDescription()
{
return super.getDescription()+"+"+Obj.getDescription();
}
}
5.创建各个配料类
/*
* 配料:糖果
*/
public class Candy extends Seasoning {
public Candy(Ice obj){
super(obj);
super.setDescription("糖果");
super.setPrice(2.0);
}
}
/*
* 配料:水果粒
*/
public class Fruit extends Seasoning {
public Fruit(Ice Obj) {
super(Obj);
super.setDescription("水果粒");
super.setPrice(2.5);
}
}
/*
* 配料:果酱
*/
public class Jam extends Seasoning {
public Jam(Ice Obj) {
super(Obj);
super.setDescription("果酱");
super.setPrice(1.5);
}
}
6.创建测试类IceShop,模拟订单系统
/*
* 冰淇淋商店类
*/
public class IceShop {
public static void main(String[] args) {
Ice ice;
ice = new MilkCream();
System.out.println("ice1 价格:"+ice.cost());
System.out.println("ice1 描述:"+ice.getDescription());
System.out.println("---------------------------------------");
ice = new MixCream();
ice = new Candy(ice);
ice = new Fruit(ice);
ice = new Jam(ice);
System.out.println("ice2 价格:"+ice.cost());
System.out.println("ice2 描述:"+ice.getDescription());
}
}
7.结果
8.图形模拟
优缺特点
- 装饰者模式与继承关系的目的都是要扩展对象的功能,但是装饰者可以提供比继承更多的灵活性。
- 使用不同的具体类与装饰类的排列组合,可以在不改变原有的基础上创造出很多不同行为的组合。
- 对比继承而言,代码更复杂。
适用场景
1.某个已有的对象添加新功能时
2.设计某个状态经常需要改动的对象时