zoukankan      html  css  js  c++  java
  • 设计模式总结篇系列:装饰器模式(Decorator)

    在面向对象设计过程中,经常会遇到需要对现有的类的功能进行扩展,通常我们可以采用继承的方式。例如老罗最近在做手机,一开始需要定义手机所应具有的功能:

    1 interface Phone{
    2     
    3     public void tel();
    4     
    5     public void sms();
    6     
    7 }

    在此,为简单起见,只是定义了接打电话和收发短信功能。

    然后,老罗开始造手机,经过两年艰苦努力,第一代手机T1终于面世了,很高兴的开了发布会,反响还不错。

     1 class T1 implements Phone{
     2 
     3     @Override
     4     public void tel() {
     5         System.out.println("可以实现基本的接打电话");
     6     }
     7 
     8     @Override
     9     public void sms() {
    10         System.out.println("可以实现基本的收发短信功能");
    11     }
    12     
    13 }

    T1面世了,当然,只有不短的追求才有更大的进步的,在进一步的努力后,新一代的T2可以在T1的基础上实现安装Android软件的功能了。通常情况下,我们可能进行如下定义:

    1 class T2 extends T1{
    2     
    3     public void installApk(){
    4         System.out.println("可以安装Android软件了");
    5     }
    6     
    7 }

    显然,采取了继承的方式,T2在T1的基础上新增了自己所具有的更多功能——可以安装Android软件。这是一个进步。

    又过了一段时间,老罗想出做手机不仅仅需要满足这些基本的功能,还应该最大程度上的最好用户体验,于是,目前最快的闪拍被想到了,此功能被加入到了最新一代的T3中。

    1 class T3 extends T2{
    2     
    3     public void fastestPhoto(){
    4         System.out.println("可以实现目前最快的闪拍");
    5     }
    6     
    7 }

    自然的,我们首先想到的还是通过继承的方式,在继承第2代基础上扩充了T3的功能。

    当然,这是一件相当值得庆贺的事情。现在T1、T2、T3都面向市场推出了。由于市场需求较大,每一代的手机都在不断的制造中。现在有一个新的需求出来了,电信的用户反馈,这么手机都不支持电信卡,怎么办?

    于是,老罗为了满足电信用户,急需在T2上推出相应的电信手机,那么现在怎么办呢?第一个想到的可能是修改T2类:

     1 class T2 extends T1 {
     2 
     3     public void installApk() {
     4         System.out.println("可以安装Android软件了");
     5     }
     6 
     7     @Override
     8     public void tel() {
     9         supportDx();
    10         System.out.println("可以实现基本的接打电话");
    11     }
    12 
    13     @Override
    14     public void sms() {
    15         supportDx();
    16         System.out.println("可以实现基本的收发短信功能");
    17     }
    18 
    19     public void supportDx() {
    20         System.out.println("可以支持电信用户了");
    21     }
    22 
    23 }

    现在问题就出来了,由于继承关系的存在,虽然表面上只是修改了T2类,实际上其所有的子类都间接的被修改了。那么现在又有两个选择,要么修改其所有子类,这肯定不实际,要么不修改T2类,以免带来不必要的T2修改后对其所有子类的影响,但为了满足需求,可能得继承T2类新定义一个新的类,这样会导致类的无线膨胀问题(因为这种需求是不可预估的),那么有没有什么好的解决方案呢?

    于是,基本包装/装饰而不是继承来实现此类场景的类的设计是一个不错的选择。由此带来的设计模式称之为"装饰模式"。

    为了给一个现有的类增加一些新的功能,而不引其原来类的修改,用装饰模式去代替继承模式,要求装饰类和被装饰类实现同一接口,装饰对象有被装饰对象的实例。

    那么我们具体看一下在上面场景中使用装饰模式如何设计类。

     1 class T2 implements Phone {
     2 
     3     private Phone phone;
     4 
     5     public T2(Phone phone) {
     6         this.phone = phone;
     7     }
     8 
     9     public void installApk() {
    10         System.out.println("可以安装Android软件了");
    11     }
    12 
    13     @Override
    14     public void tel() {
    15         phone.tel();
    16     }
    17 
    18     @Override
    19     public void sms() {
    20         phone.tel();
    21     }
    22 
    23 }

    T3类设计与之类似,那么现在T2需要支持电信用户呢?这是直接修改T2类即可,并且对其他类(因为它没有子类之说了)是没有影响的。

     1 class T2 implements Phone {
     2 
     3     private Phone phone;
     4 
     5     public T2(Phone phone) {
     6         this.phone = phone;
     7     }
     8 
     9     public void installApk() {
    10         System.out.println("可以安装Android软件了");
    11     }
    12 
    13     @Override
    14     public void tel() {
    15         this.supportDx();
    16         phone.tel();
    17     }
    18 
    19     @Override
    20     public void sms() {
    21         this.supportDx();
    22         phone.tel();
    23     }
    24 
    25     public void supportDx() {
    26         System.out.println("可以支持电信用户了");
    27     }
    28 
    29 }

    测试:

     1 public class DecoratorTest {
     2 
     3     public static void main(String[] args) {
     4         Phone t1 = new T1();
     5         Phone t2 = new T2(t1);
     6         t2.tel();
     7         t2.sms();
     8     }
     9 
    10 }

    怎么样,利用装饰模式设计类代替原本的继承是不是优势马上显示出来了,可能有人会说,这样设计会违背T2的愿意,可能有些T2本没必要支持电信用户,哦,确实如此,那好办啊,在创建的时候T2对象的时候加一个参数去控制就可以了啊。大概如下:

     1 class T2 implements Phone {
     2 
     3     private Phone phone;
     4     private boolean isSupportDx;
     5 
     6     public T2(Phone phone, boolean isSupportDx) {
     7         this.phone = phone;
     8         this.isSupportDx = isSupportDx;
     9     }
    10 
    11     public void installApk() {
    12         System.out.println("可以安装Android软件了");
    13     }
    14 
    15     @Override
    16     public void tel() {
    17         this.supportDx();
    18         phone.tel();
    19     }
    20 
    21     @Override
    22     public void sms() {
    23         this.supportDx();
    24         phone.tel();
    25     }
    26 
    27     public void supportDx() {
    28         if (isSupportDx) {
    29             System.out.println("可以支持电信用户了");
    30         }
    31     }
    32 
    33 }
     1 public class DecoratorTest {
     2 
     3     public static void main(String[] args) {
     4         Phone t1 = new T1();
     5         Phone t2 = new T2(t1, true); // Phone t2 = new T2(t1, false);
     6         t2.tel();
     7         t2.sms();
     8     }
     9 
    10 }

     怎么样?装饰模式还是不错的吧,Java IO中的包装流都是采用装饰模式实现的哦。。

  • 相关阅读:
    select详解
    java Map及Map.Entry详解
    Java 基本类型
    java 获取String出现最多次数的字段
    java 居民身份证的校验
    java 删除文件
    Java 导出excel进行换行
    获取文件及其文件路径
    List<Map<String,Object>> 中文排序
    Java ----单个list 删除元素
  • 原文地址:https://www.cnblogs.com/lwbqqyumidi/p/3750634.html
Copyright © 2011-2022 走看看