设计模式之装饰者模式
装饰者模式:
在<<Java与模式>>一书是这样描述的:孙悟空有七十二般变化,每一种变化都可以附加一种新的本领,变成鱼儿游在水里,变成雀儿飞在天空,但是在二郎神眼里,他始终还是那只猢狲.
装饰者模式也叫包装器模式 ---- 是你还有你,一切拜托你
1)可以实现动态(在编译器是固定的,在运行期是随着java程序运行的不同而变化)地为对象增加一个新的功能
2)使用组合或聚合的形式代替继承的方式来扩展新的功能,使整个功能模块更加灵活,很好的避免了新增功能使得类快速增多,从而使整个软件类复杂度极高的情况发生.
包含角色:
Component(抽象的主体角色): 是一个具体的主体对象和装饰者对象都要去实现的接口或者需要继承的抽象类.这样客户端能够像调用具体的主体对象相同的方法去调用装饰者对象完成功能'
ConcreteComponent(具体的主体角色,被装饰者角色).就是一个用于被装饰的主体,也可以单独调用;
Decorator:(抽象装饰者角色),一般为一个抽象类,也可以是实体类,它持有一个抽象主体的引用,并接受客户端的请求,并把这些请求转发给具体的被装饰者对象去处理,这样就能在具体被装饰者调用前后增加新的功能
ConcreteDecorator(具体装饰者角色):负责给被装饰者扩展新的功能
为了说明在特定场景装饰者模式的优点,我们举个栗子
我们现有一个KFC店铺,销售披萨,披萨共有两种口味,假设我们这样设计
披萨抽象类
public abstract class Pizza { protected String desc; public String getDesc() { return desc; } public abstract double cost(); }
披萨具体类
public class OriginalPizza extends Pizza{ public OriginalPizza() { this.desc="普通披萨"; } @Override public double cost() { return 10; } }
public class BeefPizza extends Pizza { public BeefPizza() { this.desc="牛肉披萨"; } @Override public double cost() { return 20; } }
public class FishPizza extends Pizza{ public FishPizza(){ this.desc="烤鱼披萨"; } @Override public double cost() { return 15; } }
public class Client { public static void main(String[] args) { Pizza pizza = new FishPizza(); System.out.println("吃:"+pizza.desc+" 花费:"+pizza.cost()); } }
但是随着业务的增长,消费者想吃鲍鱼披萨,有的消费者想吃鲍鱼牛肉披萨,有的想吃牛肉烤鱼披萨......
总之,上帝的需求越来越多,千奇百怪,我们难道要继承抽象类,每一种口味都设计成一种披萨类吗?这样会导致类的数量暴增,后期维护也相当麻烦.而装饰者模式就看到了这一点,大家都要吃披萨,只不过是披萨的口味不太一样,我们可以把披萨和搭配的口味分离解耦,可不可以这样去设计,把牛肉,烤鱼,单独设计成一个类,需要什么我们就在披萨抽象类添加什么,但是我们每加一个口味,就需要改动披萨抽象类添加一种口味类进去,这样虽然减少了类的暴增,但是违反了开闭原则.所以我们看下使用装饰者模式是如何解决这种场景下的问题的.
抽象的被装饰者主体
public abstract class Pizza { protected String desc; public String getDesc() { return desc; } public abstract double cost(); }
具体的被装饰者主体
public class OriginalPizza extends Pizza{
public OriginalPizza() {
this.desc="普通披萨";
}
@Override
public double cost() {
return 10;
}
}
抽象装饰者(在实际应用中可能不是抽象类,但是由于它的功能是抽象装饰功能)
public abstract class Decorator extends Pizza {
protected Pizza pizza;
public Decorator(Pizza pizza) {
this.pizza = pizza;
this.desc = pizza.desc;
}
@Override
public abstract double cost();
}
具体装饰者
public class Beef extends Decorator{
public Beef(Pizza pizza) {
super(beef);
this.desc += "牛肉";
}
@Override
public String getDesc() {
return desc;
}
@Override
public double cost(){
return pizza.cost()+10.0;
}
}
当我们需要增加一种口味,也就相当于增加一个装饰者,客户端调用是无需改动的,口味可以自由组合搭配,完全无需改动
public class Client { public static void main(String[] args) { Pizza pizza = new Beef(new OriginalPizza()); System.out.println("吃:"+pizza.desc+" 花费:"+pizza.cost()); } }
装饰模式使用场景:
1)需要扩展一个类的功能,或者给类附加责任
2)需要动态地给一个对象增加功能,这些功能可以动态地撤销
3)需要增加由一些功能的排列组合而产生的非常大量的功能,从而使继承关系变得不显示
模式的简化:(引用<<Java与模式>>)
实际开发可能没有这么标准的装饰者模式,多少会简化
在实际开发中如果没有抽象被装饰者主体(Component),只有具体装饰者主体(ConcreteComponent),那么抽象装饰者(Decorator)可以是具体被装饰者的子类
如果只有一个ConcreteDecorator类,那么就没有必要建立一个单独的Decorator类,而可以把Decorator和ConcreteDecorator的责任合并成一个类,两个也可以这样做.但是如果ConcreteDecorator超过三个,使用一个Decorator类去区分抽象和具体责任就很有必要.