装饰器模式 Decorator 结构型 设计模式 (十)

通过手抓饼的例子,深入浅出地讲解了装饰器模式的原理与应用,探讨了其在解决类爆炸问题上的优势及替代继承的场景。

引子

 
image_5b91e046_4d0a
 
image_5b91e046_bbf
 
 

 

现实世界的装饰器模式

大家应该都吃过手抓饼,本文装饰器模式以手抓饼为模型展开简介
"老板,来一个手抓饼,  加个培根,  加个鸡蛋,多少钱?"
这句话会不会很耳熟,或者自己可能都说过呢?
 
我们看看这句话到底表达了哪些含义呢?
你应该可以看得到这两个基本角色
1.手抓饼                                 核心角色
2.配菜(鸡蛋/培根/香肠...)          装饰器角色
 

你既然想要吃手抓饼,自然你是奔着手抓饼去的,对吧
所以,你肯定会要一个手抓饼,至少是原味的
然后可能根据你的口味或者喜好添加更多的配菜
这个行为很自然,也很正常.
 
如果是在代码的世界里面,你怎么描述:  顾客 购买 手抓饼     这一行为呢?  
顾客Customer   顾客有方法buy  然后有一个手抓饼HandPancake,看起来是这样子的
那么问题来了
如何表示 加了鸡蛋的手抓饼,或者加了鸡蛋和培根的手抓饼呢?
 
一种很可能方式是把他们都当成手抓饼的不同种类,也就是使用继承或者说实现类的形式
那么我们有多少种手抓饼呢?
原味手抓饼/加鸡蛋手抓饼/加鸡蛋加培根手抓饼/加鸡蛋加烤肠手抓饼/加鸡蛋加培根加烤肠手抓饼手抓饼/.......
很显然,这就是数学中的组合,最终的个数跟我们到底有多少种配菜有关系
如果按照这种思维方式,我们将会有无数个手抓饼类,而且如果以后多了一种配菜,类的个数将会呈现爆炸式的增长
这是你想要的结果么?
 
在现实世界里面,你会很自然的说 "老板,来一个手抓饼,  加个培根,  加个鸡蛋,多少钱?""
那么为什么在程序世界里面,你却很可能说"老板,给我来一个加了鸡蛋加了培根的那种手抓饼" 呢?
 
 

手抓饼代码示例

手抓饼接口和具体的一家店铺提供的手抓饼
package decorator;
/**
* Created by noteless on 2018/9/6.
* Description:手抓饼接口 描述抽象的手抓饼
*/
public interface HandPancake {
/**
* 提供手抓饼
*/
String offerHandPancake();
/**计算手抓饼的价格
* @return
*/
Integer calcCost();
}

package decorator;
/**
* Created by noteless on 2018/9/6.
* Description: Noteless 家的手抓饼
*/
public class NotelessHandPancake implements HandPancake {
/**
* 提供noteless 家的手抓饼一份
*/
@Override
public String offerHandPancake() {
return " noteless 家的手抓饼";
}
/**计算 noteless 家 一份手抓饼的价格
* @return
*/
@Override
public Integer calcCost() {
return 3;
}
}
配菜抽象类(装饰器)
package decorator;
/**
* Created by noteless on 2018/9/6.
* Description:装饰器类实现了手抓饼接口,具有了手抓饼的类型
*/
public abstract class Decorator implements HandPancake{
private HandPancake handPancake;
Decorator(HandPancake handPancake){
this.handPancake = handPancake;
}
/**提供手抓饼
* @return
*/
@Override
public String offerHandPancake() {
return handPancake.offerHandPancake();
}

/**提供手抓饼的价格
* @return
*/
@Override
public Integer calcCost() {
return handPancake.calcCost();
}
}
具体的配菜(具体的装饰)
package decorator;
/**
* Created by noteless on 2018/9/6.
* Description:培根
*/
public class Bacon extends Decorator {
    Bacon(HandPancake handPancake){
        super(handPancake);
    }

    @Override
    public String offerHandPancake() {
        return super.offerHandPancake()+" 加培根";
    }
    @Override
    public Integer calcCost() {
        return super.calcCost()+4;
    }
}

package decorator;
/**
* Created by noteless on 2018/9/6.
* Description:鸡蛋
*/
public class Egg extends Decorator {
    Egg(HandPancake handPancake){
        super(handPancake);
    }
    @Override
    public String offerHandPancake() {
        return super.offerHandPancake()+"加鸡蛋";
    }
    @Override
    public Integer calcCost() {
        return super.calcCost()+2;
    }
}

