1装饰器模式
1.1装饰器模式定义
装饰器模式(Decorator Pattern),也叫包装模式 Wrapper Pattern
是指在不改变原有对象的基础之上,将功能附加到对象上.提供了比继承更灵活的替代方法 属于结构型模式.
1.2装饰器的UML类图
从类图分析 主要包含4种角色
抽象组件(Component):可以是一个接口或者抽象类:其充当被装饰类的原始对象,定义了被装饰的对象方法(既 抽出公用的方法)
具体实现类(ConcreteComponent): 继承/实现Component的一个具体对象,也是被装饰对象
抽象装饰器(Decorator)* 通用的装饰ConcreteComponent的装饰器,Decorator其内部必然有个属性指向Component 抽象组件;里面一般包含的是抽象类/接口,主要是为了让其子类按照其构造形式传入一
个 Component 抽象组件,这是强制的通用行为(当然,如果系统中装饰逻辑单一,并不需要实现许
多装饰器,那么我们可以直接省略该类,而直接实现一个具体装饰器(ConcreteDecorator)即可);
具体装饰器(ConcreteDecorator):Decorator 的具体实现类,理论上,每个 ConcreteDecorator
都扩展了Component对象的一种功能;
1.3装饰器的使用场景
1.用于拓展一个类的功能或者给一个类添加附加职责
2.动态的给一个对象添加功能,这些功能可以再动态的撤销。
3.需要为一批的兄弟类进行改装或加装功能。
1.4案例之手抓饼
场景:给我做个手抓饼+鸡蛋+香肠 然后告诉我一共多少钱
1.4.1菜鸟实现法之继承
首先定义一个煎饼类
/**煎饼Battercake类
* @Description
* @Author fanjia
* @Version V1.0.0
* @Since 1.0
* @Date 2020/6/11
*/
public class Battercake {
protected String getMsg(){ return "煎饼";}
public int getPrice(){ return 5;}
}
创建一个加1个鸡蛋的类
**创建一个加鸡蛋的煎饼 BattercakeWithEgg类
* @Description
* @Author fanjia
* @Version V1.0.0
* @Since 1.0
* @Date 2020/6/11
*/
public class BattercakeWithEgg extends Battercake{
@Override
protected String getMsg() {
return super.getMsg()+"+1个鸡蛋";
}
@Override
public int getPrice() {
return super.getPrice()+1;
}
}
创建一个加1根香肠的类
package my.decorate.shouzhuabingDemo.v1;
/**再创建一个既加鸡蛋又加香肠的BattercakeWithEggAndSausage类:
* @Description
* @Author fanjia
* @Version V1.0.0
* @Since 1.0
* @Date 2020/6/11
*/
public class BattercakeWithEggAndSausage extends BattercakeWithEgg {
@Override
protected String getMsg(){ return super.getMsg() + "+1根香肠";}
@Override
//加一个香肠加 2 块钱
public int getPrice(){ return super.getPrice() + 2;}
}
最后生产手抓饼并交对方买单
package my.decorate.shouzhuabingDemo.v1;
public class Test {
public static void main(String[] args) {
Battercake battercake = new Battercake();
System.out.println(battercake.getMsg() + ",总价:" + battercake.getPrice());
BattercakeWithEgg battercakeWithEgg = new BattercakeWithEgg();
System.out.println(battercakeWithEgg.getMsg() + ",总价:" + battercakeWithEgg.getPrice());
BattercakeWithEggAndSausage battercakeWithEggAndSauage = new BattercakeWithEggAndSausage();
System.out.println(battercakeWithEggAndSauage.getMsg() + ",总价:" + battercakeWithEggAndSauage.getPrice());
}
}
附上uml图
1.4.1.1思考菜鸟实现法继承会面临的问题
手抓饼会有很多配菜.怎么点(组装) 是顾客决定的.那么 我们怎么做到灵活的给客户生产手抓饼呢?? 如果我需要一个加2个鸡蛋加1根香肠的煎饼 那么用我们现在的类结构是创建不出来的,也无法自动计算出价格,除非再创建一个类做定制。如果需求再变,一直加定制
解决方案:采用装饰器模式
1.4.2老鸟写法:装饰器模式登场
1.将煎饼类抽象化
/**煎饼的抽象类
* @Description
* @Author fanjia
* @Version V1.0.0
* @Since 1.0
* @Date 2020/6/11
*/
public abstract class Battercake {
protected abstract String getMsg();
protected abstract int getPrice();
}
2.创建一个基本的煎饼(或者叫基础套餐)BaseBattercake:继承Battercake
/**创建一个基本的煎饼(或者叫基础套餐)BaseBattercake:
* @Description
* @Author fanjia
* @Version V1.0.0
* @Since 1.0
* @Date 2020/6/11
*/
public class BaseBattercake extends Battercake {
@Override
protected String getMsg(){ return "煎饼";}
@Override
public int getPrice(){ return 5;}
}
- 创建一个扩展套餐的抽象装饰器BattercakeDecotator类 继承Battercake
/** 创建一个扩展套餐的抽象装饰器BattercakeDecotator类:
* @Description
* @Author fanjia
* @Version V1.0.0
* @Since 1.0
* @Date 2020/6/11
*/
public class BattercakeDecotator extends Battercake
{
//静态代理,委派
//(指定抽象类 而不指定BaseBattercake 实体是为了灵活,
//因为可能以后会有多个类似BaseBattercake 继承了Battercake 的实现类)
private Battercake battercake;
public BattercakeDecotator(Battercake battercake) {
this.battercake = battercake;
}
@Override
protected String getMsg() {
return this.battercake.getMsg();
}
@Override
protected int getPrice() {
return this.battercake.getPrice();
}
}
4.鸡蛋装饰器EggDecorator类
package my.decorate.shouzhuabingDemo.v2Decorate;
/** 鸡蛋装饰器EggDecorator类
* @Description
* @Author fanjia
* @Version V1.0.0
* @Since 1.0
* @Date 2020/6/11
*/
public class EggDecorator extends BattercakeDecotator {
public EggDecorator(Battercake battercake) {
super(battercake);
}
@Override
protected String getMsg() {
return super.getMsg()+ "1个鸡蛋";
}
@Override
protected int getPrice() {
return super.getPrice()+1;
}
}
5.香肠类
package my.decorate.shouzhuabingDemo.v2Decorate;
/**
* @Description
* @Author fanjia
* @Version V1.0.0
* @Since 1.0
* @Date 2020/6/11
*/
public class SausageDecorator extends BattercakeDecotator {
public SausageDecorator(Battercake battercake) {
super(battercake);
}
@Override
protected String getMsg() {
return super.getMsg()+ "1根香肠";
}
@Override
protected int getPrice() {
return super.getPrice()+2;
}
}
最后生产手抓饼并交对方买单
package my.decorate.shouzhuabingDemo.v2Decorate;
import java.io.InputStream;
/**
* @Description
* @Author fanjia
* @Version V1.0.0
* @Since 1.0
* @Date 2020/6/11
*/
public class Test {
public static void main(String[] args){
Battercake battercake = new BaseBattercake();
//煎饼有点小,想再加一个鸡蛋
battercake = new EggDecorator(battercake);
//再加一个鸡蛋
battercake = new EggDecorator(battercake);
//很饿,再加根香肠
battercake = new SausageDecorator(battercake);
System.out.println(battercake.getMsg() + ",总价" + battercake.getPrice());
}
}
以上就完美实现了灵活加菜加量的功能
场景二:
最近老板瞟了 觉得自己手抓饼生意好.要求大家买手抓饼必须买手抓饼基础套餐(煎饼+可乐) 价格10元 否则不卖
我们只需
创建个必须套餐类 MustBattercake 继承Battercake 重写里面的getMsg
package my.decorate.shouzhuabingDemo.v2Decorate;
/**
* @Description
* @Author fanjia
* @Version V1.0.0
* @Since 1.0
* @Date 2020/6/11
*/
public class MustBattercake extends Battercake {
@Override
protected String getMsg() {
return "煎饼+可乐";
}
@Override
protected int getPrice() {
return 10;
}
}
然后test代码
只需要将
换成即可
Battercake battercake = new MustBattercake();
package my.decorate.shouzhuabingDemo.v2Decorate;
import java.io.InputStream;
/**
* @Description
* @Author fanjia
* @Version V1.0.0
* @Since 1.0
* @Date 2020/6/11
*/
public class Test {
public static void main(String[] args){
Battercake battercake = new MustBattercake();
//煎饼有点小,想再加一个鸡蛋
battercake = new EggDecorator(battercake);
//再加一个鸡蛋
battercake = new EggDecorator(battercake);
//很饿,再加根香肠
battercake = new SausageDecorator(battercake);
System.out.println(battercake.getMsg() + ",总价" + battercake.getPrice());
}
}
类图
是不是感觉灵活了很多 符合了开闭原则不需要去修改原来的代码了?
1.5 装饰模式的优缺点
优点:
1、装饰器是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地给一个对象扩展功能,即插即用。
2、通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果。
3、装饰器完全遵守开闭原则。
缺点:
1、会出现更多的代码,更多的类,增加程序复杂性。
2、动态装饰时,多层装饰时会更复杂。追踪代码更难看点