专栏首页吉林乌拉设计模式之装饰者模式

设计模式之装饰者模式

在之前的设计模式文章中楼主已经介绍过了,要尽量针对接口编程,而不要针对实现编程。因为这样我们的程序比较方便扩展,又遵循了设计模式的基本原则。既然要针对接口编程,那么势必会创建大量的子类来实现。但有些时候并不是所有的业务都可以通过创建子类就可以实现的,反而通过创建大量子类,而增加了程序的不可扩展性。所以今天楼主分享一下设计模式中另一种模式叫装饰者模式。装饰者模式运用了对象组合的方式,可以做到在运行时动态的装饰类,这也是装饰者模式的由来。那么在介绍装饰者模式之前,我们和其他的设计模式一样,我们先看一个简单的例子。我们将以游戏中角色为例。我们知道在游戏中角色可以使用很多不同的武器,在使用不同的武器时,用户角色的攻击力就会不同,那么下面的例子我们将创建3个不同的武器分别为刀、剑、枪,并为这3个武器分别初始化不同的攻击力。下面为具体的代码。

至此我们实现了上述基本的需求了。但我们知道在游戏的角色中,除了携带武器外,还可以穿戴很多种饰品,也就是装备。穿戴不同的装备可以增加不同的攻击力。如果我们将上述的需求在修改的复杂一点,也就是游戏角色除了要携带武器外,还可以穿戴很多种装备。我们知道游戏中的装备很多种为了方便我们测试,我们暂时只创建护肩、胸甲、饰品等。并且游戏角色穿戴不同的装备需要为用户增加不同的攻击力。如果需求变更成这样时,那我们应该怎么修改上述的代码呢?具体的代码如下:

我们现在已经实现了新的需求了。但上面的代码虽然实现了需求,但是违背了设计模式的基本原则,也就是将程序中涉及到可能变化的部分提取出来。除此之外还违背了另一种设计模式的基本原则,也就是开放关闭原则。开放关闭原则:类应该对扩展开放,而对修改关闭。说的简单点就是,如果我们已经开发好了一个类,如果有其他的需求,这个类现在并不能满足我们的需求时,那么可以通过任何扩展的方式来改变我们这个类的行为,也就是前面说的对类的扩展是开放的。而不能通过直接在原有类中修改,这也就是前面所说的关闭,也就是说对一个已经开发好的类对它的修改是关闭的,不允许任何的修改,只能通过扩展来实现。如果我们现在要添加一个新的装备,宠物类型的话,那么显然上述的代码违背了上述这2个设计模式的基本原则了,因为我们不得不修改原先已经修改好的代码,这样很容易会导致曾经已经编写好的代码产生新的问题。那么我们怎么能解决上述的问题呢?有没有一种设计模式类似观察者模式那样呢?在我们创建新的观察者时,是不需要重新修改主题的代码的。答案一定是有的,也就是本文将要介绍的设计模式,也就是装饰者模式。下面我们看一下装饰者模式的定义。

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

上面就是装饰者模式的定义,说的比较笼统。下面我们以游戏的需求为例,在来做简单的说明。如果我们创建一个游戏角色的话,如果不设置装备的话,那么当前游戏角色就是没有穿戴任何装备,也就是当前游戏角色的攻击力只有武器。如果我们这时想要穿戴护肩这个装备的话,那么就创建这个护肩对象来装饰这个游戏角色,如果我们这时还想要穿戴胸甲的话,那么就继续创建胸甲这个对象来,继续装饰已经被护肩对象装饰的游戏角色。以此类推,并且,每个对象都负责计算自己对象的攻击力即可。这样程序在运行时,会先执行最外面的装饰对象,计算攻击力,然后在继续计算被它装饰对象的攻击力,这样就达到了我们想要的效果了,也就是动态的为对象添加相应的职责了。那我们怎么保证,不同的对象可以彼此被装饰呢?所以,在使用装饰者模式有几点需要特殊的注意。下面是几点特殊的地方。

  • 装饰者和被装饰者对象有相同的超类型
  • 可以用一个或多个装饰者包装一个对象
  • 对象可以在任何时候被装饰,可以重复使用任意装饰者