package decorator;
/**
* Created by noteless on 2018/9/6.
* Description:烤肠
*/
public class Sausage extends Decorator {
    Sausage(HandPancake handPancake){
        super(handPancake);
    }
    @Override
    public String offerHandPancake() {
        return super.offerHandPancake()+" 加香肠";
    }
    @Override
    public Integer calcCost() {
        return super.calcCost()+3;
    }
}

package decorator;
/**
* Created by noteless on 2018/9/6.
* Description:青菜
*/
public class Vegetable extends Decorator {
    Vegetable(HandPancake handPancake){
        super(handPancake);
    }
    @Override
    public String offerHandPancake() {
        return super.offerHandPancake()+" 加青菜";
    }
    @Override
    public Integer calcCost() {
        return super.calcCost()+1;
    }

}
顾客
package decorator;
/**
* Created by noteless on 2018/9/6.
* Description:顾客具有名字,然后购买手抓饼
*/
public class Customer {
private String name;
Customer(String name){
this.name = name;
}

public void buy(HandPancake handPancake){
  System.out.println(name+"购买了 : "+handPancake.offerHandPancake()+
  " 一份, 花了 : "+handPancake.calcCost()+"块钱~");
  System.out.println();
}
}
测试类
package decorator;

/**
* Created by noteless on 2018/9/6.
* Description:
* 手抓饼3块
* Sausage 烤肠 3块
* Bacon 培根 4块
* Egg 鸡蛋2块
* Vegetable 青菜 1块
*/

