装饰者模式(Decorator Pattern)允许向一个现有的对象扩展新的功能,同时不改变其结构。主要解决直接继承下因功能的不断横向扩展导致子类膨胀的问题,无需考虑子类的维护。,装饰者模式有4种角色:,抽象构件角色(Component):具体构件类和抽象装饰者类的共同父类。,具体构件角色(ConcreteComponent):抽象构件的子类,装饰者类可以给它增加额外的职责。,装饰角色(Decorator):抽象构件的子类,具体装饰类的父类,用于给具体构件增加职责,但在子类中实现。,具体装饰角色(ConcreteDecorator):具体装饰类,定义了一些新的行为,向构件类添加新的特性。,
,
,当然,如果需要扩展更多功能的话,可以再定义其他的ConcreteDecorator类,实现其他的扩展功能。如果只有一个ConcreteDecorator类,那么就没有必要建立一个单独的Decorator类,而可以把Decorator和ConcreteDecorator的责任合并成一个类。,如风之前在一家保险公司干过一段时间。其中保险业务员也会在自家产品注册账号,进行推销。不过在这之前,他们需要经过培训,导入一张展业资格证书。然后再去推销保险产品供用户下单,自己则通过推销产生的业绩,参与分润,拿对应的佣金。,对于上面导证书这个场景,实际上是会根据不同的保险产品,导入不同的证书的。并且证书的类型也不同,对应的解析、校验、执行的业务场景都是不同的。如何去实现呢?当然if-else确实也是一种不错的选择。下面放一段伪代码,从上面的伪代码看到,所有的业务逻辑是在一起处理的,通过productCode去处理对应产品的相关逻辑。这么一看,好像也没毛病,但是还是被技术大佬给否决了。好吧,如风决定重写。运用装饰者模式,重新处理下了下这段代码。1、一切再从注解出发,自定义Decorate注解,这里定义2个属性,scene和type,2、抽象构件接口,BaseHandler,这个是必须滴,3、抽象装饰器类,AbstractHandler,持有一个被装饰类的引用,这个引用具体在运行时被指定,4、具体的装饰器类AProductServiceDecorate,主要负责处理“导师证书”这个业务场景下,A产品相关的导入逻辑,并且标记了自定义注解Decorate,表示该类是装饰器类。主要负责对A产品证书导入之前逻辑的增强,我们这里称之为“装饰”。,当然,还是其他装饰类,BProductServiceDecorate,CProductServiceDecorate等等,负责装饰其他产品,这里就不举例了。,5、当然还有管理装饰器类的装饰器类管理器DecorateManager,内部维护一个map,负责存放具体的装饰器类,6、用了springboot,当然需要将这个管理器交给spring的bean容器去管理,需要创建一个配置类DecorateAutoConfiguration,7、被装饰的service类,CertificateService,只需要关注自己的核心逻辑就可以,8、在原来的controller中,注入管理器类DecorateManager去调用,以及service,也就是被装饰的类。首先拿到装饰器,然后再通过setService方法,传入被装饰的service。也就是具体装饰什么类,需要在运行时才确定。,下面模拟下代理人导入证书的流程,当选择A产品,productCode传A过来,后端的处理流程。,
,当选择B产品,productCode传A过来,后端的处理流程。,
,最后说一句,既然都用springboot了,这块可以写一个starter,做一个公用的装饰器模式。如果哪个服务需要用到,依赖这个装饰器的starter,然后标记Decorate注解,定义对应的scene和type属性,就可以直接使用了。,来看下IO流中,InputStream、FilterInputStream、FileInputStream、BufferedInputStream的一段代码,再来看下这几个类的类图 ,
, 这些类的代码有删改,可以看到BufferedInputStream中定义了很多属性,这些数据都是为了可缓冲读取来作准备的,看到其有构造方法会传入一个InputStream的实例。实际编码如下,这里觉得很眼熟吧,其实已经运用了装饰模式了。,在mybatis中,有个接口Executor,顾名思义这个接口是个执行器,它底下有许多实现类,如CachingExecutor、SimpleExecutor、BaseExecutor等等。类图如下: ,
,主要看下CachingExecutor类,看着很眼熟,很标准的装饰器。其中该类中的update是装饰方法,在调用真正update方法之前,会执行刷新本地缓存的方法,对原来的update做增强和扩展。,再来看下BaseExecutor类,这里有一个update方法,这个是原本的被装饰的update方法。然后再看这个原本的update方法,它调用的doUpdate方法是个抽象方法,用protected修饰。咦,这不就是模板方法么,关于模板方法模式,这里就不展开赘述了。,通过组合而非继承的方式,动态地扩展一个对象的功能,在运行时可以选择不同的装饰器从而实现不同的功能。,有效的避免了使用继承的方式扩展对象功能而带来的灵活性差、子类无限制扩张的问题。,具体组件类与具体装饰类可以独立变化,用户可以根据需要新增具体组件类跟装饰类,在使用时在对其进行组合,原有代码无须改变,符合"开闭原则"。,这种比继承更加灵活机动的特性,也同时意味着更加多的复杂性。,装饰模式会导致设计中出现许多小类 (I/O 类中就是这样),如果过度使用,会使程序变得很复杂。,装饰模式是针对抽象组件(Component)类型编程。但是,如果你要针对具体组件编程时,就应该重新思考你的应用架构,以及装饰者是否合适。,编程文档: https://gitee.com/cicadasmile/butte-java-note,应用仓库: https://gitee.com/cicadasmile/butte-flyer-parent,
© 版权声明
文章版权归作者所有,未经允许请勿转载。