文章目录

  • 一、基本概念
    • 模式角色
  • 二、简单实例
    • 实例一、变形金刚
      • 类图
      • 代码实现
      • 结果截图
    • 实例二、喜羊羊与灰太狼
      • 类图
      • 方式一:半透明模式
        • 代码实现
        • 结果截图
      • 方式二:半透明模式+透明模式
        • 代码实现
        • 结果截图
  • 三、透明模式和半透明模式的区别
    • 辨析
    • 透明模式
    • 半透明模式
  • 四、小结
    • 优缺点
    • 适用场景

一、基本概念

装饰模式是一种用于替代继承的技术,它通过一种无须定义子类的方式来给对象动态增加职责,使用对象之间的关联关系取代类之间的继承关系。

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

模式角色

  • Component(抽象构件类):定义了对象的接口,可以给这些对象动态增加职责(方法),是具体构建和抽象装饰类的共同父类,声明了具体构件中需要实现的方法。

  • ConcreteComponent(具体构件类):定义了具体的构建对象,实现了在抽象构建中声明的方法,装饰器可以给他增加额外的方法。

  • Decorator(抽象装饰类):是抽象构建类的子类,用于具体构件增加职责。

  • ConcreteDecorator(具体装饰类):是抽象装饰类的子类,负责增添加新的职责。

二、简单实例

实例一、变形金刚

类图

代码实现

  • 抽象构件类Transform(变形金刚)
package com.transform;public interface Transform {public void move();  //移动方法
}

接口中定义move()方法,具体构件类必须实现此方法。

  • 具体构件类Car(汽车类)
package com.transform;
/*** @Description:具体构件类* 注意这里的Car设置为final类型* 意味着不能通过继承拓展其功能,但是可以通过关联关系来拓展功能*/
public final class Car implements Transform {public Car() {System.out.println("变形金刚是一辆车!");}@Overridepublic void move() {System.out.println("在陆地上移动");}
}
  • 抽象装饰类Changer(变化类)
package com.transform;/*** @Description:具体装饰类Changer* 具体装饰类是装饰模式的核心* 实现了move方法,保证原有方法不会丢失,又可以添加新的功能*/
public class Changer implements Transform{private Transform transform;//构造方法注入public Changer(Transform transform) {this.transform = transform;}@Overridepublic void move() {transform.move();}
}
  • 具体装饰类Robot(机器人类)
package com.transform;
/*** @Description:具体装饰类*/
public class Robot extends Changer {//用构造方法继承了父类定义的方法,还可以增加新的职能public Robot(Transform transform) {super(transform);System.out.println("变成机器人");}public void say(){System.out.println("你tm说话啊!");}
}
  • 具体装饰类Airplane(飞机类)
package com.transform;
/*** @Description:具体装饰类*/
public class Robot extends Changer {//用构造方法继承了父类定义的方法,还可以增加新的职能public Robot(Transform transform) {super(transform);System.out.println("变成机器人");}public void say(){System.out.println("你tm说话啊!");}
}
  • 客户端Client
package com.transform;
/*** @Description:*/
public class Client {public static void main(String[] args) {//创建carmaro对象Transform camaro;camaro = new Car();camaro.move();System.out.println("-------------------------");Robot bumblebee = new Robot(camaro);bumblebee.move();bumblebee.say();}
}

结果截图

实例二、喜羊羊与灰太狼

题目: “喜羊羊逃命”游戏:喜羊羊被灰太狼追,喜羊羊最多5条命,灰太狼每咬到喜羊羊一次,喜羊羊就要少一条命。在逃的过程中喜羊羊可以吃到三种苹果,吃“红苹果”可以给喜羊羊加上保护罩,吃“绿苹果”可以加快喜羊羊奔跑速度,吃“黄苹果”可以使喜羊羊趟着水跑。应用装饰模式,用JAVA控制台应用程序实现该设计。绘制该模式的UML图。
提示:这个例子如果用类的继承来实现的话那可就麻烦了,你需要为喜羊羊派生321=6个子类(有保护罩的喜羊羊,奔跑速度加快的喜羊羊,会趟水的喜羊羊,既有保护罩又会趟水的喜羊羊,奔跑速度快且会趟水的喜羊羊,有保护罩且奔跑速度快的喜羊羊,有保护罩、奔跑速度快且会趟水的喜羊羊),如果使用装饰模式的那就不用派生诸多子类了,当喜羊羊每吃到一个苹果,我们就用装饰模式给喜羊羊加一个动态增加一个新功能即可。

类图

方式一:半透明模式

实质方式一是以构造方法注入方式实现,方式二通过调用子类新添加的方法实现。

代码实现

