您的位置:首页 > 编程语言 > Java开发

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] view
plaincopy

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里面的标准的装饰模式的类图表示:

仔细看看上面的几个图后我们肯定就会理解这句话了:装饰模式是怎么达到不仅类的数目大减少了,性能的重复也可以减至到最少。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: