JAVA设计模式(7) —<结构型>装饰模式(Decorator)
2015-10-16 18:09
901 查看
1 定义:
装饰模式(Decorator)Attach additional responsibilities to an object dynamically keeping the same interface. Decorators provide a flexible alternative to subclassing
for extending functionality.(保持接口不变的情况下,动态地给一个对象添加一些额外的职责。就增加功能来讲,装饰模式相比生成子类更为灵活。)
1.1 通用类图:
要变为更可理解的类图
Component:抽象构件(必须的),Component是一个接口或者是抽象类,定义我们最核心的对象,也就是最原始的对象可以给这些对象动态的添加职责。
ConcreteComponent:具体构件,是Component的接口或抽象类的实现,它是要被装饰的目标。
Decorator:装饰角色,一般是一个抽象类,用来实现Component,这里面不一定有抽象的方法,但在它的属性里必然有一个private变量指向Component抽象的构件。
ConcreteDecorator:具体的装饰类(可以有多个),通过具体装饰类来为具体构件添砖加料。
1.2 通用代码:
[java] viewplaincopy
public abstract class Component {
// 抽象的方法
public abstract void operate();
}
public class ConcreteComponent extends Component {
// 具体实现
@Override
public void operate() {
System.out.println("do Something");
}
}
public abstract class Decorator extends Component {
private Component component = null;
// 通过构造函数传递被修饰者
public Decorator(Component _component) {
this.component = _component;
}
// 委托给被修饰者执行
@Override
public void operate() {
this.component.operate();
}
}
public class ConcreteDecorator1 extends Decorator {
// 定义被修饰者
public ConcreteDecorator1(Component _component) {
super(_component);
}
// 定义自己的修饰方法
private void method1() {
System.out.println("method1 修饰");
}
// 重写父类的Operation方法
public void operate() {
this.method1();
super.operate();
}
}
public class ConcreteDecorator2 extends Decorator {
// 定义被修饰者
public ConcreteDecorator2(Component _component) {
super(_component);
}
// 定义自己的修饰方法
private void method2() {
System.out.println("method2修饰");
}
// 重写父类的Operation方法
public void operate() {
super.operate();
this.method2();
}
}
public class Client {
public static void main(String[] args) {
Component component = new ConcreteComponent();
// 第一次修饰
component = new ConcreteDecorator1(component);
// 第二次修饰
component = new ConcreteDecorator2(component);
// 修饰后运行
component.operate();
}
}
2 优点
2.1 装饰类与被装饰类可以独立发展,而不会相互耦合。2.2 装饰模式是继承关系的一个替代方案。查看Decorator类,不管装饰多少层,返回的对象还是Component,实现的还是is-a的关系。
2.3 装饰模式可以动态地扩展一个实现类的功能,其定义如此。
3 缺点
尽量减少装饰叠加类的数量,叠加次数越多,系统的复度越高。
4 应用场景
继承是静态的给类增加功能,而装饰是动态的增加功能;当业务变更,冒出新需求时,尤其是一个较大的需求时,可以考虑它。
4.1 需要扩展一个类的功能,或给一个类增加额外的功能;
4.2 需要动态地给一个对象增加功能,这些功能可以再动态地撤销;
4.3 需要为一批兄弟类进行改装或加装功能,首选装饰模式。
主要应用于这样一种场合,当你已经有了一个目标类,并且它是一个接口的实现类<或者抽象类的子类>,在对该类使用的后期发现需要对相应接口程序的前后做更多的处理,这些处理是变化的,不固定的,但是又不能去修改这个目标类,这时我们就可以使用这个装饰器模式:
5 注意事项
装饰者会导致设计中出现许多的小对象,如果过度使用,会让程序变得很复杂。
6 扩展
6.1 装饰者与被装饰者拥有共同的超类,继承的目的是继承类型,而不是行为。6.2 具体被装饰者类,可以定义初始的状态或者初始的自己的装饰,后面的装饰行为都在此基础上一步一步进行点缀、装饰。
6.3 装饰者模式的设计原则为:对扩展开放、对修改关闭,这句话体现在我如果想扩展被装饰者类的行为,无须修改装饰者抽象类,只需继承装饰者抽象类,实现额外的一些装饰或者叫行为即可对被装饰者进行包装。所以:扩展体现在继承、修改体现在子类中,而不是具体的抽象类,这充分体现了依赖倒置原则。
7 范例
7.1汉堡包
现在需要一个汉堡,主体是鸡腿堡,可以选择添加生菜、酱、辣椒等等许多其他的配料,这种情况下就可以使用装饰者模式。汉堡基类(被装饰者)
[java] view
plaincopyprint?
package decorator;
public abstract class Humburger {
protected String name ;
public String getName(){
return name;
}
public abstract double getPrice();
}
鸡腿堡类(被装饰者的初始状态,有些自己的简单装饰,相当于上面的Person)
[java] view
plaincopyprint?
package decorator;
public class ChickenBurger extends Humburger {
public ChickenBurger(){
name = "鸡腿堡";
}
@Override
public double getPrice() {
return 10;
}
}
配料的基类(装饰者,用来对汉堡进行多层装饰,每层装饰增加一些配料,相当于上面Decorator)
[java] view
plaincopyprint?
package decorator;
public abstract class Condiment extends Humburger {
public abstract String getName();
}
生菜(装饰者的第一层,相当于上面的decorator_zero)
[java] view
plaincopyprint?
package decorator;
public class Lettuce extends Condiment {
Humburger humburger;
public Lettuce(Humburger humburger){
this.humburger = humburger;
}
@Override
public String getName() {
return humburger.getName()+" 加生菜";
}
@Override
public double getPrice() {
return humburger.getPrice()+1.5;
}
}
辣椒(装饰者的第二层,相当于上面的decorator_first)
[java] view
plaincopyprint?
package decorator;
public class Chilli extends Condiment {
Humburger humburger;
public Chilli(Humburger humburger){
this.humburger = humburger;
}
@Override
public String getName() {
return humburger.getName()+" 加辣椒";
}
@Override
public double getPrice() {
return humburger.getPrice(); //辣椒是免费的哦
}
}
测试类
[java] view
plaincopyprint?
package decorator;
public class Test {
/**
* @param args
*/
public static void main(String[] args) {
Humburger humburger = new ChickenBurger();
System.out.println(humburger.getName()+" 价钱:"+humburger.getPrice());
Lettuce lettuce = new Lettuce(humburger);
System.out.println(lettuce.getName()+" 价钱:"+lettuce.getPrice());
Chilli chilli = new Chilli(humburger);
System.out.println(chilli.getName()+" 价钱:"+chilli.getPrice());
Chilli chilli2 = new Chilli(lettuce);
System.out.println(chilli2.getName()+" 价钱:"+chilli2.getPrice());
}
}
输出
[java] view
plaincopyprint?
鸡腿堡 价钱:10.0
鸡腿堡 加生菜 价钱:11.5
鸡腿堡 加辣椒 价钱:10.0
鸡腿堡 加生菜 加辣椒 价钱:11.5
7.2 打印字符
[java] view
plaincopy
//Component抽象构件
public interface TextConsole {
public void printWords();
public void readWords();
}
//ConcreteComponent:具体构件
public class MyText implements TextConsole {
private String words;
public void printWords() {
System.out.print(words);
}
public void readWords() {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
try{
System.out.print("please enter some word to display:");
words = br.readLine();
}catch(IOException e){
e.printStackTrace();
words = "There are some errors occured during your entering!";
}
}
}
//Decorator:装饰角色
public class DecoratorTextConsole implements TextConsole {
private TextConsole text;
public DecoratorTextConsole(TextConsole text){
this.text = text;
}
public void printWords() {
text.printWords();
}
public void readWords() {
text.readWords();
}
}
//ConcreteDecorator:具体的装饰类AsteriskTextConsole
public class AsteriskTextConsole extends DecoratorTextConsole {
public AsteriskTextConsole(TextConsole text){
super(text);
}
public void printWords(){
addAsterisk();
super.printWords();
addAsterisk();
}
private void addAsterisk(){
System.out.print("*");
}
}
//ConcreteDecorator:具体的装饰类PlusTextConsole
public class PlusTextConsole extends DecoratorTextConsole {
public PlusTextConsole(TextConsole text){
super(text);
}
public void printWords(){
addPlus();
super.printWords();
addPlus();
}
private void addPlus(){
System.out.print("+");
}
}
//测试类
public class TestMain {
public static void main(String[] args){
TextConsole mt = new MyText(); // mt 实例在整个被调用的过程中都不会改变,但是它的其他装饰器类却是动态生成的,而且是可自由“拆卸”的,这样就比单一的继承灵活的多。
mt.readWords();
mt.printWords();
System.out.println();
String commond = new String();
System.out.println("please enter commond: (add ?(-plus | -astr | -all) | exit");
while(!commond.equals("exit")){
try{
commond = new BufferedReader(new InputStreamReader(System.in)).readLine();
}catch(IOException e){
e.printStackTrace();
commond = "exit";
}
if(commond.equals("add -plus")){
TextConsole ptc = new PlusTextConsole(mt);
ptc.printWords();
System.out.println();
}else if(commond.equals("add -astr")){
TextConsole atc = new AsteriskTextConsole(mt);
atc.printWords();
System.out.println();
}else if(commond.equals("add -all")){
TextConsole tc = new AsteriskTextConsole(new PlusTextConsole(mt));
tc.printWords();
System.out.println();
}else{
mt.printWords();
System.out.println();
}
}
}
}
7.3 咖啡店饮料配置扩展说明装饰模式
为什么不用继承而用装饰模式我们知道Java I/O库需要很多性能的各种组合,如果说这些性能的组合是通过继承方式来实现的话,那么每一种组合都需要一个类,这样就会出现大量重复性问题的出现,从而 使类数目“爆炸”。而如果采用装饰模式,那么不仅类的数目大大减少,性能的重复也可以减至到最少。所以装饰模式是Java
I/O库的基本模式。在这里我想再用<<Head First Design Pattern>>中讲到装饰模式时候的一个例子,看看装饰模式是怎么达到不仅类的数目大减少了,性能的重复也可以减至到最少:
它这个例子大概是说:Beverage是一个抽象类,它被所有在一个咖啡店里卖的饮料继承。Beverage有个抽象方法cost,所有的子类都 要实现这个抽象方法,计算它们的价格。现在有四个最基本的咖啡:HouseBlend,DarkRoast,Decaf,Espresso他们都继承自 Beverage,现在的需求是说在四个最基本的咖啡里,每个都可以随便地添加调味品,像steamed milk,soy,还有mocha最后是加上whipped
milk。如果是说按继承来实现这种几个调味品跟原来咖啡的组合的话,我们会很自然地设计来下面的类图来:
看到了上面的类图了吗,我们不禁会说这就是“类爆炸”。如果是按装饰模式的设计思路我们可以得出下面的设计类图:
我们再来看看Gof里面的标准的装饰模式的类图表示:
仔细看看上面的几个图后我们肯定就会理解这句话了:装饰模式是怎么达到不仅类的数目大减少了,性能的重复也可以减至到最少。
相关文章推荐
- springmvc + shiro 配置(一、结构及配置文件)
- Java对象自我救赎机制
- java
- Java String 中 “==” 与 equals()方法的区别
- Spring3系列9- Spring AOP——Advice
- Spring security oauth2最简单入门环境搭建--二、干货
- POJO和javabean的异同
- java调用lpsolve的配置
- javaweb开发错误笔记总结
- 在Spring环境中建立JPA Spring JPA 提供了三种方法创建JPA EntityManagerFactory:
- java 获取字符串安全截取长度
- spring mvc 国际化
- java中的多线程
- Spring全局事务之WebLogicJtaTransactionManager
- JPA EntityManager的四个主要方法 ——persist,merge,refresh和remove
- java《类与对象》课后作业
- 用TransactionProxyFactoryBean实现spring的声明式事务
- Java反射工具包
- 演示java内存泄漏(通过HashSet实现)
- eclipse定制调试透视图(perspective)