  • 抽象构件类Appearance
package sheep;
/*** @Description:抽象构建类*/
abstract class Appearance {public int lives;public abstract void display();
}
  • 具体构件类Sheep
package sheep;
/*** @Description:具体构建类*/
public final class Sheep extends Appearance {public String name;public Sheep(String name) {this.name = name;}public void setlives(int lives) {this.lives = lives;}@Overridepublic void display() {System.out.println(name+"的状态:");}
}
  • 抽象装饰类Decorator
package sheep;
/*** @Description:抽象装饰类*/
public class Decorator extends Appearance {protected Appearance appearance;//装饰方法public void Decorate(Appearance appearance){this.appearance = appearance;this.lives = appearance.lives;}@Overridepublic void display() {if (appearance!=null){ //确保注入成功appearance.display();}}
}
  • 具体装饰类:GreenApple
package sheep;
/*** @Description:具体装饰类*/
public class GreenApple extends Decorator {@Overridepublic void display() {appearance.lives = lives; //更新组件的lives属性//注意这两句不能上下调转super.display();System.out.println("吃了绿苹果,具有加速技能,生命值为"+appearance.lives);}
}
  • 具体装饰类RedApple
package sheep;
/*** @Description:具体装饰类*/
public class RedApple extends Decorator {@Overridepublic void display() {this.appearance.lives = lives;//更新组件的lives属性//注意这两句不能上下调转super.display();System.out.println("吃了红苹果,具有保护罩技能,生命值为"+appearance.lives);}
}

还有一个具体装饰类,YellowApple代码省略…

  • 具体装饰类WolfAtack
package sheep;
/*** @Description:具体构架类:狼咬羊类*/
public class WolfAtack extends Decorator {@Overridepublic void display() {super.display();//注意这两句不能上下调转this.appearance.lives = appearance.lives - 1;System.out.println();System.out.println("----------------------------------");System.out.println("被狼咬了一口,生命值-1,生命值为"+appearance.lives);System.out.println("----------------------------------");System.out.println();}
}
  • 客户端Client
package sheep;
public class Client {public static void main(String[] args) {//半透明模式:可以调用其他方法Sheep sheep = new Sheep("喜羊羊");sheep.setlives(5);//设置初始为5条命//半透明模式:可以调用其他方法Decorator redApple,greenApple,yellowApple;//声明一只狼Decorator wolf = new WolfAtack();//羊吃了红苹果redApple = new RedApple();redApple.Decorate(sheep);//狼咬了羊wolf.Decorate(redApple);wolf.display();//羊又吃了绿苹果greenApple = new GreenApple();greenApple.Decorate(redApple);//狼咬了羊wolf.Decorate(greenApple);wolf.display();yellowApple = new YellowApple();yellowApple.Decorate(greenApple);//现在羊的状态yellowApple.display();}
}

结果截图

方式二:半透明模式+透明模式

代码实现

