文章目录

  • 前言
  • 正文
    • 一、定义
    • 二、情景假设
    • 三、情景分析
    • 四、模式结构及分析
      • (一) 两种装饰模式
      • (二) 两种组合模式的总结
      • (三) 模式分析
        • (1) 模式特点
        • (2) 模式缺点
    • 五、使用情景
    • 六、延申及拓展
  • 总结

前言

文章内容主要参考了刘伟主编的《设计模式(第2版)》,同时也结合了自己的一些思考和理解,希望能帮到大家。


本篇讲解装饰模式,和组合模式一样分成两种类型,一个是透明装饰模式,一个是不透明装饰模式,仔细观察的人可能会发现好像两个设计模式有点相像,但是说实话两个思想却是相差很远,我个人觉得如果你是学会模式的内在思考方式,学完后你并不会混淆而说像。(因为这个相像其实一开始我没发现的,是老师说有同学学到这觉得很像,我仔细一看确实外貌挺像的,但是除了外貌就再无其他了。。。这也说明其实有些同学学的时候更多看在了外在的东西,没学到骨子里)。
最后学完,看大家自行去理解区分两个模式了,里面确实有很多相近的东西。

正文

一、定义

装饰模式(Decorator Pattern) :动态地给一个对象增加一些额外的职责,就增加对象功能来说,装饰模式比生成子类实现更为灵活,是一个对象结构型模式。

装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任,可以在不需要创建更多子类的情况下,让对象的功能得以扩展。是一种用于替代继承的技术,它通过一种无须定义子类的方式给对象动态增加职责,使用对象之间的关联关系取代类之间的继承关系,同时引入了装饰类,在装饰类中既可以调用待装饰的原有类的方法,还可以增加新的方法,以扩展原有类的功能。

二、情景假设

变形金刚在变形之前是一辆汽车,它可以在陆地上移动。当它变成机器人之后除了能够在陆地上移动之外,还可以说话;如果需要,它还可以变成飞机,除了在陆地上移动还可以在天空中飞翔。

情景大概是由汽车拓展成机器人和飞机时,该怎么处理?

三、情景分析

关于上面情景的类图(具体分析在下面)

首先我们很容易想到的是,既然要拓展嘛,那就先继承汽车拥有汽车的方法,然后再在继承类上加上机器人特定的方法,那就是机器人啦,加上汽车特有的方法,那就是汽车类啦。一切都很简单,但是论实际上,增加一种拓展方式,很可能增加非常多的是实现类,这将会让系统变得非常的庞大(树形的深度越来越深,互相耦合程度更加庞大)。其次可能还会牵扯到多继承的问题(因为有些类可能会继承多个需要拓展的),同时使用继承也是破化了类的封装性

所以装饰模式所采用的方法就是利用关联代替继承,这样一来降低了继承所导致的耦合度增大,同时,软件维护上,关联关系的松耦合性,使得系统更加的容易维护。树形的层数较少,在于横向的拓展,拓展起来更加方便,还能够很好的实现运行时动态拓展。

所以先定义抽象类

//抽象构件类
public abstract class Transform
{public abstract void move();
}

具体已有的汽车类

//汽车Car
public final class Car extends Transform{public void Car(){System.out.println("变形金刚是一辆车!");}public void move(){System.out.println("在陆地上移动!");}
}

细心的同学发现了,我还定义了final,声明着这不能继承拓展增加难度,这样其实我们就不得不利用关联的方式了。

定义一个装饰类,装饰类同样继承抽象类Transform,因为装饰类的本质我们不能忘,我们只是对原有存在的进行拓展,而对于客户端而言应当还是能当作之前的情况继续使用,为了实现统一的使用,实现同一的抽象类客户端便能够可以无视细节。在这个时候我们可以增加需要被拓展的类作为属性关联起来。