下面我们将代码修改为装饰者模式,下面为具体的代码:

这样我们就将代码修改为装饰者模式的代码了,如果我们要新添加宠物装备时,我们只要新创建一个宠物类,并让该类继承装备类GameEquipment即可,并且我们并不需要修改曾经已经编写好的代码,我们只是用了类的扩展而已。下面为具体的代码。

这就是装饰者模式的好处,非常方便的扩展。但它也有相应的缺点就是会造成设计时有很多个类,因为任何一种装饰都需要定义一种类,如果我们需要更多的装备时时,那我们就要创建更多装备的装饰类,如果还有其它业务处理的话,我们又会创建符合这种需求的装饰类。这就会造成一个简单的工具类,会依赖很多个类,造成使用这个工具类的困惑。就像Java IO一样,大家都知道IO里的类其在是大多了,多到很多类压根就没有用过 ,就是因为java io 也是采用了这种装饰者模式,所以它需要为每一种可能有的需求都创建相应的装饰类,来达到装饰的目的。这也是装饰者模式的弊端。但同时也是装饰者模式的好处,也就是方便扩展,如果我们要想扩展Java IO中某些类的功能,那我们只需要创建一个新的装饰者就可以了,这样在程序中,我们就可以用我们自己创建的装饰类,来装饰Java IO中的对象了。

在Java IO中FilterInputStream类是一个抽象装饰者,我们我们要实现自己的装饰对象,那我们需要继承FilterInputStream类。具体代码如下:

直接读取txt.txt内容:

现在我们新创建了一个新的装饰类UpperCaseInputStream,也就是用这个装饰类装饰的对象,获取出的内容都会将自动转成大写字母。下面为测试用例。

这样我们就可以直接用这个UpperCaseInputStream装饰类去装饰任何FilterInputStream的类了。这也就是装饰模式的好处。

本文分享自微信公众号 - 吉林乌拉(jilinwulacom)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-09-21

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 采用Hibernate框架的研发平台如何能够真正兼容Oracle和sqlServer数据库

      都说Hibernate框架的使用可以很容易的让你的研发平台支持多种不同类型的数据库,但实践表明,这里的“容易”,是相对的。

    程序员一一涤生
  • Jenkins 解决Jenkins下java无法运行slave-agent jnlp程序连接Windows Slave主机

    java下载地址:http://www.oracle.com/technetwork/cn/java/javase/downloads/index.html

    授客
  • 如何获取hibernate代理类代理的实际对象实例?

    在hibernate中,通过sql语句查询带clob字段的记录,查出来的结果集是List<HashMap<String,Object>>类型,在调用jackso...

    程序员一一涤生
  • Linux下JDK安装笔记

    Linux版本: CentOS6.2 JDK:jdk-7u60-linux-x64.tar.gz

    程序员一一涤生
  • java动态编译类文件并加载到内存中

      如果你想在动态编译并加载了class后,能够用hibernate的数据访问接口以面向对象的方式来操作该class类,请参考这篇博文-http://www.c...

    程序员一一涤生
  • 讨论一下hibernate如何动态注册一个动态生成的实体类

      如何动态生成实体类请参考这篇博文:http://www.cnblogs.com/anai/p/4269858.html

    程序员一一涤生
  • renren-fast后端源码参考-配置和对应工具

    其中gson对象是来自qiniu-java-sdk,不需要的可以剔除或者一般国内就用fastjson

    老梁
  • tomcat下jsp要加工程名后缀才能访问的问题解决

    今天发现一个部署的项目,在tomcat中配置了去掉工程名,直接通过域名访问。配置后其它的html、动态请求等都可以不带工程名访问,但是只要访问jsp页面就报40...

    程序员一一涤生
  • Hibernate saveOrUpdate方法到底是怎么执行的?

      saveOrUpdate方法,如果传入的对象有主键就执行更新,没有就执行新增。这句话误导了很多人。

    程序员一一涤生
  • Lambda 表达式

    Java Lambda 表达式是一种匿名函数;它是没有声明的方法,即没有访问修饰符、返回值声明和名字。

    希希里之海

扫码关注云+社区

领取腾讯云代金券