初识设计模式(装饰者模式)

 前言:总结这两天学到的装饰者模式,并用java小小的实现一下。书中有写到:给爱用继承的人一个全新的设计眼界。(ps,本文最后有个小问题待解决)

什么是装饰者模式(Decorator Pattern)?

定义:动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

使用的设计原则:开放-关闭原则,类应该对扩展开放,对修改关闭。

代表:Java IO 流

类图:

 

装饰者模式的优缺点?

  优点

  1. Decorator模式与继承关系的目的都是要扩展对象的功能,但是Decorator可以提供比继承更多的灵活性。
  2. 通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。

  缺点

  1. 装饰模式会导致设计中出现许多小类,如果过度使用,会使程序变得很复杂,也会给使用此API的程序员造成困扰。
  2. 这种比继承更加灵活机动的特性,也同时意味着更加多的复杂性。
  3. 装饰模式是针对抽象组件(Component)类型编程。但是,如果你要针对具体组件编程时,就应该重新思考你的应用架构,以及装饰者是否合适。当然也可以改变Component接口,增加新的公开的行为,实现“半透明”的装饰者模式。在实际项目中要做出最佳选择。
  4. 采用装饰者在实例化组件时,将增加代码的难度。一旦使用装饰者模式,不只需要实例化组件,还要把此组件包装进装饰者中。(这个问题可以使用工厂(Factory)模式和生成器(Builder)模式来解决)

什么情况下使用装饰者模式?

  1. 需要扩展一个类的功能,或给一个类添加附加职责。
  2. 需要动态的给一个对象添加功能,这些功能可以再动态的撤销。
  3. 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实。
  4. 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。

举例说明

  场景:现在需要炒一盘菜,可以给这盘菜加多种作料,这盘菜也可以有不同作料的组合。

  实现:(如果只有一盘菜存在,不会再有第二盘菜的情况,也可以不创建抽象组件,直接创建具体组件,抽象的装饰类继承具体组件。)

  • 先创建抽象组件
     1 /**
     2  * 抽象组件 一盘菜
     3  */
     4 abstract class Dish{
     5     protected String name;
     6 
     7     public String getName(){
     8         return this.name;
     9     }
    10 }
  • 然后创建具体组件
    1 /**
    2  * 具体组件
    3  */
    4 class MeatDish extends Dish{
    5     public MeatDish(){
    6         super.name = "meatDish";
    7     }
    8 }
  • 然后创建抽象装饰类
    1 /**
    2  * 抽象装饰类 作料
    3  */
    4 abstract class Seasoning extends Dish{
    5     @Override
    6     public abstract String getName();
    7 }
  • 然后创建抽象具体装饰类
     1 /**
     2  * 具体装饰类 盐
     3  */
     4 class Salt extends Seasoning{
     5     private Dish dish;
     6     public Salt(Dish dish){
     7         this.dish = dish;
     8     }
     9     @Override
    10     public String getName() {
    11         return this.dish.getName() + " ,Salt";
    12     }
    13 }
    14 
    15 /**
    16  * 具体装饰类,鸡精
    17  */
    18 class ChickenPowder extends Seasoning{
    19     private Dish dish;
    20     public ChickenPowder(Dish dish){
    21         this.dish = dish;
    22     }
    23     @Override
    24     public String getName() {
    25         return this.dish.getName() + " ,ChickenPowder";
    26     }
    27 }
  • 然后测试
    1 public static void main(String[] args){
    2         Dish dish = new MeatDish();
    3         System.out.println(dish.getName());
    4 
    5         dish = new Salt(dish);
    6         dish = new ChickenPowder(dish);
    7         System.out.println(dish.getName());
    8     }
  • 最后输出结果

总结:

  1. 装饰者和被装饰者拥有相同的超类
  2. 你可以用一个或多个装饰者包装一个对象
  3. 既然装饰者和被装饰者对象有相同的超类型,所以在任何需要原始对象(被包装的)的场合,可以用装饰过的对象代替它
  4. 装饰者可以在所委托被装饰者的行为之前与/或之后,加上自己的行为,以达到特定的目的
  5. 对象可以在任何时候被装饰,所有可以在运行时动态地、不限量地用你喜欢的装饰者来装饰对象

