课程名称:程序设计方法学
实验4:OOP设计模式-结构型模式的应用与实现
时间:2015年11月18日星期三,第3、4节 地点:理1#208
一、实验目的
加深对结构型设计模式的理解以及在开发中的实际应用能力。
二、实验内容
众所周知,开店的申请手续是很繁琐的。以个体工商户开一家小餐馆为例,首先要拿身份证原件及复印件到当地工商所登记名称,然后凭登记的名称分别到辖区内的环保部门和卫生监督所去申领排污许可证和卫生许可证。拿到这两个证后,再凭这两个证及相应的房屋租赁证明、身份证,去工商所申请领取工商营业执照。此外,在开业之前,还需要向消防部门进行消防审批,向当地税务局申请领取地税税务登记号。这些步骤中,有些有严格的先后顺序,有些则是可以同时进行的。此外,根据所开店铺的具体类型,可能还有一些额外的审批手续要办。(比如开药店,还需要向当地的药监局申领药品经营许可证)。
针对以上说明,现在要求你设计一个开店审批工作流管理系统,实现对上述审批流程的自动化管理。要求使该系统实现审批步骤的动态组合和叠加,并具有较强的可扩展性。
请你选择适当的面向对象设计模式,使用面向对象的建模方法,给出上述系统的设计,使其在满足基本需求的前提下具有最佳的可扩展性。
要求:
实验报告中要求绘制UML类图,给出设计中各个类的主要成员,并附以适当的文字说明详细描述每个类的作用;
实验报告中应针对上述设计,给出使用C++(或java)实现的完整的示意性代码,以及在本地计算机上调试、运行该程序的截图(要求截图的结果中能体现个人的学号、姓名等信息)。
实验报告的末尾请对所用的设计模式、该模式的优缺点及使用心得等做简要小结。
附加要求:
对于我们所介绍的常见的几种结构型的设计模式,你能不能也举出这些模式的一些应用实例,并给出相关的UML类图说明呢?
三、实验环境
硬件条件:微机
操作系统:Windows 2007
开发环境:Eclipse,Rational Rose 2003
四、实验步骤和结果
(一)选择适当的面向对象设计模式
由题意,要求设计一个开店审批工作流管理系统,在开店审批的流程中,开店者需要不断的去申请证书,这就相当于给店铺不断的附加上不同的许可证书。依据开店的申请手续,各个步骤之间有些有严格的先后顺序,有些则是可以同时进行的。而且,根据所开店铺的具体类型,可能还有一些额外的审批手续要办。因此,在原有的店铺审批申请流程的基础上,为其添加装饰器组件进行具体手续的办理。故,为实现对上述审批流程的自动化管理,且使该系统实现审批步骤的动态组合和叠加、具有较强的可扩展性,则选用的是结构型设计模式中的装饰器模式。即可以使用在需要增加一些功能的排列组合而产生其他的功能。因此可以将各类许可证书看成具体的装饰角色,负责给店铺添加开店必备的证书,将店铺看成一个具体构件角色,用来接收附加给他的装饰品,即证书。
(二)UML类图的设计和绘制
设计分析:
装饰器模式是以对客户透明的方式动态的给对象附加上更多的功能,是继承关系的一个替代方案。装饰模式可以在不使用创造更多子类的情况下,将对象的功能加以扩展。装饰器模式中的角色有:
1、 抽象可视化组件(VisualComponent)角色:给出一个抽象接口,以规范准备接收附加功能的对象。
2、 具体组件角色(ConcreteCompont)角色:用来定义一个将要接收附加功能的类。
3、 装饰器(Decorato)角色:持有一个组件(VisualComponent)对象的实例,并定义一个与抽象组件接口一致的接口。
4、 具体装饰器(ConcreteDecorator)角色:负责为组件对象实现附加的功能。
(图1 一般的装饰器模式的UML类图 )
(图2该开店审批工作流管理系统的装饰器模式的UML类图)
(三)针对上述设计,用java实现的完整的示意性代码如下所示:
Java 类的建立如下所示:
1、VisualComponent接口,即可视化组件接口, 该抽象接口规范准备接收附加责任的对象。
package com.shen.decorator; //VisualComponent接口,即可视化组件接口, 该抽象接口规范准备接收附加责任的对象 public interface VisualComponent { public void Operation(); }
2、ShopApprovalProcess 开店审批工作流类,继承自父类VisualComponent可视化组件类。
package com.shen.decorator; //ShopApprovalProcess 开店审批工作流类,继承自父类VisualComponent可视化组件类 public class ShopApprovalProcess implements VisualComponent{ @Override public void Operation() { System.out.println("***欢迎来到【开店审批工作流管理系统】_105032013120***"); } }
3、Decorator装饰器类,继承自父类VisualComponent可视化组件类,用于对开店审批工作流管理系统的组件装饰作用。
package com.shen.decorator; //Decorator装饰器类,继承自父类VisualComponent可视化组件类,用于对开店审批工作流管理系统的组件装饰作用 public class Decorator implements VisualComponent{ private VisualComponent visualComponent; public Decorator(VisualComponent visualComponent) { super(); this.visualComponent = visualComponent; } @Override public void Operation() { visualComponent.Operation(); } }
4、装饰器——登记名称De_RegisterName
package com.shen.decorator; //装饰器——登记名称De_RegisterName public class De_RegisterName extends Decorator { public De_RegisterName(VisualComponent visualComponent) { super(visualComponent); } public void AppRegisterName() { super.Operation();//调用基类的默认实现(调用被装饰对象的Operator方法) System.out.println(">>>到当地工商所[登记名称]");//登记名称(添加额外工作) } }
5、装饰器——排污许可证De_PollutionLicense
package com.shen.decorator; //装饰器——排污许可证De_PollutionLicense public class De_PollutionLicense extends Decorator{ public De_PollutionLicense(VisualComponent visualComponent) { super(visualComponent); } public void AppPollution() { //super.Operation();//调用基类的默认实现(调用被装饰对象的Operator方法) System.out.println(">>>到辖区内的环保部门申领[排污许可证]");//排污许可证(添加额外工作) } }
6、装饰器——卫生许可证De_Hygienic
package com.shen.decorator; //装饰器——卫生许可证De_Hygienic public class De_Hygienic extends Decorator{ public De_Hygienic(VisualComponent visualComponent) { super(visualComponent); } public void AppHygienic() { //super.Operation();//调用基类的默认实现(调用被装饰对象的Operator方法) System.out.println(">>>到辖区内的卫生监督所去申领[卫生许可证]");//卫生许可证(添加额外工作) } }
7、装饰器——工商营业执照De_BusinessRegistration
package com.shen.decorator; //装饰器——工商营业执照De_BusinessRegistration public class De_BusinessRegistration extends Decorator{ public De_BusinessRegistration(VisualComponent visualComponent) { super(visualComponent); } public void AppBusiness() { //super.Operation();//调用基类的默认实现(调用被装饰对象的Operator方法) System.out.println(">>>到工商所申请领取[工商营业执照]");//工商营业执照(添加额外工作) } }
8、装饰器——消防审批De_FireApproval
package com.shen.decorator; //装饰器——消防审批De_FireApproval public class De_FireApproval extends Decorator{ public De_FireApproval(VisualComponent visualComponent) { super(visualComponent); } public void AppFire() { //super.Operation();//调用基类的默认实现(调用被装饰对象的Operator方法) System.out.println(">>>向消防部门申请[消防审批]");//消防审批(添加额外工作) } }
9、装饰器——地税税务登记号De_TaxRegistrationNumber
package com.shen.decorator; //装饰器——地税税务登记号De_TaxRegistrationNumber public class De_TaxRegistrationNumber extends Decorator{ public De_TaxRegistrationNumber(VisualComponent visualComponent) { super(visualComponent); } public void AppTax() { //super.Operation();//调用基类的默认实现(调用被装饰对象的Operator方法) System.out.println(">>>向当地税务局申请领取[地税税务登记号]");//地税税务登记号(添加额外工作) } }
10、装饰器——药品经验许可证De_DrugBusinessLicense
package com.shen.decorator; //装饰器——药品经验许可证De_DrugBusinessLicense public class De_DrugBusinessLicense extends Decorator{ public De_DrugBusinessLicense(VisualComponent visualComponent) { super(visualComponent); } public void AppDrug() { //super.Operation();//调用基类的默认实现(调用被装饰对象的Operator方法) System.out.println(">>>向当地的药监局申领[药品经营许可证]");//药品经营许可证(添加额外工作) } }
11、装饰器——其它额外手续De_OtherExtraProcedures
package com.shen.decorator; //装饰器——其它额外手续De_OtherExtraProcedures public class De_OtherExtraProcedures extends Decorator{ public De_OtherExtraProcedures(VisualComponent visualComponent) { super(visualComponent); } public void AppOther() { //super.Operation();//调用基类的默认实现(调用被装饰对象的Operator方法) System.out.println(">>>办理其它额外手续...... ");//其它额外手续(添加额外工作) } }
(四)编写测试代码如下所示:
1、开一家小餐馆审批工作流测试代码
package com.shen.decorator; //客户装饰器测试类 public class ClientDecoratorTest { public static void main(String[] args) { VisualComponent vc=new ShopApprovalProcess();//创建一个被装饰对象,公共接口的实例 Decorator dec=new Decorator(vc); //创建一个装饰器对象,用以实例化各种装饰器 /*------------以下为开一家小餐馆所需审批工作流-----------------*/ System.out.println("---------《以下为开一家小餐馆所需审批工作流》---------"); //登记名称 De_RegisterName dec1=new De_RegisterName(dec); dec1.AppRegisterName(); //排污许可证 De_PollutionLicense dec2=new De_PollutionLicense(dec); dec2.AppPollution(); //卫生许可证 De_Hygienic dec3=new De_Hygienic(dec); dec3.AppHygienic(); //工商营业许可证 De_BusinessRegistration dec4=new De_BusinessRegistration(dec); dec4.AppBusiness(); //消防审批 De_FireApproval dec5=new De_FireApproval(dec); dec5.AppFire(); //地税税务登记号 De_TaxRegistrationNumber dec6=new De_TaxRegistrationNumber(dec); dec6.AppTax(); //其它额外手续 De_OtherExtraProcedures dec7=new De_OtherExtraProcedures(dec); dec7.AppOther(); } }
调试程序,运行结果如图所示:
2、开一家药店审批工作流测试代码
package com.shen.decorator; //客户装饰器测试类 public class ClientDecoratorTest { public static void main(String[] args) { VisualComponent vc=new ShopApprovalProcess();//创建一个被装饰对象,公共接口的实例 Decorator dec=new Decorator(vc); //创建一个装饰器对象,用以实例化各种装饰器 /*------------以下为开一家药店所需审批工作流-----------------*/ System.out.println("---------《以下为开一家药店所需审批工作流》---------"); //登记名称 De_RegisterName dec1=new De_RegisterName(dec); dec1.AppRegisterName(); //卫生许可证 De_Hygienic dec2=new De_Hygienic(dec); dec2.AppHygienic(); //工商营业许可证 De_BusinessRegistration dec3=new De_BusinessRegistration(dec); dec3.AppBusiness(); //药品经营许可证 De_DrugBusinessLicense dec4=new De_DrugBusinessLicense(dec); dec4.AppDrug(); //消防审批 De_FireApproval dec5=new De_FireApproval(dec); dec5.AppFire(); //地税税务登记号 De_TaxRegistrationNumber dec6=new De_TaxRegistrationNumber(dec); dec6.AppTax(); //其它额外手续 De_OtherExtraProcedures dec7=new De_OtherExtraProcedures(dec); dec7.AppOther(); } }
调试程序,运行结果如图所示:
五、实验结果和讨论
(一)开一家小餐馆程序代码运行结果如下图所示:
(二)开一家药店程序代码运行结果如下图所示:
六、总结
(一)本次实验按时按量完成。通过实验基本掌握了结构型设计模式中的装饰器模式。
(二)本次实验使用的是装饰器模式,它动态地给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类更为灵活。装饰器模式在不必改变原类文件和使用继承的情况下,动态的扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
优点:
1. 装饰器模式与继承关系的目的都是要扩展对象的功能。但是装饰器模式比静态继承更灵活,装饰模式允许系统动态决定“贴上”一个需要的“装饰”,或者除掉一个不需要的“装饰”。继承关系则不同,继承关系是静态的,它在系统运行前就决定了。
2. 装饰器模式可以避免在层次结构高层的类有太多的特征。
3. 通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。
缺点:
1. Decorator与它的Component不一样。Decorator是一个透明的包装。如果我们从对象标识的观点出发,一个被装饰了的组件与这个组件是有差别的,因此,使用装饰时不应该依赖对象标识。
2. 由于使用装饰模式,可以比使用继承关系需要较少数目的类。使用较少的类,当然使设计比较易于进行。但是,在另一方面,使用装饰模式会产生比使用继承关系更多的对象。更多的对象会使得查错变得困难,特别是这些对象看上去都很相像。
(三)使用装饰器模式的心得体会:
1.装饰器模式除了采用组合的方式取得了比采用继承方式更好的效果外,它还给设计带来一种“即用即付”的方式来添加职责。在OO设计和分析经常有这一种情况:为了多态,通过父类指针指向其具体子类,但是这就带来另外一个问题,当具体子类要添加新的职责,就必须向其父类添加一个这个职责的抽象接口,否则是通过父类指针是调用不到这个方法的。这样处于高层的父类就承载了太多的特征(方法),并且继承自这个父类的所有子类都不肯避免继承了父类的这些接口,但是可能这并不是这个具体子类所需要的。而在装饰器模式提供了一种较好的解决方法,当需要添加一个操作的时候就可以通过装饰器模式来解决,也可以一步步添加新的职责。
2.装饰器模式具有比继承更加灵活机动的特性,也同时意味着更加多的复杂性。装饰模式会导致设计中出现许多小类,如果过度使用,会使程序变得很复杂。
装饰模式是针对抽象组件(Component)类型编程。但是,如果要针对具体组件编程时,就应该重新思考应用架构,以及装饰者是否合适。当然也可以改变Component接口,增加新的公开的行为,实现“半透明”的装饰者模式。在实际项目中要做出最佳选择。
七、附加要求
对于我们所介绍的常见的几种结构型的设计模式,举出这些模式的一些应用实例,并给出相关的UML类图说明。
对适配器模式(Adapter)的举例如下:
水利工程的水管有大号、小号水管之分,而水管适配器的作用是将大号水管与小号水管连接起来,以达到大号水管的水能够流到小号水管中的作用。
(一) UML类图设计如下所示:
(二) 代码设计如下所示:
1、大号水管
package com.shen.adapter; //大号水管 public class BigWaterPipe { public void Common() { System.out.println("我是大号水管"); } }
2、小号水管
package com.shen.adapter; //小号水管 public class SmallWaterPipe { public void Special() { System.out.println("我是小号水管"); } }
3、水管适配器
package com.shen.adapter; //水管适配器 public class WaterPipeAdapter extends BigWaterPipe { private SmallWaterPipe swp=new SmallWaterPipe(); public void Common() { System.out.println("我是水管适配器,使大号水管和小号水管能够连接起来。"); swp.Special(); } }
4、适配器测试类
package com.shen.adapter; //适配器测试类 public class AdapterTest { public static void main(String[] args) { System.out.println("----------105032013120----------"); BigWaterPipe bwp1=new BigWaterPipe(); bwp1.Common(); BigWaterPipe bwp2=new WaterPipeAdapter(); bwp2.Common();; } }
(三) 测试结果如下所示: