设计模式之装饰模式
一、什么是装饰模式
概念:动态的给一个对象添加一些额外的功能。就增加功能来说,装饰模式比生成自子类更加的灵活。
举个例子,笔者自己以前喜欢吃手抓饼。每天都要去十字路口大爷那里买手抓饼,但是大爷卖的手抓饼有大份,小份等等,还有各种口味的配料添加。比如我比较喜欢变态辣,喜欢加火腿,喜欢加鸡蛋,所以每次去买手抓饼都会在最基础的手抓饼上加上"装饰",让他变成豪华版手抓饼,这其实就是装饰模式的一个体现。
二、装饰模式详解
1、手抓饼模式
手抓饼其实就是一个组件(Component),之后会有很多的装饰类(Decorate)对它进行装饰,比如加鸡蛋,变态辣等等。
Cookie类,组件接口,仅仅包含一个售价方法,cost()
public interface Cookie { public double cost(); }
BigCookie类,大份手抓饼,具体的手抓饼实现类
public class BigCookie implements Cookie { @Override public double cost() { return 6.0; } }
BatchCookie类,装饰的抽象类,此处演示并没有将它设为抽象
public class BatchCookie implements Cookie { private Cookie cookie; public BatchCookie(Cookie cookie){ this.cookie = cookie; } @Override public double cost() { return cookie.cost(); } }
AddEgg类,AddHam类都是继承了BatchCookie类,为具体的装饰方法,加鸡蛋加两元,加火腿加一块
public class AddEgg extends BatchCookie { public AddEgg(Cookie cookie) { super(cookie); } @Override public double cost() { return super.cost() + 2; } }
public class AddHam extends BatchCookie { public AddHam(Cookie cookie) { super(cookie); } @Override public double cost() { // TODO Auto-generated method stub return super.cost() + 1; } }
客户端代码
public class Show2 { public static void main(String[] args) { Cookie cookie = new BigCookie(); cookie = new AddEgg(cookie); cookie = new AddHam(cookie); double money = cookie.cost(); System.out.println("大份手抓饼加鸡蛋加火腿一起" + money + "元"); } }
测试结果
大份手抓饼加鸡蛋加火腿一起9.0元
可以看到手抓饼可以加鸡蛋加火腿,手抓饼被装饰了,原本普通的手抓饼添加了新的功能,而这就是装饰模式的最直接的体现,接下来看看装饰模式的具体结果。
2、装饰模式具体结构
Component类,组件抽象类,被装饰对象的抽象类
public abstract class Component { public abstract void opreate(); }
ConcreteComponent类,具体的组件实现类,也可以对该对象进行动态的装饰,即添加新功能
public class ConcreteComponent extends Component { @Override public void opreate() { System.out.println("具体对象的操作"); } }
Decorate类,装饰的抽象类,也继承了Component对象
public class Decorate extends Component { private Component component; public Component getComponent() { return component; } //设置Component public void setComponent(Component component) { this.component = component; } //重写operate(),实际执行的是component的operate() @Override public void opreate() { if (component != null) { component.opreate(); } } }
ConcreteDecorateA类,具体的装饰类,实现了Decorate类,并提供了该装饰对象独特的装饰方法
public class ConcreteDecorateA extends Decorate { //本类独有的装饰功能,区别于ConcreteDecorateB private String addedState; public String getAddedState() { return addedState; } public void setAddedState(String addedState) { this.addedState = addedState; } @Override public void opreate() { super.opreate(); this.addedState = "New State"; System.out.println("具体装饰对象A的操作"); } }
ConcreteDecorateB类,也提供了该对象独特的装饰方法
public class ConcreteDecorateB extends Decorate { private void addedBehavior(){ System.out.println("装饰类B添加行为"); } @Override public void opreate() { super.opreate(); System.out.println("具体装饰对象B的操作"); addedBehavior(); } }
客户端代码
public class Show { //首先用ConcreteComponent实例对象component,然后用ConcreteDecorateA //对象d1来包装component,再用ConcreteDecorateB对象d2来包装d1,最后是用 //d2执行opreate() public static void main(String[] args) { ConcreteComponent component = new ConcreteComponent(); ConcreteDecorateA d1 = new ConcreteDecorateA(); ConcreteDecorateB d2 = new ConcreteDecorateB(); d1.setComponent(component); d2.setComponent(d1); d2.opreate(); } }
测试结果
具体对象的操作
具体装饰对象A的操作
具体装饰对象B的操作
装饰类B添加行为
上面就是装饰模式的节本构造,虽然不是很好懂,但是结合实际生活的场景就很容易理解。
3、UML图
三、总结
Component是定义一个对象接口,可以给这些对象动态的添加功能。ConcreteComponent是定义了一个具体的对象,也可以给这个对象添加一些功能。Decorate类,装饰抽象类,也继承了从外来类扩展Component的功能,但是对于Component来说,没有必要知道Decorate的存在。至于ConcreteDecorate就是具体的装饰对象,起到了给Component添加功能的作用。
装饰模式是为已有的功能动态的添加更多功能的一种方式。当系统需要添加新功能的时候,是向旧的类中添加新代码,这是设计之初的想法,但是严重违反了开放-封闭原则,这些新加的功能严重和原有的类耦合在一起了,因为新加入的功仅仅是为了满足一些只在某种特定情况下才会执行的特殊行为的需要。但是装饰模式提供了一个非常好的解决方案,它把每个要装饰的功能放在单独的类中,并让这个类包装它所要装饰的对象,因此,当需要执行特殊行为时,客户端代码就可以在运行时根据需要有选择的、按顺序的使用装饰功能的包装对象。