提问:

  问:不是说这个让爱用继承的人改变眼界吗?怎么类图还是使用的继承?

  答: 这么做的重点在于,装饰者和被装饰者必须是一样的类型,也就是有共同的超类,这是相当关键的地方。在这里,是利用继承达到“类型匹配”,而不是利用继承获得“行为”。

  问:那么行为从哪里来?

  答:当我们将装饰者与组件组合时,就是在加入新的行为。所得到的新行为,并不是继承自超类,而是由组合对象得来的。如果这里依赖继承,那么类的行为只能只能在编译时静态决定。换句话说,行为如果不是来自超类,就是子类覆盖后的版本。反之,利用组合,可以把装饰者混合着用,而且是在运行时。这样,就可以在任何时候实现新的装饰者增加新的行为,如果依赖继承,每当需要新行为时,还得修改现有的代码。

  问:为什么Componet要设计为一个抽象类,而不是一个接口?

  答:通常装饰者模式是采用抽象类。(有大神知道的吗?还请不吝赐教)

 

参考书籍:《Head First 设计模式》《大话设计模式》

转载于:https://www.cnblogs.com/yuxiaole/p/9220672.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/465642.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

帮一个读者解锁手机

昨天晚上,深圳下了好大好大的雨,我还在加班的时候,小云就发消息说家里打雷很大,到了十点多,我打开手机准备打车,发现打车排队有800多人,然后我赶紧冲下楼,拿起两个雨衣,消…

STM32F7xx —— 96位唯一ID

STM32F7xx —— 96位唯一ID // 配置单片机型号 对外只有这个宏CONFIG_SYSTEM_HARDWARE_TYPE #define CONFIG_SYSTEM_HARDWARE_TYPE SOC_TYPE_STM32F7// SOC类型 typedef enum {SOC_TYPE_STM32F0,SOC_TYPE_STM32F1,SOC_TYPE_STM32F2,SOC_TYPE_STM32F3,SOC_TYPE_STM32F4,SOC_TY…

我喜欢这样的老大[10-24]

据说原图是奥巴马和一个小朋友打雪仗,下面的图片是众多恶搞作品中的一个。也是我最喜欢的一个,因为总统先生可爱,猫更可爱。。。。我喜欢这样的老大,一把年纪了还保有童真非常难得;作为一位总统,愿意把童真…

知识点:Mysql 数据库索引优化实战(4)

知识点:Mysql 索引原理完全手册(1) 知识点:Mysql 索引原理完全手册(2) 知识点:Mysql 索引优化实战(3) 知识点:Mysql 数据库索引优化实战(4) 一:插入订单 业务逻辑:插入订单数据,为了避免重复导单…

看漫画学电子,非常精彩!有些概念以前模糊现在真的懂了

来源:gadgetronicx.com,排版:晓宇微信公众号:芯片之家(ID:chiphome-dy)1、按键消抖,在机械按键断开与闭合时,按键的触电是有一点弹性的,按下去的时候不会马上…

STM32F7xx —— 串口通信

STM32F7xx —— 串口通信 目录 STM32F7xx —— 串口通信 一、串口初始化过程 二、几个重要的串口函数 三、几个重要的结构 四、基本接口设计 一、串口初始化过程 1、时钟使能; 2、GPIO初始化; 3、串口波特率设置; 4、串口控制&#…

一个跳楼博士生的遗书:这个世界是一沟绝望的死水

o(︶︿︶)o 唉北邮跳楼博士生给母亲堵塞遗书:这个世界是一沟绝望的死水 这个世界是一沟绝望的死水,我在这里再怎么折腾也激不起半点涟漪。所有的努力都会被既得利益集团踩在脚下,所有的奋斗都面临着举步维艰。冷漠的人,谢谢你…

jquery插件课程1 幻灯片、城市选择、日期时间选择、拖放、方向拖动插件

jquery插件课程1 幻灯片、城市选择、日期时间选择、拖放、方向拖动插件 一、总结 一句话总结:都是jquery插件,都还比较小,参数(配置参数、数据)一般都是通过json传递。 1、插件配置数据或者参数的时候用的是什么数据结…

由c语言转向c++,我们需要做什么?

