被说了很多遍的设计模式---装饰模式

    xiaoxiao2021-12-10  26

    [把你的理性思维慢慢变成条件反射]

    本文,我们讲介绍装饰模式,文章主题结构与上位一致。惯例,先来看看我们示例工程的环境:

    操作系统:win7 x64

    其他软件:eclipse mars,jdk7

    -------------------------------------------------------------------------------------------------------------------------------------

    经典问题:

    人们的服装搭配问题、家庭装修问题、客户定制化的功能问题等。本文以“人们的服装搭配问题”为例进行说明。

    思路分析:

    要点一:以人为核心。

    要点二:服装搭配不改变核心。

    要点三:搭配按需变化,具有不确定性。

    示例工程:

    错误写法(1):

    创建Person.java文件,具体内容如下:

    package com.csdn.ingo.gof_Decorator.base; public class Person { private String name; public Person(String name){ this.name = name; } public void wearTShirts(){ System.out.println("大T恤"); } public void wearBigTrouse(){ System.out.println("垮裤"); } public void wearSneakers(){ System.out.println("球鞋"); } public void wearSuit(){ System.out.println("西装"); } public void wearTie(){ System.out.println("领带"); } public void wearLeatherShoes(){ System.out.println("皮鞋"); } public void Show(){ System.out.println("装扮后的"+name); } } 创建Window.java文件,具体内容如下:

    package com.csdn.ingo.gof_Decorator.base; public class Window { public static void main(String[] args) { Person ingo = new Person("Ingo"); System.out.println("第2种装扮:"); ingo.wearTShirts(); ingo.wearBigTrouse(); ingo.wearSneakers(); ingo.Show(); System.out.println("第2种装扮:"); ingo.wearSuit(); ingo.wearTie(); ingo.wearLeatherShoes(); ingo.Show(); } }

    错误原因:

    虽然代码工程正确,但违反了我们前文介绍的“单一职责原则”:Person的创建与装饰功能混合在一起。违反“开闭原则”:由于装饰的功能经常处于变动状态,因此,对于某个功能的修改可能扩展到其他装饰功能当中。故,base包下的这种写法是错误的。

    错误写法(2):

    创建Finery.java文件,具体内容如下:

    package com.csdn.ingo.gof_Decorator.one; public abstract class Finery { public abstract void show(); } 创建BigTrouser.java,LeatherShoes.java,Sneakers.java,Suit.java,Tie.java,TShirts.java文件,在此仅列举BigTrouser.java文件,具体内容如下:

    package com.csdn.ingo.gof_Decorator.one; public class BIgTrouser extends Finery { @Override public void show() { System.out.println("垮裤"); } } 创建Person.java文件,具体内容如下:

    package com.csdn.ingo.gof_Decorator.one; public class Person { private String name; public Person(String name){ this.name = name; } public void Show(){ System.out.println("装扮的"+name); } } 创建Window.java,具体内容如下:

    package com.csdn.ingo.gof_Decorator.one; public class Window { public static void main(String[] args) { Person ingo = new Person("Ingo"); System.out.println("第1种装扮:"); Finery tshirts = new TShirts(); Finery bigt = new BIgTrouser(); Finery sneaker = new Sneakers(); tshirts.show(); bigt.show(); sneaker.show(); ingo.Show(); System.out.println("第2种装扮:"); Finery suit = new Suit(); Finery tie = new Tie(); Finery lea = new LeatherShoes(); suit.show(); tie.show(); lea.show(); ingo.Show(); } }

    错误原因:

    这种写法将装饰的内容一个一个的都列举了出来,这种做法将导致客户端产生非常多的对象声明,方法调用等,正如上文展示的那样。并且导致后期维护成本的急剧上升。因此,这种采用继承方式的设计也是错误的。

    推荐写法:

    创建Finery.java文件,具体内容如下:

    package com.csdn.ingo.gof_Decorator.three; public class Finery extends Person { protected Person component; public void Decorate(Person component){ this.component = component; } @Override public void show(){ if(component!=null){ component.show(); } } } 创建BigTrouser.java,LeatherShoes.java,Sneakers.java,Suit.java,Tie.java,TShirts.java文件,在此仅列举BigTrouser.java文件,具体内容如下:

    package com.csdn.ingo.gof_Decorator.three; public class BIgTrouser extends Finery { @Override public void show() { System.out.println("垮裤"); super.show(); } } 创建Window.java,具体内容如下:

    package com.csdn.ingo.gof_Decorator.three; public class Window { public static void main(String[] args) { Person ingo = new Person("Ingo"); System.out.println("第1种装扮:"); TShirts tshirts = new TShirts(); BIgTrouser bigt = new BIgTrouser(); Sneakers sneaker = new Sneakers(); tshirts.Decorate(ingo); bigt.Decorate(tshirts); sneaker.Decorate(bigt); sneaker.show(); System.out.println("第2种装扮:"); Suit suit = new Suit(); Tie tie = new Tie(); LeatherShoes lea = new LeatherShoes(); suit.Decorate(ingo); tie.Decorate(suit); lea.Decorate(tie); lea.show(); } }

    推荐原因:

    符合“单一职责原则”,“开闭原则”等每个装饰功能单元均是独立的,达到易扩展,易维护的目的。对客户端而言,当扩展新的功能节点时,可以方便的增加相关功能。并且减少了测试的成本。

    模式总结:

    本例UML结构图:

    装饰模式标准UML图

    概念总结:

    装饰模式:动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。

    组成部分:Component(抽象部件),ConcreteComponent(具体部件),Decorator(抽象装饰),ConcreteDecorator(具体装饰)四部分组成。

    特别提醒:

    具体构件类和装饰类都实现了相同的抽象构件接口,因此装饰模式以对客户透明的方式动态地给一个对象附加上更多的功能结构中所有对象均实现Operation()方法。Decorator中维护了一个Component的引用。

    模板代码:

    Component.java文件:

    package com.csdn.ingo.gof_Decorator.two; public abstract class Component { public abstract void Operation(); } ConcreteComponent.java文件:

    package com.csdn.ingo.gof_Decorator.two; public class ConcreteComponent extends Component { @Override public void Operation() { System.out.println("具体对象的操作"); } } ConcreteDecoratorA.java文件:

    package com.csdn.ingo.gof_Decorator.two; public class ConcreteDecoratorA extends Decorator { private String addedState;//本类独有,以区别于B @Override public void Operation() { super.Operation(); addedState = "New State"; System.out.println("具体装饰对象A的操作"); } } package com.csdn.ingo.gof_Decorator.two; public class ConcreteDecoratorB extends Decorator { @Override public void Operation() { super.Operation(); AddedBehavior(); System.out.println("具体装饰对象A的操作"); } private void AddedBehavior() { //本类独有,以区别于A } } Decorator.java文件:

    package com.csdn.ingo.gof_Decorator.two; public abstract class Decorator extends Component { protected Component component; public void setComponent(Component component){ this.component = component; } @Override public void Operation() { if(component!=null){ component.Operation();//调用原有业务操作 } } }

      在抽象装饰类Decorator中定义了一个Component类型的对象component,维持一个对抽象构件对象的引用,并可以通过构造方法或Setter方法将一个Component类型的对象注入进来,同时由于Decorator类实现了抽象构件Component接口,因此需要实现在其中声明的业务方法operation(),需要注意的是在Decorator中并未真正实现operation()方法,而只是调用原有component对象的operation()方法,它没有真正实施装饰,而是提供一个统一的接口,将具体装饰过程交给子类完成。

    Window.java文件: package com.csdn.ingo.gof_Decorator.two; public class Window { public static void main(String[] args) { ConcreteComponent c = new ConcreteComponent(); ConcreteDecoratorA d1 = new ConcreteDecoratorA(); ConcreteDecoratorB d2 = new ConcreteDecoratorB(); d1.setComponent(c); d2.setComponent(d1); d2.Operation(); } }

    问题:为什么模板代码与示例代码结构不同?

    答:当只有一个ConcreteComponent类,而没有抽象的Component类时,那么Decorator类可以是ConcreteComponent的一个子类。同理,如果只有一个ConcreteDecorator类,那么就没有必要建立一个独立的Decorator类,而可以把Decorator和ConcreteDecorator合并为一个类。因此,在本例中服饰类直接作为人的子类。

    扩展问题:“交通工具也是隐含装饰品”

    在上文的例子“服装搭配”中,所有的具体子类都是需要最终显示出来的。但是,有些“隐含的装饰品”仅仅存在在装饰的过程当中,最终结果并不会体现出来,如本文的,ingo通过一系列的装饰之后,乘坐各种交通工具前往约定地点的过程,就仅仅在“过程中”出现,到达之后就会消失。换句话说,某些操作仅仅存在过程中,并且,该操作与其他装饰步骤无任何调用耦合关系。类似的场景还有,单据的审批过程,在最终的审批单中并不会显示出审批过程一样。等等类似的场景。

    扩展问题的UML结构:

    解决步骤:

    对于“隐含的装饰品”需要使用ConcreteDecorator来进行。即,客户端需要显示声明一个具体的“交通工具”对象来进行装饰,之后再将对象交还给装饰流程。如此的好处是给系统设计带来了更多的灵活性。

    特别注意:

    这里的“过程中存在”的概念,即过程只有一次,该装饰过程也只能发生一次,不可多次调用。

    反思:

    应用场景:

    在不影响其他对象的条件下,为已有功能动态地添加更多的功能,这些功能通常仍以原有的类作为核心职责或主要行为。

    当不能使用继承的方式作为扩展,或者,不适合使用继承的方式进行扩展与维护时。(原因:继承会产生大量的子类,由此产生管理问题。另外,某些类不能够内继承扩展)

    优点:

    对当前已有功能的进行扩展是,灵活性更好,不会产生更多的子类。可以通过动态的方式进行扩展,即使用配置文件,可以在运行时选择不同的装饰类。装饰的顺序可以自由组合,以产生更多功能的对象。分离核心功能与装饰功能,达到“单一职责原则”的要求。具体部件与具体装饰可以独立变化。新的修改不会影响原有类。

    缺点:

    客户端可能会产生很多小对象。可能会占用更多的资源,而影响性能。多种排列组合的灵活性,可能会导致产生不符合要求的对象。

    -------------------------------------------------------------------------------------------------------------------------------------

    至此,被说了很多遍的设计模式---装饰模式 结束

    参考资料:

    图书:《大话设计模式》

    其他博文:http://blog.csdn.NET/lovelion/article/details/7563445

    转载请注明原文地址: https://ju.6miu.com/read-700036.html

    最新回复(0)