装饰者模式是什么?
装饰者模式动态地将责任附加到对象上。若要扩展功能,装饰者模式提供了比继承更有弹性的代替方案。
装饰者模式解决什么问题?
现有奶茶类,description 用于描述奶茶中加的配料,cost用于返回价格:
public class MilkTea {
String description = "MilkTea";
public String getDescription() {
return description;
}
public int cost() {
return 7;
}
}
如果要给奶茶加配料(如布丁、珍珠等):
实现方式1
创建布丁奶茶继承奶茶,加上布丁的价格(珍珠同理):
class MilkTeaWithPudding extends MilkTea {
public MilkTeaWithPudding() {
description += "+Pudding";
}
@Override
public int cost() {
return super.cost() + 1;
}
}
- problem1:当配料越来越多时,其继承类也会越来越多(MilkTeaWithXXX)
- problem2:不可同时加配料(MilkTeaWithPuddingAndPearl)
- problem3:不可双倍加配料(MilkTeaWithDoublePudding)
- problem4:当推出新奶茶时(bananaMilkTea)又得维护其庞大的继承类
实现方式2
修改MilkTea,内部维护各种配料,有则在描述和价格中加上:
public class MilkTea {
String description = "MilkTea";
private boolean pudding = false;
private boolean pearl = false;
public void setPudding(boolean pudding) {
this.pudding = pudding;
}
public void setPearl(boolean pearl) {
this.pearl = pearl;
}
public String getDescription() {
if (pudding) {
description += "+pudding";
}
if (pearl) {
description += "+pearl";
}
return description;
}
public int cost() {
int cost = 7;
if (pudding) {
cost += 1;
}
if (pearl) {
cost += 1;
}
return cost;
}
}
- problem1:内部通过大量循环判断
- problem2:如果要加上新配料或配料价格改变时需要修改源代码
- problem3:当推出新奶茶时(bananaMilkTea),上述重复代码又得写一遍
装饰者模式实现
被装饰者
保持原有类不变:
public class MilkTea {
String description = "MilkTea";
public String getDescription() {
return description;
}
public int cost() {
return 7;
}
}
装饰者
装饰者继承被装饰者,内部维护被装饰者,抽出装饰者的公用方法:
abstract class CondimentDecorator extends MilkTea {
MilkTea milkTea;
public abstract String getDescription();
}
装饰者实例
新建Pudding和Pearl装饰者,重写方法加上自己的装饰(价格和描述),当推出新配料时,只要让其继承CondimentDecorator即可:
class Pudding extends CondimentDecorator {
public Pudding(MilkTea milkTea) {
this.milkTea = milkTea;
}
@Override
public String getDescription() {
return milkTea.description + "+Pudding";
}
@Override
public int cost() {
return milkTea.cost() + 1;
}
}
class Pearl extends CondimentDecorator {
public Pearl(MilkTea milkTea) {
this.milkTea = milkTea;
}
@Override
public String getDescription() {
return milkTea.description + "+Pearl";
}
@Override
public int cost() {
return milkTea.cost() + 1;
}
}
装饰过程
因为Pudding和Pearl继承CondimentDecorator,而CondimentDecorator继承MilkTea,故都可转为MilkTea类型进行操作:
MilkTea milkTea=new MilkTea();
milkTea=new Pudding(milkTea);
milkTea=new Pudding(milkTea);
milkTea=new Pearl(milkTea);
System.out.println(milkTea.getDescription()+" "+milkTea.cost());
新增被装饰者
当推出新奶茶时(BananaMilkTea ),只要让其继承MilkTea即可:
class BananaMilkTea extends MilkTea {
public BananaMilkTea() {
description = "BananaMilkTea";
}
public int cost() {
return 10;
}
}
新装饰过程
利用多态,装饰过程同理:
MilkTea milkTea=new BananaMilkTea ();
milkTea=new Pudding(milkTea);
milkTea=new Pudding(milkTea);
milkTea=new Pearl(milkTea);
System.out.println(milkTea.getDescription()+" "+milkTea.cost());
Tips:
- Java中的io流采用的就是装饰者模式,通过层层装饰完成对文件的读写操作,如new BufferedInputStream(new FileInputStream(path))