点击上方蓝字添加关注在此送大家一份小礼物,公众号内回复linux0001即可获得一本Linux电子教程“c语言和c到底有什么不同和联系呢?”毫无疑问,c语言和c是两种不同的语言,但是又有着千丝万缕的联系。语法上c语言与c一脉相承&#xf…

黄聪:【强烈推荐】搜索引擎排名决定一切吗!

在点石看到一篇《搜索引擎排名决定一切么》,作者cqqc626。写的太赞的,Kyw看后都有点激动,希望天下所有要需要SEO服务的客户们,都能明白其中道理,明白排名不是一切。正文如下: 前段时间经常看到很多和自己差…

STM32F7xx —— CAN通信

STM32F7xx —— CAN通信 目录 STM32F7xx —— CAN通信 一、CAN基础 二、几个重要的CAN函数 三、几个重要的结构 四、接口设计 一、CAN基础 差分信号:显性电平对应逻辑0,CAN_H和CAN_L差为2.5V;隐形电平对应逻辑1,CAN_H和CAN_…

[leetcode] 14. 最长公共前缀

14. 最长公共前缀 超级简单。。。 class Solution { public:string longestCommonPrefix(vector<string> &strs) {if (strs.size() 0) {return "";}string common strs[0];for (int i 1; i < strs.size(); i) {common findCommon(strs[i], common);…

Cmake确实应该用到的时候再学

最近在做项目用到Cmake相关的知识&#xff0c;之前做的是BSP驱动开发&#xff0c;基本用不到Cmake&#xff0c;唯一和Cmake有交集的应该是我们移植网关项目&#xff0c;不过也只是修修改改&#xff0c;直到最近用到Cmake开发项目&#xff0c;才真正是接触了这个东西。前天加载一…

手机吞吃蛇游戏的设计与开发

为什么80%的码农都做不了架构师&#xff1f;>>> J2ME(Java 2 Micro Edition) 是近年来随着各种不同设备&#xff0c;尤其是移动通信设备的飞速发展而诞生的一项新的开发技术。它定位在消费性电子产品的应用上&#xff0c;对设备的智能化、www.21cnlunwen.com Write…

STM32F7xx —— 内部flash

STM32F7xx —— 内部flash 这个就没什么好说的了&#xff0c;直接上代码了&#xff0c;主要封装了三个函数&#xff0c;擦除&#xff0c;写flash&#xff0c;读flash。 // STM32F767IGT6: 1M flash // STM32F767ZIT6: 2M flash #define ADDR_FLASH_SECTOR_0 ((uint32_t)0x…

深入理解 Vue Computed 计算属性

Computed 计算属性是 Vue 中常用的一个功能&#xff0c;我们今天来说一下他的执行过长 拿官网简单的例子来看一下&#xff1a; <div id"example"><p>Original message: "{{ message }}"</p><p>Computed reversed message: "{…

vscode 使用 ssh 登录

// 执行 // 使用你自己的服务器IP与登录账户 export USER_AT_HOST"服务器账户名服务器IP" // PUBKEYPATH是你公钥的路径 export PUBKEYPATH"$HOME/.ssh/id_rsa.pub"ssh-copy-id -i "$PUBKEYPATH" "$USER_AT_HOST"

SNMP4J的一点缺陷

最近在使用SNMP4J的过程中发现一个缺陷&#xff0c;不知道应不应该算是个bug&#xff0c;但我想终究算是一个不完善的地方。 问题描述如下&#xff1a; 在通过SNMP4J去获取某些交换机上的MAC地址转发表(dot1dTpFdbTable, OID为1.3.6.1.2.1.17.4.3&#xff09;时&#xff0c;发现…

3-3 数数字

算法入门经典 P57 把前n&#xff08;n<100000&#xff09;个整数顺序写在一起&#xff0c;123456789...数一数0-9各出现多少次。 #include<stdio.h>#include<string.h>#include "stdafx.h"#include "iostream" #include <string&…

如何用示波器测量串口波特率

这是前段时间遇到的问题&#xff0c;刚好这里找到了答案&#xff0c;记录下分享给大家。如何确定时基假如要测量的波特率为9600, 则每一比特位的时间为&#xff1a;1/9600 ≈ 104 μs&#xff0c;一般示波器横向上每个大格子里5个小格子&#xff0c;要想看清一比特位一般需要一…