//装饰类Changer
public class Changer extends Transform{private Transform t ;public  void add(Transform t){this.t = t;}public void move(){t.move();//调用关联类自己的方法}
}

接下来就是机器人类和飞机类,继承装饰器类即可

public class Robot extends Changer{public Robot(Transform transform){super(transform);System.out.println("变成机器人!");}public void say(){System.out.println("说话!");}
}public class AirPlane extends Changer{public AirPlane(Transform transform){super(transform);System.out.println("变成飞机!");}public void fly(){System.out.println("在天空飞翔!");}
}

接下来是客户端的代码:

public class Client
{public static void main(String args[]){Transform camaro;camaro = new Car();camaro.move();Robot bumblebee = new Robot(camaro);bumblebee.move();bumblebee.say();}
}

可以看到,我们这里先定义了一个原先的Car类,之后我们再关联起来运用了新的“装饰”的方法。

四、模式结构及分析

(一) 两种装饰模式

两种装饰模式分别是透明装饰模式和不透明模式。其实这个划分的原因是从客户端的视角来区分的,对于客户端来说是透明的还是不透明的。(其实这里的理解和我们之前讲的组合模式是一样的,我相信如果认真看过我上一篇组合模式的童鞋,应该能秒懂和想象得到我接下来讲的是什么)

  • 半透明装饰模式
    咱们看上面的例子的客户端:
public class Client
{public static void main(String args[]){Transform camaro;camaro = new Car();camaro.move();Robot bumblebee = new Robot(camaro);bumblebee.move();bumblebee.say();}
}

我们看到开头两行的代码,其实我们一开始设计就已经封装了抽象类Transform但是在实际中并不是统一的定义,为什么呢?其实是因为我们我们在装饰之后,我们新的装饰类子类多了些和之前不同的全新方法,而这些方法,在抽象类中是没有的,以至于只能在具体装饰类中里面去定义实现

而这对于客户端来说是不透明的, 我们原本装饰的目的就是,不改变原先的借口进行拓展,增强原有的功能,但是这里因为装饰后而增加了新的功能,以至于我们编写代码的时候不能很好的统一去编写而是要分别定义。但是实际开发中,其实半透明还更常见,因为增加方法其实是常有的事情。但是有个缺点就是不能实现多重装饰。可以看下面透明模式中客户端代码理解

  • 透明装饰模式
    在这模式中,针对于客户端操作的统一性,就有了透明装饰模式。
    上面的例子我们可以修改一下让大家理解,其实就是只要在最开始的抽象类中就定义好大家共用的方法即可,也即装饰具体类本身也不要去拓展新方法,而是稍微的重构或者补充原方法,那就可以写成透明装饰模式。
//抽象构件类
public abstract class Transform
{public abstract void move();
}
//汽车Car
public final class Car extends Transform{public void Car(){System.out.println("变形金刚是一辆车!");}public void move(){System.out.println("在陆地上移动!");}
}//装饰类Changer
public class Changer extends Transform{private Transform t ;public  void add(Transform t){this.t = t;}public void move(){t.move();//调用关联类自己的方法}
}//接下来就是机器人类和飞机类,继承装饰器类即可
public class Robot extends Changer{public Robot(Transform transform){super(transform);System.out.println("变成机器人!");}public void move(){System.out.println("连滚带爬的move!");}
}public class AirPlane extends Changer{public AirPlane(Transform transform){super(transform);System.out.println("变成飞机!");}public void move(){System.out.println("在天空飞翔的移动!");}
}//客户端
public class Client
{public static void main(String args[]){Transform camaro;camaro = new Car();camaro.move();Transform bumblebee;bumblebee = new Robot(camaro);bumblebee.move();Transform plane;plane = new AirPlane(camaro);plane.move();}
}

好的可以看到我们抽象构件类定义了统一的方法,而在装饰子类中我们也只是增强了原来的方法,所以对于客户端我们可以用抽象类去定义然后统一的操作,细心的同学肯定发现了,我这客户端最后定义的plane用的是我新建装饰器具体类,也就是透明模式比半透明模式还好的就是可以实现多重装饰

(二) 两种组合模式的总结

  • 半透明(Semi-transparent)装饰模式

