分享
 
 
 

设计模式实践(抽象工厂模式应用)—通信录的优化过程

王朝other·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

通信录的需求

1. 实现功能:

a) 联系人信息,包括:姓名、电话、手机等3项。(数据库中包含的其它信息暂不加入)

b) 向数据库中添加联系人信息,并能修改联系人信息。

c) 从显示列表中查看联系人信息。

2. 界面要求:

a) 主界面,主体为显示所有联系人信息的列表,菜单上分为:文件-->新建联系人,查看详细,退出。

b) 新建和修改界面,当点击菜单中的“新建联系人”项或“查看详细”时弹出,主体为“姓名”、“电话”“手机”3个输入框与相应的Label,下方为“确定”与“取消”两个按钮。当以修改界面出现时,“姓名”、“电话”“手机”3个输入框中显示相应的联系人的信息。当按下“确定”按钮时,本界面退出,主界面对新建或修改的联系人进行显示。按下“取消”键则不做变化。

如图所示:

主界面 新建/修改信息

未优化的设计

按照未优化设计,程序的UML类图如下所示:

从以上类图可以看出以下特点:

1. 界面类知道整个程序的所有内容,包括处理逻辑、数据库调用方法、数据库类型与结构等,并完成所有操作,包括数据采集、处理与结果显示;

2. 操作与数据库耦合紧密,编程时必须清楚数据库编程方法,如果数据库发生改变,程序所做的修改将很大;

3. 重用的界面必须清楚自己当前处于何种状态,即每进行一次操作时,都需要对标志位进行一次检查;

4. 如果界面发生大的改变,几乎所有的代码都要重写。

以上特点的解释是:

1. 主界面FormMain负责以下工作:

l FormMain_Load()-LoadData(): 从数据库中取出当前已存在的联系人信息并显示在界面的列表上;

l miNewPerson_Click()-LoadData(): 用户选择新建时,显示新建界面,并在该界面操作完成时根据返回值决定是否从数据库中取出联系人,在界面上重新显示。

l miDetail_Click()-LoadData(): 用户选择更新时,显示更新界面,并将联系人的ID值传给它,当该界面退出时根据返回值决定是否从数据库中取出联系人,在界面上重新显示。

新建修改界面FormInput负责以下工作:

l FormInput()-LoadData(): 根据传来的标志值决定本界面是新建界面还是修改界面,如果是修改界面,从数据库中取出对应于联系人ID的完整信息,显示在界面上;

l cmdAdd_Click(): 根据标志位的值决定是调用新建方法Add()还是调用更新方法UpdateData();

l Add()-IsDataValidate()-Execute(): 从界面上获得输入的联系人的信息,进行验证后存入到数据库中,使界面退出;

l UpdateData()-IsDataValidate()-Execute(): 从界面获得修改后的联系人的信息,进行验证后存入到数据库中,使界面退出;

从以上功能可以看出,界面类必须知道所有程序的细节,不然无法完成操作。

2. 所有涉及数据库的操作都是直接操作,要负责连接与断开数据库,以及处理数据库异常,程序员的负担很重。如果数据库发生改变,程序员因为无法准确地定位修改点,必须在很长的界面代码中进行遍历,工作量很大。

3. 新建修改界面FormInput在初始化获得标志位,但进行操作时还要检查这个标志位,如果这种检查项很多,会造成很多的类似代码。

4. 如果界面发生改变,特别是转换界面风格时,所有的代码都要重新移植,工作量大。

由以上的分析可以看出,这样的设计造成程序的可扩展性差,维护的代价大。不是一个好的设计,需要进行优化。

优化设计

我们根据以下原则对设计进行优化:

第一条原则:界面类进行操作,只负责输入的收集与操作结果的显示。

第二条原则:输入输出相同,内容功能不同的操作要进行抽象,并由工厂模式进行选择。

第三条原则:操作逻辑控制与具体操作分开。

第四条原则:配置信息与数据实体独立出来。

根据第一条原则,可以将FormMain与FormInput的界面与操作分开,所有界面的事件都交给各自的事件处理类进行处理,两个事件处理类为MainFormEventHandler和FormInsUpdEventHandler。界面中只保留对信息的收集与处理结果显示的代码,其余的代码都放入对应的事件处理类中。这样FormMain类中只保留Form_Load(), Add_Click(), Update_Click()等3个方法,FormInsUpd类中只保留FormInsUpd(), Form_Load(), OK_Click(), Cancel_Click()等4个方法。