  • 抽象构件类Appearance
package sheet;
/*** @Description:抽象构件类*/
abstract class Appearance {public int lives;public abstract void display();
}
  • 具体构建类
package sheet;
/*** @Description:具体构件类*/
public final class Sheep extends Appearance {public String name;public Sheep(String name) {this.name = name;}public void setlives(int lives) {this.lives = lives;}@Overridepublic void display() {System.out.println(name+"的状态:");}
}
  • 抽象装饰类
package sheet;
/*** @Description:抽象装饰类*/
public class Decorator extends Appearance {protected Appearance appearance;public Decorator(Appearance appearance) {this.appearance = appearance;this.lives = appearance.lives;}@Overridepublic void display() {appearance.display();}
}
  • 具体装饰类:只写GreenApple类省略了YellowApple和RedApplle代码
package sheet;/*** @author:xiaofa* @date2020/5/111:18* @Description:具体装饰类*/
public class GreenApple extends Decorator {public GreenApple(Appearance appearance) {super(appearance);}@Overridepublic void display() {this.appearance.lives = lives;//注意这两句不能上下调转super.display();System.out.println("吃了绿苹果,具有加速技能,生命值为"+appearance.lives);}
}
  • 具体实现类WolfAtack
package sheet;
public class WolfAtack extends Decorator {public WolfAtack(Appearance appearance) {super(appearance);}@Overridepublic void display() {super.display();appearance.lives = appearance.lives-1;this.lives = appearance.lives;System.out.println();System.out.println("----------------------------------");System.out.println("被狼咬了一口,生命值-1,生命值为"+appearance.lives);System.out.println("----------------------------------");System.out.println();}
}
  • 客户端Client
package sheet;public class Client {public static void main(String[] args) {//半透明模式:可以调用其他方法Sheep sheep = new Sheep("喜羊羊");sheep.setlives(5);//设置初始为5条命//透明模式:不能调用其他方法Appearance redApple,greenApple,yellowApple,wolfAtack;//羊吃了红苹果redApple = new RedApple(sheep);//狼咬了羊wolfAtack = new WolfAtack(redApple);//狼咬了羊wolfAtack.display();//羊吃了绿苹果greenApple = new GreenApple(redApple);//狼咬了羊wolfAtack = new WolfAtack(greenApple);//狼咬了羊wolfAtack.display();//羊吃了黄苹果yellowApple = new YellowApple(greenApple);//羊现在的状态yellowApple.display();}
}

结果截图

三、透明模式和半透明模式的区别

辨析

注意本实例中创建和初始化喜羊羊都是调用setlives()方法

Sheep sheep = new Sheep("喜羊羊");
sheep.setlives(5);//设置初始为5条命

而不是

Appearance sheep = new Sheep("喜羊羊");
sheep.setlives(5);//设置初始为5条命  //非法

是因为Sheep是具体构件类继承了抽象构件类Appearance的方法,如果需要调用子类Sheep新添加的setLives方法,就要用这种半透明模式定义。


另外方式一中:

//半透明模式:不能调用其他方法Decorator redApple,greenApple,yellowApple;//声明一只狼Decorator wolf = new WolfAtack();//羊吃了红苹果redApple = new RedApple();redApple.Decorate(sheep);//狼咬了羊wolf.Decorate(redApple);wolf.display();

方式二中:

//透明模式:不能调用其他方法Appearance redApple,greenApple,yellowApple,wolfAtack;//羊吃了红苹果redApple = new RedApple(sheep);//狼咬了羊wolfAtack = new WolfAtack(redApple);

方式一是半透明模式,调用了具体装饰类中新添加的Decorate()方法;而方式二是透明模式,不能直接调用新添加的方法,但是可以一构造方法注入的方式实现。

透明模式

使用抽象构件类型Component定义全部具体构件对象和具体装饰对象,客户端可以一致地使用这些对象,因此符合透明装饰模式的要求。透明装饰模式可以让客户端透明地使用装饰之前的对象和装饰之后的对象,无须关心它们的区别,此外,还可以对一个已装饰过的对象进行多次装饰,得到更为复杂、功能更为强大的对象。在实现透明装饰模式时,要求具体装饰类的operation()方法覆盖抽象装饰类的operation()方法,除了调用原有对象的operation()外还需要调用新增的addedBehavior()方法来增加新行为,

半透明模式

透明装饰模式的设计难度较大,而且有时我们需要单独调用新增的业务方法。为了能够调用到新增方法,我们不得不用具体装饰类型来定义装饰之后的对象,而具体构件类型还是可以使用抽象构件类型来定义,这种装饰模式即为半透明装饰模式,也就是说,对于客户端而言,具体构件类型无须关心,是透明的;但是具体装饰类型必须指定,这是不透明的。

思考:为什么半透明装饰模式不能实现对同一个对象的多次装饰?

因为在半透明装饰模式中,使用具体装饰类来声明装饰之后的对象,具体装饰类中新增的方法并未在抽象构件类中声明,这样做的优点在于装饰后客户端可以单独调用在具体装饰类中新增的业务方法,但是将导致无法调用到之前装饰时新增的方法,只能调用到最后一次装饰时具体装饰类中新增加的方法,故对同一个对象实施多次装饰没有任何意义。

四、小结

优缺点

  • 优点

(1) 对于扩展一个对象的功能,装饰模式比继承更加灵活性,不会导致类的个数急剧增加。

(2) 可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的具体装饰类,从而实现不同的行为。

(3) 可以对一个对象进行多次装饰,通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合,得到功能更为强大的对象。

(4) 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,原有类库代码无须改变,符合“开闭原则”。

  • 缺点

(1) 使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,大量小对象的产生势必会占用更多的系统资源,在一定程序上影响程序的性能。

(2) 装饰模式提供了一种比继承更加灵活机动的解决方案,但同时也意味着比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为繁琐。

适用场景

(1) 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。

(2) 当不能采用继承的方式对系统进行扩展或者采用继承不利于系统扩展和维护时可以使用装饰模式。不能采用继承的情况主要有两类:第一类是系统中存在大量独立的扩展,为支持每一种扩展或者扩展之间的组合将产生大量的子类,使得子类数目呈爆炸性增长;第二类是因为类已定义为不能被继承(如Java语言中的final类)。

参考书籍《装饰模式——Java设计模式刘伟第二版》

装饰模式——初学JAVA设计模式相关推荐

  1. 初学 Java 设计模式(十一):实战外观模式 「类型转换器」

    一.外观模式介绍 1. 解决的问题 主要解决访问复杂系统的内部子系统的复杂度问题,简化客户端与其子系统的接口. 2. 定义 外观模式是一种结构型设计模式,能为程序库.框架或其他复杂类提供一个简单的接口 ...

  2. 初学 Java 设计模式(十五):实战命令模式 「扫码点餐」

    一.命令模式介绍 1. 解决的问题 主要解决在系统中,行为请求者和行为实现者紧耦合的问题. 2. 定义 命令模式是一种行为设计模式,它可将请求转换为一个包含与请求相关的所有信息的独立对象.这个转换会根 ...

  3. 初学 Java 设计模式(五):实战原型模式 「英雄联盟齐天大圣-真假猴王」

    一.原型模式介绍 1. 解决的问题 主要解决的问题就是创建重复对象,这部分对象内容本身比较复杂,生成过程可能从库中或者RPC接口中获取数据的耗时较长,因此采用克隆的方式节省时间. 2. 定义 原型模式 ...

  4. 初学 Java 设计模式(三):实战抽象工厂方法模式 「QQ 厘米秀装扮」

    一.抽象工厂方法模式介绍 1. 解决的问题 通过接口的选择,解决在系统产品存在多个产品族,而系统仅消费某一族的产品的问题. 2. 定义 抽象工厂模式是一个围绕超级工厂创建其他工厂的模式,即抽象工厂是一 ...

  5. 初学 Java 设计模式(十九):实战观察者模式 「您的快递已到达蜂站,请及时签收」

    一.观察者模式介绍 1. 解决的问题 主要解决将一个对象的状态改变通知给其他对象的问题. 2. 定义 观察者模式是一种行为设计模式,允许你定义一种订阅机制,可在对象事件发生时通知多个 "观察 ...

  6. 初学Java常用设计模式之——装饰器模式

    声明:转载请附上原文链接 提示:标题序号从8开始,是照应不同设计模式笔记发布的顺序而定的,比如,上一篇文章 初学Java常用设计模式之--桥接模式和组合模式 序号从7开始. 8. 装饰器设计模式(重点 ...

  7. Java设计模式(7)装饰模式(Decorator模式)

    Decorator常被翻译成"装饰",我觉得翻译成"油漆工"更形象点,油漆工(decorator)是用来刷油漆的,那么被刷油漆的对象我们称decoratee.这 ...

  8. JAVA设计模式之装饰模式

    在阎宏博士的<JAVA与模式>一书中开头是这样描述装饰(Decorator)模式的: 装饰模式又名包装(Wrapper)模式.装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替 ...

  9. java设计模式之装饰模式_Java中的装饰器设计模式

    java设计模式之装饰模式 装饰器设计模式允许在运行时将附加职责或行为动态附加到对象. 它是一种结构模式,利用聚合来组合这些行为. 在本教程中,我们将学习实现装饰器模式. UML图: 让我们从装饰器模 ...

最新文章

  1. css menu builder,AutoPlay Menu Builder使用教程【图文教程】
  2. C# HashTable的用法总结
  3. sqlldr,将数据批量导入Oracle数据库
  4. 使用soapUI代替WSDL2JAVA生成cxf HTTPS 客户端调用代码
  5. 这些年,我收集的JavaScript代码(二)
  6. redis与lua整合
  7. pixhawk的姿态控制算法解读
  8. Python环境下,提高pip安装库速度的方法!
  9. Java源码混淆,jar包加密,禁止反编译jar包
  10. 根据一张表更新另一张表
  11. 如何打开 Mac 的摄像头?
  12. python-绘制双轴柱状图
  13. android 通过串口来控制pwm的输出_ESP8266_07基于PWM的呼吸灯
  14. 单片机原理及应用c语言版答案,单片机原理及应用(C语言版(周国运)习题答案.doc...
  15. 华为畅享20plus能更鸿蒙不,甘南收购华为畅享20Plus尾插排线数据线耳机
  16. Microsoft Teams显示连接不上网
  17. locust工具学习笔记(三)-Tasks属性、tag修饰符、TaskSet类
  18. 那些年,我们一起写的情诗
  19. 微型计算机的主要硬件以及技术指标,微型计算机的硬件组成.doc
  20. 配置免费的SSL证书

热门文章

  1. React-Native仿某电商商品详情页面
  2. aotm对php加密,在 Atom 中使用 PHP-CS-Fixer
  3. python pip升级需要网快吗_为什么pip要求我升级,而它已经是最新的了?
  4. 【机器学习5】python实现单纯形法和大M法
  5. 大M法的简单matlab程序
  6. Linux ALSA声卡驱动之七:录音(Capture) 调用流程
  7. 孔径问题 — The Aperture Problem
  8. 学习opencv3 pdf_【资源分享】有哪些学习openCV的网站或书籍?
  9. 剑指offer:滑动窗口的最大值(Python)
  10. 2010年:中国IC新一轮造福年