    • 用具体装饰类型来定义装饰之后的对象,而具体构件使用抽象构件类型来定义
    • 对于客户端而言,具体构件类型无须关心,是透明的;但是具体装饰类型必须指定,这是不透明的
    • 可以给系统带来更多的灵活性,设计相对简单,使用起来也非常方便
    • 客户端使用具体装饰类型来定义装饰后的对象,因此可以单独调用扩展新增的方法
    • 最大的缺点在于不能实现对同一个对象的多次装饰,而且客户端需要有区别地对待装饰之前的对象和装饰之后的对象
  • 透明(Transparent)装饰模式
    • 要求客户端完全针对抽象编程,装饰模式的透明性要求客户端程序不应该将对象声明为具体构件类型或具体装饰类型,而应该全部声明为抽象构件类型
    • 对于客户端而言,具体构件对象和具体装饰对象没有任何区别
    • 可以让客户端透明地使用装饰之前的对象和装饰之后的对象,无须关心它们的区别
    • 可以对一个已装饰过的对象进行多次装饰,得到更为复杂、功能更为强大的对象
    • 无法在客户端单独调用新增拓展方法。(只能内嵌使用)

(三) 模式分析

模式类图:

  • Component: 抽象构件类
  • ConcreteComponent: 具体构件类
  • Decorator: 抽象装饰类
  • ConcreteDecorator: 具体装饰类

(1) 模式特点

  1. 对于扩展一个对象的功能,装饰模式比继承更加灵活,不会导致类的个数急剧增加
  2. 可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的具体装饰类,从而实现不同的行为
  3. 可以对一个对象进行多次装饰
  4. 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,且原有类库代码无须改变,符合开闭原则

(2) 模式缺点

  1. 使用装饰模式进行系统设计时将产生很多小对象,大量小对象的产生势必会占用更多的系统资源,在一定程度上影响程序的性能
  2. 比继承更加易于出错,排错也更困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐

五、使用情景

  • 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责
  • 当不能采用继承的方式对系统进行扩展或者采用继承不利于系统扩展和维护时可以使用装饰模式

六、延申及拓展

  • 两种装饰模式,上面讲述过了就不重复了
  • 装饰模式的简化
    • 怎么简化都好,对于客户端来说,装饰前后都可以同等对待
    • 尽量保持构建具体类为“轻”类,可以通过装饰类进行拓展
    • 如果只有一个具体构件类没有抽象构建类,那么可以将抽象装饰类作为具体构建类子类。
  • 最后再给个小例子:
    • 软件公司基于面向对象技术开发了一套图形界面构件库VisualComponent,该构件库提供了大量基本构件,如窗体、文本框、列表框等。由于在使用该构件库时,用户经常要求定制一些特效显示效果,如带滚动条的窗体、带黑色边框的文本框、既带滚动条又带黑色边框的列表框等等,因此经常需要对该构件库进行扩展以增强其功能。
    • 原先利用继承的方式:
    • 现在利用装饰模式:

总结

本篇文章主要介绍设计模式的装饰模式,以及它的两种实现方式,和组合模式有相像的地方,注意区分哦!。

设计模式(十)—— 装饰模式(定义、案例分析、特点、缺点)相关推荐

  1. Java设计模式—门面模式(带案例分析)