根据第二条原则,当新建/修改界面进行新建或修改操作时,都是输入联系人的信息,返回该操作是否成功的bool值,因此Add()和UpdateData()可以进行抽象,抽象出接口IInsUpd,内含方法Execute()的定义,由类ExecuteInset与类ExecuteUpdate实现该接口,并由其工厂类InsUpdFactory决定具体返回哪个实现类的实例。

根据第三条原则,接口IInsUpd的实现类不需要直接执行数据库操作,只需要规定数据库操作的逻辑,即访问哪个数据库,访问哪个表以及做何种操作(插入还是删除?)等,直接执行数据库操作的类是OleDbInsUpd。

根据第四条原则,将联系人的信息组装成Person类,另有规定新建/修改界面类型的枚举类ExeType,并将连接字符串放入DataConfig类中。

此外,我们还有进行验证数据有效性的类ValidatePerson和进行界面初始化时对界面信息进行填充的类Select,我们还将Main函数放入专门的程序入口类Enter中以减小可执行文件的尺寸。

通过以上的工作,我们基本完成了对通信录的优化。UML类图如下:

进一步优化

从一面的类图中,我们可以发现

1. Select类自己既规定逻辑又直接操作数据库,不符合第三条原则,因此需要将它的功能分开,将数据库操作功能合并到类OleDbInsUpdDel类中;

2. ExecuteInsert与ExecuteUpdate都要使用ValidatePerson类的校验方法IsValidateData(),为避免零碎,应该将该方法合并到它们的父类上去,但接口没有实现,只能在接口中规定该方法,再只能用一个抽象类实现该方法,再由ExecuteInsert与ExecuteUpdate类去继承抽象类。

针对这两个发现的问题,对类又做了修改,并添加了构件信息后,得到如下UML类图:

从上面的图上可以清晰地看出,整个程序的结构分为6层,即:

界面层,包括FormMain1, FormInsUpd;

界面事件处理层,包括MainFormEventHandler, FormInsUpd;

工厂层:包括IInsUpd, InsUpd, InsUpdFactory;

逻辑层:包括Select, ExecuteInsert, ExecuteUpdate;

执行层:包括OleDbExecute;

配置层:包括ExeType, DataConfig, Person;

可以归纳各层的功能为:

界面层:想要干

界面事件处理层:负责干

工厂层:由谁干

逻辑层:怎么干

执行层:我来干

配置层:工作时用到的参数

扩展的实现

检验程序是否具有良好的扩展性是看是否符合开闭原则,即对扩展开放,对修改关闭。

优化设计后,我们可以很容易地对程序做如下扩展:

1. 替换界面。例如要替换主界面,只需将MainFormEventHandler作为替换后主界面的字段,即可获得原有主界面的功能,移植很方便。

2. 扩展界面功能。例如要扩展新建/修改界面,增加一个删除功能,从图上可以看出界面的状态由枚举类ExeType决定,而由新建/修改界面衍生的并依赖ExeType类的类只有FormInsUpd和InsUpdFactory,而FormInsUpd只是传递ExeType值,并不做实际操作,只有InsUpdFactory需要明白ExeType值的意义,所以扩展点就在这里。实际扩展时,只要再从InsUpd抽象类继承一个ExecuteDel类完成删除功能,再在InsUpdFactory中增加决定代码即可。

3. 增加或减少数据库的字段。与数据库字段相关的类是Person类,如果要改变字段,只需查找与Person相关的类即可准确定位需要修改的代码的位置,且修改的代码不会对其它代码造成影响。

重用的实现

从上面的图上可以看出,不依赖其它类的类有:

l 接口IInsUpd;

l 枚举类型ExeType;

l 数据库配置类DataConfig;

l 联系人描述类Person;

l 数据库操作执行类OleDbExecute。

这些类只被别的类使用,自己却没有使用别的类,因此它们的独立性较强,但进一步分析后,发现:

l 枚举类型ExeType依赖于本程序界面类型共用的种类;

l 数据库配置类DataConfig依赖于本程序所有使用的数据库的种类与数据库名;

l 联系人描述类Person依赖于数据为对联系人信息所做的规定。

因此这三个类并不是完全自由的,无法脱离本程序。而接口IInsUpd与类OleDbExecute则彻底与本程序无关联。这样在以后的数据库操作中,我们就可以重用接口IInsUpd与类OleDbExecute。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有