zoukankan      html  css  js  c++  java
  • 装饰模式--Decorator Pattern

      模拟场景:张三今天有个约会,但是他很苦恼,因为他在纠结今天不知道要穿什么衣服去约会。假如张三现在身上只穿着内衣裤。那他在挑选衣服进行装饰自己的时候,要怎么实现呢?

      分析:不用说,肯定会有一个Person类。现在我们用最简单的方式来完成。

    Person

     1 package com.zqz.dp.decorator;
     2 /**
     3  * @author Qin person类,来保存张三的属性
     4  */
     5 public class Person {
     6     private String name; // person name
     7     public Person(String name) { // 构造方法赋值
     8         this.name = name;
     9     }
    10     public void show() { // 展示方法
    11         System.out.print(" 装扮的" + this.name);
    12     }
    13     public void wearA(){
    14         System.out.print("A服饰 ");
    15     }
    16     public void wearB(){
    17         System.out.print("B裤衩 ");
    18     }
    19 }

    Client

     1 package com.zqz.dp.decorator;
     2 /**
     3  * @author Qin
     4  * 场景类
     5  */
     6 public class Client {
     7     public static void main(String[] args) {
     8         Person per=new Person("张三");
     9         System.out.println("------第一种装扮-------");
    10         per.wearA();    //张三装饰自己
    11         per.wearB();    //张三装饰自己
    12         per.show();    //张三展示自己
    13     }
    14 }

      以上确实完成了功能,但是耦合度很高。现在假设张三在去约会之前要先打电话给MM,跟她更改会面的时候,要怎么做呢?

      很明显,以现在的模式的话肯定要修改Person类和Client端。每次添加一个操作就要修改Person,明显违背了开闭原则,要记住,在开发中,允许对类进行拓展,但是对修改时关闭的。当然所有类要做到全无修改是不实际的。但是要让类的修改达到最少。

      先考虑下,我们现在是要对类进行装饰,也就是说要在类前后进行一些操作。咦,这不是可以用静态代理来实现吗?

    其实装饰模式就是代理模式的一个扩展,只是代理和装饰两者负责的范畴是不一样的。简单的说,代理模式专注的是对对象访问的控制AOP中添加权限处理的操作就是进行控制,只有有权限的才可以进行执行,没有权限的无法执行。而装饰模式专注的是为对象进行装饰,也就是说为对象添加逻辑。

      其实现在就涉及了一个问题:添加逻辑的方法

      也就是说现在需要对某个类添加一些操作,那该如何做呢?有这么几种方法:

      1、  在原来的代码中补充,违背了开闭原则,如上面的操作(不建议使用)。

      2、  继承原来的类,在类执行之前添加操作。这就是我们要说的装饰模式

      3、  使用接口,和继承是一样的道理,不过接口实现会方便点。

      现在我们修改下需求,现在张三正在哼着歌,准备要去约会。突然间他发现自己的装扮有点乱,还发现要去约会的地点人满为患;所以这个时候张三先去打扮下,出门后给MM打电话更改约会地点。要如何进行装饰呢?

      既然装饰是代理的一种体现,那在这里也不废话了,只是这个用的是继承的操作。

    Person

     1 package com.zqz.dp.decorator;
     2 /**
     3  * @author Qin
     4  * person类,来保存张三的属性
     5  */
     6 public class Person {
     7     @SuppressWarnings("unused")
     8     private String name; // person name
     9     public Person() {
    10     }; // 提供无参构造器
    11     public Person(String name) { // 构造方法赋值
    12         this.name = name;
    13     }
    14     public void show() { // 展示方法
    15         System.out.println("我要去约会。。。");
    16     }
    17 }

    Decorator

     1 package com.zqz.dp.decorator;
     2 /**
     3  * @author Qin
     4  * 装饰类,因为要对对象进行装饰,所以需要传递对象的引用,而且调用对象的具体方法
     5  */
     6 public abstract class Decorator extends Person {
     7     private Person per;        //person对象
     8     public Decorator(Person per) {        //构造方法表示对哪个对象进行装饰
     9         this.per = per;
    10     }
    11     @Override
    12     public void show() {
    13         if(per!=null){
    14             per.show();
    15         }
    16     }
    17 }

    WearDecorator

     1 package com.zqz.dp.decorator;
     2 /**
     3  * @author Qin
     4  * 第一个装饰类,继承自装饰类。
     5  */
     6 public class WearDecorator extends Decorator {
     7     public WearDecorator(Person per) {    //指明被装饰的类
     8         super(per);    //调用父类的操作
     9     }
    10     @Override
    11     public void show() {    //覆写父类方法
    12         this.ware();    //出门前进行操作
    13         super.show();    //调用父类方法
    14     }
    15     public void ware(){        //出门前进行打扮操作
    16         System.out.println("打扮中。。。。");
    17     }
    18 }

    CallDecorator

     1 package com.zqz.dp.decorator;
     2 /**
     3  * @author Qin
     4  * 第一个装饰类,继承自装饰类。
     5  */
     6 public class CallDecorator extends Decorator {
     7     public CallDecorator(Person per) {    //指明被装饰的类
     8         super(per);    //调用父类的操作
     9     }
    10     @Override
    11     public void show() {    //覆写父类方法
    12         super.show();    //调用父类方法
    13         this.call();    //出门后添加逻辑
    14     }
    15     public void call(){        //出门口打电话
    16         System.out.println("打电话改见面地点。。。");
    17     }
    18 }

    Client

     1 package com.zqz.dp.decorator;
     2 /**
     3  * @author Qin
     4  * 场景类
     5  */
     6 public class Client {
     7     public static void main(String[] args) {
     8         Person per=new Person("张三");        //实例化person对象
     9         System.out.println("------第一种装扮-------");
    10         Decorator  wear=new WearDecorator(per);    //用装饰类1对person对象进行装饰
    11         Decorator  call=new CallDecorator(wear);    //用装饰类2对装饰类1对象进行装饰,递归
    12         call.show();      //调用装饰类中的方法
    13     }
    14 }

      装饰模式的定义:动态的给一个对象添加一些额外的逻辑操作。

      装饰模式有如下四个角色:

      1、  Component抽象构件:相当于所有的person都应该有一个父类,这里因为只有张三一个对象,所有被我省略了。该类一般为抽象类或接口。

      2、  ConcreteComponent:具体构件,相当于上面的person。

      3、  Decorator装饰角色:装饰对象的统一进口,一般为抽象类,但方法不一定为抽象方法。

      4、  ConcreteDecorator具体装饰角色:表示具体的装饰类,CallDecorator等。

    装饰模式的优点:

      1、  装饰类与被装饰类可以独立发展,相互不耦合。

      2、  装饰对象可拓展实现类的功能。(目的)

    装饰模式的缺点:

      如果存在多重装饰的话,就会变得非常复杂。一般尽量减少装饰的量。

    装饰模式的使用场景:

      1、  需要对一个类的功能进行拓展。

      2、  需要动态的给一个对象添加功能,并动态的撤销。

      3、  需要对一批兄弟类进行添加功能。

    装饰模式与代理模式的异同:

      其实装饰模式就是静态代理的一种体现,但不能把代理和装饰混为一谈。

      在装饰模式中,我们都需要显示的指明我们要进行装饰的对象,这和静态代理是很相似的,但是代理模式的功能是实现对对象访问的控制,而装饰模式主要是对实现类功能的添加。

      还有的是,代理类中注重的是对对象访问的控制,在client端,一般我们只需要知道代理类的存在即可,不需要知道实现类的存在,也就是说一般使用的是普通代理或者虚拟代理,代理中最重点的是动态代理,实际开发中很少使用静态代理。

      在jdk中,代理模式使用的特别多,如动态代理Proxy。而装饰模式使用的较少,向java.io包中使用的就是装饰模式。

    InputStream in=new DataInputStream(new FileInputStream("text.txt"));

      举两个例子来说明分别使用代理和装饰的情况

      1、西游记中,孙悟空和唐僧路过高老庄时,悟空代替了高家三小姐去见八戒,这个时候八戒并不知道来的“高家三小姐”是悟空,而这个时候如果八戒只是对说说话,牵牵手,那么“高家三小姐”还是同意的,而如果八戒想亲她,那就不能让他得逞了。

      2、悟空学会了七十二变之后,如果他变成鱼,那么他就具有了游泳的能力;如果他变成了鸟,那他就具有了飞翔的能力;也就是说悟空变成了什么,就具有了什么的功能。但是不管悟空怎么变,他还是一只猴子,这是变不了的。

      相必大家应该都很清楚吧,第一个是代理,因为对对象的访问进行了控制;第二个是装饰,因为为对象添加了功能,进行了扩展

  • 相关阅读:
    老李推荐:第14章7节《MonkeyRunner源码剖析》 HierarchyViewer实现原理-装备ViewServer-获取版本号 2
    老李推荐:第14章7节《MonkeyRunner源码剖析》 HierarchyViewer实现原理-装备ViewServer-获取版本号 1
    老李推荐:第14章6节《MonkeyRunner源码剖析》 HierarchyViewer实现原理-装备ViewServer-启动ViewServer
    老李推荐:第14章5节《MonkeyRunner源码剖析》 HierarchyViewer实现原理-装备ViewServer-查询ViewServer运行状态
    老李推荐:第14章4节《MonkeyRunner源码剖析》 HierarchyViewer实现原理-装备ViewServer-端口转发 3
    老李推荐:第14章4节《MonkeyRunner源码剖析》 HierarchyViewer实现原理-装备ViewServer-端口转发 2
    mybatis-generator : 自动生成代码
    mybatis-generator XML Parser Error on line 38: 必须为元素类型 "table" 声明属性 "enableInsertByPrimaryKey"。
    server.properties 文件详解
    Java 生成 JNI 头文件
  • 原文地址:https://www.cnblogs.com/littleQin/p/3689114.html
Copyright © 2011-2022 走看看