    (尊重劳动成果,转载请注明出处:https://blog.csdn.net/qq_25827845/article/details/52032536冷血之心的博客) 目录 1.门面模式的定义: 2.通 ...

  2. SWAT模型案例分析

    SWAT模型的产生 SWAT模型的最直接前身是SWRRB模型.而SWRRB模型则起始于20世纪70年代美国农业部农业研究中心开发的CREAMS(Chemicals, Runoff, and Erosi ...

  3. SWAT模型十八个案例

    详情点击链接:SWAT模型十八项案例分析 一:基于网络资源的SWAT模型快速建模 二:基于遥感产品的SWAT模型率定与验证 三:基于水文响应单元(HRU)的水资源时空分布特征 四:基于自定义流域与河道 ...

  4. 【西安】SWAT模型高阶十七项案例分析

    [案例实践]:本次学习共十七个实践案例 1)遥感产品和SWAT模型快速建模 2)基于水文响应单元(HRU)的水资源时空分布特征 3)基于自定义流域与河道的SWAT模型建模与分析 4)子流域划分原理及其 ...

  5. 【设计模式十六之装饰模式】装饰者模式

    Decorator Pattern装饰模式 细说装饰模式 细说装饰模式 定义 UML模型 场景 场景一 场景二 代码 代码一 代码二 基于UML的代码 装饰模式应用和注意事项 细说装饰模式 提示: 博 ...

  6. 系分 - 案例分析 - 系统维护与设计模式

    个人总结,仅供参考,欢迎加好友一起讨论 文章目录 系分 - 案例分析 - 系统维护与设计模式 典型例题 1 题目描述 参考答案 典型例题 2 题目描述 参考答案 系分 - 案例分析 - 系统维护与设计 ...

  7. Java:计算机编程语言Java的简介、安装(编程环境/工具)、学习路线(如何学习Java以及几十项代码编程案例分析)之详细攻略

    Java:计算机编程语言Java的简介.安装(编程环境/工具).学习路线(如何学习Java以及几十项代码编程案例分析)之详细攻略 目录 Java的简介 1.Java的工作原理--基于Eclipse等编 ...

  8. 设计模式案例分析与实现

    1.UML类图及Java实现 案例:某基于C/S的即时聊天系统登录模块功能描述如下:用户通过登录界面(LoginForm)输入账号和密码,系统将输入的账号和密码与存储在数据库(User)表中的用户信息 ...

  9. 不懂就问--Java基本数据类型与数组(定义了int型二维数组a[6][7]后,数组元素a[3][4]前的数组元素个数)案例分析

    首先要知道java中的数组是线性排列的 其次要知道数组的初始标度为0 例如定义了一个二维数组 a[1][2],则数组详细为 [0][0]: [0][1]: [0][2]: [1][0]: [1][1] ...

最新文章

  1. java 判断页面刷新_如何判断一个网页是刷新还是关闭的方法
  2. 如何使用以太网将 Mac 接入互联网?
  3. 10:Java人脸识别认证-Java API 实战
  4. 三种常见的ps删除通道的操作
  5. php中的单引号和双引号
  6. 服务器操作系统有哪些?
  7. 永恒之蓝方程式利用工具使用教程
  8. html+js 实现 推箱子 贪吃蛇和简单的飞机大战
  9. 从七桥问题开始:全面介绍图论及其应用
  10. 阿里大佬自述:Java零基础到精通是这样炼成的!
  11. 机器学习数据的预处理
  12. windows禁用屏幕旋转_如何在Windows 10中禁用屏幕自动旋转
  13. 60 个让程序员崩溃的瞬间,哈哈哈哈哈哈哈哈哈
  14. SAAS产品有哪些优缺点?
  15. POI 在导出的Excel中插入图片
  16. 第十五届 D2 前端技术论坛,我们云端相聚!
  17. 微信公众号硬件开发杂谈(二)
  18. python图形界面设计代码_(八)Python 图形化界面设计
  19. 读书笔记-《孩子,你慢慢来(彩插新版) (龙应台人生三书)》
  20. 计算FDTD光纤耦合效率!煞费苦心熬了几个通宵!!!

热门文章

  1. 杨老师的课(JSU-ZJJ)
  2. 十个有趣的英文文字游戏(下)
  3. 5个层面解构游戏的设计标准
  4. 【无标题】解决source insight 按空格键或者添加新文字内容会覆盖原文字内容
  5. 蚂蚁金服美女分析师告诉你:从数据分析到数据洞察,我们是这么玩儿的
  6. 低代码:传统软件厂商达摩克利斯之剑吗?
  7. 解决WinXP网络邻居共享问题
  8. 类的加载流程、反射、直接引用和符号引用
  9. python sklearn 线性回归 报错_(转)Python- sklearn之最小二乘法
  10. Camunda工作流平台的使用