public class Test {
public static void main(String ...strings){

//有一个顾客张三,他想吃手抓饼了,来了一个原味的
Customer customerA = new Customer("张三");
customerA.buy(new NotelessHandPancake());

//有一个顾客李四,他想吃手抓饼了,他加了一根烤肠
Customer customerB = new Customer("李四");
customerB.buy(new Sausage(new NotelessHandPancake()));

//有一个顾客王五,他想吃手抓饼了,他加了一根烤肠 又加了培根
Customer customerC = new Customer("王五");
customerC.buy(new Bacon(new Sausage(new NotelessHandPancake())));

//有一个顾客王五的兄弟,他想吃手抓饼了,他加了培根 又加了烤肠
Customer customerC1 = new Customer("王五的兄弟");
customerC1.buy(new Sausage(new Bacon(new NotelessHandPancake())));

//有一个顾客赵六,他想吃手抓饼了,他加了一根烤肠 又加了2份培根
Customer customerD = new Customer("赵六");
customerD.buy(new Bacon(new Bacon(new Sausage(new NotelessHandPancake()))));
//有一个顾客 王二麻子,他想吃手抓饼了,特别喜欢吃青菜 来了三分青菜 Customer customerE = new Customer("王二麻子"); customerE.buy(new Vegetable(new Vegetable(new Vegetable(new NotelessHandPancake()))));
//有一个顾客 有钱人 王大富 来了一个全套的手抓饼 Customer customerF = new Customer("王大富"); customerF.buy(new Egg(new Vegetable(new Bacon(new Sausage(new NotelessHandPancake()))))); } }
我们有一个顾客Customer类,他拥有buy方法,可以购买手抓饼
手抓饼接口为 HandPancake  具体的手抓饼为NotelessHandPancake
然后提供了一个配菜类,这个配菜类的行为和手抓饼是一致的,在提供手抓饼的同时还能够增加一些额外的
然后还有四个具体的配菜 培根 香肠 鸡蛋 青菜
 
运行测试类,会算账的亲们,看看单价是否还对的上?
image_5b91e046_7b49
UML图
懒得画了,IDEA自动生成的
image_5b91e046_739c
 
 手抓饼装饰器模式中的根本
上面的代码还是比较清晰的,如果你没办法仔细看进去的话,我们换一种思维方式来思考手抓饼的装饰器模式
 
你可以这么理解:
你过去手抓饼的摊位那边,你说老板来一个手抓饼,加培根,加鸡蛋
 
摊主那边是这样子的:
老板负责直接做手抓饼
旁边站着漂亮的老板娘,手里拿着手抓饼的袋子,负责帮你装袋,你总不能直接用手拿饼,对吧
 
接下来我们说下过程:
老板马上就开始做手抓饼了,做好了之后,老板把手抓饼交给了旁边站着的老板娘
老板娘在给装袋并且交给你之前
把鸡蛋和培根放到了你的手抓饼里面
然后又放到了包装袋子里面
接着递给了你
 
你说到底是老板娘手里包装好的手抓饼是手抓饼  还是老板做好的热气腾腾的是手抓饼呢?
 
其实,老板做好的热气腾腾的手抓饼,正是我们上面提供出来的具体的手抓饼
老板娘手里拿着的手抓饼包装袋来包装手抓饼,也是手抓饼,只不过是包装了下,这个就是装饰器的概念
 
所以装饰器模式还有一个名字  包装器模式(Wrapper)
 
 
解决问题的根本思路是使用组合替代了继承
上面我们也进行了分析,继承会出现类的个数的爆炸式增长
组合,不仅仅动态扩展了类的功能,而且还很大程度上减少了类的个数
不过显然,如果你的装饰类过多,虽说比继承好很多,但是问题还是一样的,都会类过多
 
根本:  是你还有你
 
我们上面的类的结构中,装饰器包含一个手抓饼对象作为属性,他也实现了手抓饼接口
所以我们说,是你还有你
每次自己返回结果之前,都还会调用自己含有的对象的方法
 
看下调用流程, 你说它的形式跟 递归调用有什么区别?
image_5b91e047_17fb
 
 

面向对象中的适配器模式详解

意图
动态的给一个对象添加额外的职责,简单说,动态的扩展职责
就增加功能来说,装饰器模式比生成子类要更加灵活
所以装饰器模式主要解决继承子类爆炸增长的问题
 

装饰器模式中的角色

Component 抽象构建 装饰器模式中必然有一个最基本最原始的->
接口/抽象类
来充当抽象构建
抽象的手抓饼    HandPancake
ConcreteComponent 具体构建 
是抽象构建的一个具体实现
你要装饰的就是它
具体某家店铺生产的手抓饼   NotelessHandPancake
Decorator 装饰抽象类 一般是一个抽象类
实现抽象构建
并且必然有一个private变量指向Component 抽象构建
配菜抽象类(装饰器)   Decorator
ConcreteDecorator 具体的装饰类 必须要有具体的装饰角色
否则装饰模式就毫无意义了
具体的配菜(具体的装饰)    Bacon Egg  Vegetable Sausage
 
image_5b91e047_1c47
仔细体味下<是你 还有你>
Decorator 是Component 还有Component
 
OOP中的一个重要设计原则
类应该对扩展开放,对修改关闭
所谓修改就是指继承,一旦继承,那么将会对部分源代码具有修改的能力,比如覆盖方法,所以你尽量不要做这件事情
扩展就是指的组合,组合不会改变任何已有代码,动态得扩展功能
 

装饰器模式优点

装饰类和被装饰类可以独立发展,而不会相互耦合
 
Component类无须知道Decorator类,Decorator类是从外部来扩展Component类的功能,
而Decorator也不用知道具体的构件
装饰模式是继承关系的一个替代方案
我们看装饰类Decorator,不管装饰多少层,他始终是一个Component,实现的还是is-a的关系,所以他是继承的一种良好替代方案
如果设计得当,装饰器类的嵌套顺序可以任意,比如
image_5b91e047_2ed8
一定要注意前提,那就是你的装饰不依赖顺序

装饰器模式缺点

装饰器模式虽然从数量级上减少了类的数量,但是为了要装饰,仍旧会增加很多的小类
这些具体的装饰类的逻辑将不会非常的清晰,不够直观,容易令人迷惑
装饰器模式虽然减少了类的爆炸,但是在使用的时候,你就可能需要更多的对象来表示继承关系中的一个对象
多层的装饰是比较复杂,比如查找问题时,被层层嵌套,不容易发现问题所在
 

装饰器模式使用场景

当你想要给一个类增加功能,然而,却并不想修改原来类的代码时,可以考虑装饰器模式
如果你想要动态的给一个类增加功能,并且这个功能你还希望可以动态的撤销,就好像直接拿掉了一层装饰物

装饰器模式的简化变形

装饰器模式是对继承的一种强有力的补充与替代方案,装饰器模式具有良好的扩展性
再次强调,设计模式是一种思维模式,没有固定公式
如果需要的话,可以进行简化
如果省略抽象构建,装饰器直接装饰一个类的话,
那么可以装饰器直接继承这个类

image_5b91e047_7db9
如果只有一个具体的装饰器类,那么可以省略掉 Decorator
ConcreteDecorator 充当了ConcreteDecorator 和 Decorator的角色
image_5b91e047_e8e 
 
 
设计模式是作为解决问题或者设计类层级结构时的一种思维的存在,而不是公式一样的存在!
 
 
 
多源动态最优潮流的分布鲁棒优化方法(IEEE118节点)(Matlab代码实现)内容概要:本文介绍了基于Matlab实现的多源动态最优潮流的分布鲁棒优化方法,适用于IEEE118节点电力系统。该方法旨在应对电力系统中源荷不确定性带来的挑战,通过构建分布鲁棒优化模型,有效处理多源输入下的动态最优潮流问题,提升系统运行的安全性和经济性。文中详细阐述了模型的数学 formulation、求解算法及仿真验证过程,并提供了完整的Matlab代码实现,便于读者复现与应用。该研究属于电力系统优化调度领域的高水平技术复现,具有较强的工程实用价值。; 适合人群:具备电力系统基础知识和Matlab编程能力的研究生、科研人员及从事电力系统优化调度的工程技术人员,尤其适合致力于智能电网、鲁棒优化、能源调度等领域研究的专业人士。; 使用场景及目标:①用于电力系统多源环境下动态最优潮流的建模与求解;②支撑含可再生能源接入的电网调度决策;③作为鲁棒优化方法在实际电力系统中应用的教学与科研案例;④为IEEE118节点系统的仿真研究提供可复现的技术支持。; 阅读建议:建议结合提供的Matlab代码逐模块分析,重点关注不确定变量的分布鲁棒建模、目标函数构造及求解器调用方式。读者应具备一定的凸优化和电力系统分析基础,推荐配合YALMIP工具包与主流求解器(如CPLEX、Gurobi)进行调试与扩展实验。
内容概要:本文系统介绍了物联网与云计算的基本概念、发展历程、技术架构、应用场景及产业生态。文章阐述了物联网作为未来互联网的重要组成部分,通过RFID、传感器网络、M2M通信等技术实现物理世界与虚拟世界的深度融合,并展示了其在智能交通、医疗保健、能源管理、环境监测等多个领域的实际应用案例。同时,文章强调云计算作为物联网的支撑平台,能够有效应对海量数据处理、资源弹性调度和绿色节能等挑战,推动物联网规模化发展。文中还详细分析了物联网的体系结构、标准化进展(如IEEE 1888、ITU-T、ISO/IEC等)、关键技术(中间件、QoS、路由协议)以及中国运营商在M2M业务中的实践。; 适合人群:从事物联网、云计算、通信网络及相关信息技术领域的研究人员、工程师、高校师生以及政策制定者。; 使用场景及目标:①了解物联网与云计算的技术融合路径及其在各行业的落地模式;②掌握物联网体系结构、标准协议与关键技术实现;③为智慧城市、工业互联网、智能物流等应用提供技术参考与方案设计依据;④指导企业和政府在物联网战略布局中的技术选型与生态构建。; 阅读建议:本文内容详实、覆盖面广,建议结合具体应用场景深入研读,关注技术标准与产业协同发展趋势,同时结合云计算平台实践,理解其对物联网数据处理与服务能力的支撑作用。
标题基于Java的停车场管理系统设计与实现研究AI更换标题第1章引言介绍停车场管理系统研究背景、意义,分析国内外现状,阐述论文方法与创新点。1.1研究背景与意义分析传统停车场管理问题,说明基于Java系统开发的重要性。1.2国内外研究现状综述国内外停车场管理系统的发展现状及技术特点。1.3研究方法以及创新点介绍本文采用的研究方法以及系统开发中的创新点。第2章相关理论总结Java技术及停车场管理相关理论,为系统开发奠定基础。2.1Java编程语言特性阐述Java的面向对象、跨平台等特性及其在系统开发中的应用。2.2数据库管理理论介绍数据库设计原则、SQL语言及在系统中的数据存储与管理。2.3软件工程理论说明软件开发生命周期、设计模式在系统开发中的运用。第3章基于Java的停车场管理系统设计详细介绍系统的整体架构、功能模块及数据库设计方案。3.1系统架构设计阐述系统的层次结构、模块划分及模块间交互方式。3.2功能模块设计介绍车辆进出管理、车位管理、计费管理等核心功能模块设计。3.3数据库设计给出数据库表结构、字段设计及数据关系图。第4章系统实现与测试系统实现过程,包括开发环境、关键代码及测试方法。4.1开发环境与工具介绍系统开发所使用的Java开发环境、数据库管理系统等工具。4.2关键代码实现展示系统核心功能的部分关键代码及实现逻辑。4.3系统测试方法与结果阐述系统测试方法,包括单元测试、集成测试等,并展示测试结果。第5章研究结果与分析呈现系统运行效果,分析系统性能、稳定性及用户满意度。5.1系统运行效果展示通过截图或视频展示系统实际操作流程及界面效果。5.2系统性能分析从响应时间、吞吐量等指标分析系统性能。5.3用户满意度调查通过问卷调查等方式收集用户反馈,分析用户满意度。第6章结论与展望总结研究成果,提出系统改进方向及未来发展趋势。6.1研究结论概括基于Java的停车场管理
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值