软件的可维护性和可复用性
软件工程和建模大师Peter coad认为,一个好的系统设计与应该具备如下三个性质
可扩展性
灵活性
可插入性
软件的可维护性和可复用性
软件的复用和重用拥有众多优点,如可以提高软件的开发效率,提高软件质量,节约开发成本,恰当地服用还可以改善系统发的可维护性
在面向对象设计服用的目标在于实现支持可维护性的复用
在面向对象的设计里面,可维护性复用都是以面向对象设计原则为基础的,这些设计原则首先都是复用的原则,准学这些设计原则可以有效地提高系统的复用性,同时提高系统的可维护性
面向对象设计原则和设计模式也是对系统进行合理重构的指南针,重构是在不改变软件现有功能的基础上,通过调整程序代码改善软件的质量、性能,使其程序的设计模式和架构更趋合理,提高软件的扩展性和维护性。
面向对象设计原则简介
常用的面向对象设计原则包括7个,这些原则并不是孤立存在的,他们相互依赖,相互补充。
设计原则名称 | 设计原则简介 | 重要性 |
---|---|---|
单一职责原则 | 类的职责要单一,不能将太多的职责放在一个类中 | **** |
开闭原则 | 软件实体退扩展是开发的,但对修改是关闭的,即在不修改个软件实体的基础上去扩展其功能 | ***** |
里氏代换原则 | 在软件系统中,一个可以接受基类对象的地方必然可以接受子类对象 | **** |
依赖倒转原则 | 要针对抽象层编程,而不要针对具体类编程 | ***** |
接口隔离原则 | 使用多个专门的接口来取代一个统一的接口 | ** |
合成复用原则 | 在系统中应该尽量多使用组合和聚合管理关系,尽量少使用甚至不使用继承关系 | **** |
迪米特法则 | 一个软件实体对其他实体的应用越少越好,或者说如果两个类不必必测直接通信,不那么这两个类就不应当发生直接的相互作用,而是通过引入一个第三者发生间接交互 | *** |
单一职责原则定义如下下:
一个对象应该只包含单一的职责,并且该职责被完整的封装在一个类中。
另一种定义方式如下:就一个类而言,应该仅有一个引起它变化的原因
单一职责原则分析
sunny软件公司开发人员针对某CRM(客户关系管理)系统中客户信息图形统计模块提出了如图1所示初始设计方案
CustomerData类承担了太多的职责, 即包含与数据库相关的方法,有办函与图表生成和显示相关的方法。
如果其他类中也需要连接数据库或者使用findCustiners()方法查询客户信息,则难以实现代码的重用。无论是修改数据库连接方式还是修改图标显示方式都需要修改该类,他不止一个引起他变化的原因,违背了,违背了单一职责原则。
(1) DBUtil: 负责连接数据库, 包含数据库连接方法getConnection();
(2) CustomerDAO**:** 负责操作数据库中的Customer表, 包含对Customer表的增删改查等方法, 如findCustomers();
(3) CustomerDataChart: 负责图表的生成和显示, 包含方法createChart()和displayChart()
练习
某基于java的C/S系统的登录功能通过如下登录类实现
先使用单一职责原则对其进行重构
开闭原则定义如下
一个软件实体应当对扩展开放,对修改关闭,也就是说在设计一个模块的时候,应当使这个模块可以在不被修改的前提下被扩展,即实现在不修改源代码的情况下改变这个模块的行为。
开闭原则是面向对象的可复用设计的第一会基石
任何软件都需要面临一个很重要的问题,他们的需求会随时间的推移而发生变化,当软件系统需要面对新的需求时,我们应该尽量保证系统的设计框架是稳定的。
小宋参加工作不久,买了套小房子,房间里有点灯,他希望设计一个智能控制器控制电灯的开馆
小宋后来又陆续买了很多电器,他希望智能控制器都能控制这些 电器开关
Class 控制器{
void 启动(){
灯.打开();
电视.打开();--改动点1
洗衣机.打开();--改动点4
计算机.打开();--改动点7
}
void 停止 (){
灯.关闭();
电视.关闭();--改动点2
洗衣机.关闭();--改动点5
计算机.关闭();--改动点8
}
灯
电视 --改动点3
洗衣机 --改动点6
计算机 --改动点9
}
OCP的关键在于抽象
使用开闭原则步骤
超市收银
收银机设计是否真的完全开闭了
Class 收音
日常生活中有哪些软件系统可以使用开闭原则
移动公司花费系统
LSP(Liskov Substitution Principle)在软件中如果能够使用基类对象,那么一定能够使用其子类对象
里氏代换原则分析
喜欢动物–>喜欢猫,因为猫是动物
我喜欢动物,那我一定喜欢猫,因为猫是动物的子类;但是我喜欢猫,不能据此推定我喜欢动物,因为我并不喜欢老鼠,虽然他也是动物
在sunny软件公司开发的CRM系统中,客户可以分为VIP客户和普普通客户两类,系统需要提供一个发送EMAIL的功能,原始设计方案如所示
无论是普通客户还是vip客户,发送邮件的过程都是相同的,也就是说
也就是说两个send方法中的代码重复,而且在本系统可能还将增加新类型的客户,代码修改范围比较大。
使用里氏代换原则对其进行重构
可以考虑增加一个新的抽象客户类,而降CommonCustomer和VIPCustomer类作为其子类,邮件发送类EmailSender类针对抽象客户类Customer编程。
实现“开-闭“原则的关键是抽象化,并且从抽象化到处具体化实现。如果开闭原则 是面向对象设计的目标的话,依赖倒转原则就是这个面向对象涉设计的主要机制
依赖倒转原则讲得是:要依赖与抽象,不要依赖与具体。
传统的依赖关系
传统的过程性系统的设计方法倾向于使高层次的模块依赖于低层次的模块;抽象层次依赖与具体层次。依赖倒转原则是要把这个错误的依赖关系到转过来,这就是依赖倒转原则的来由。
符合DIP的系统
依赖倒转原则定义
依赖倒转原则(Dependence Inversion Principle,Dip)的定义如下:高层模块不应该依赖底层模块,他们都应该以来抽象,抽象不应该依赖于细节,细节应该依赖于抽象。
另一种表述为:要针对接口编程, 不要针对实现编程。‘
依赖倒转原则分析
类之间的耦合
依赖倒转原则要求客户端依赖于抽象耦合,以抽象方式耦合是依赖倒转原则的关键
Sunny软件公司开发人员在开发某CRM系统时发现,该系统经常需要将存储在TXT或EXCEL文件中的客户信息转存到数据库中,因此需要进行数据格式转换,在客户数据操作类将调用数据格式转换类的方法实现格式转换和数据库插入操作,出是设计方案结构如所示
设计方案存在一个非常严重的问题,由于每次转换数据时数据来源不一定相同,因此需要更换数据转换类,如有时候需要将TXTDataConvertor改为ExcelDataConvertor,此时,需要修改CustomerDao的源代码,而且在引并使用新的数据转换类时也不得不得修改CUSTOMERDaode源代码,系统扩展性较差,违反了开闭原则,现需要对该方案进行重构。
由于CUSTOMERDAO针对具体数据转换类编程,因此在增加新的数据转换类或者更换数据转换类时都不得不修改CustomerDao的源代码,,我们可以通过引入抽象数据转换类解决该问题,在引入抽象数据转换类DataConvertor之后,Customer针对抽象类DataConvertor编程,而将具体数据转换类名存储在配置文件中,符合依赖倒转原则。
接口隔离原则(ISP)的定义如下
客户端不应该依赖那些他不需要的接口
另一种定义如下:
一旦一个接口太大,则需要将他风格成一些更细小的接口,使用该接口的客户端仅需指导与之相关的方法即可
接口隔离原则是指使用多个专门的接口,而不使用单一的总接口,,每一个接口应该承担一种相对独立的角色,不多不少,不干不该干的事,该干的事都要干。
接口隔离原则分析
实例说明
下图展示了一个拥有多个客户类的系统,在系统中定义了一个巨大的接口AbstratctService来服务所有的客户类,可以使用接口隔离原则对其进行重构。
合成复用原则定义如下(CRP)又称为组合/聚合复用原则,其定义如下
尽量使用对象组合,而不是继承来达到复用的目的
合成复用原则就是指在一个新的对象里通过关联关系(包括组合关系和聚合关系)来使用一些已有的对象,使之成为新对象的一部分,新对象通过委派调用已有对象的方法达到复用已有功能的目的,简言之:要尽量使用组合/聚合关系,少用继承。
合成复用原则分析
在面向对象设计中,可以通过两种基本方法在不同的环境中复用已有的设计和实现,即通过组合/聚合关系或通过继承。
某教学管理悬停部分数据库访问类设计如图所示
如果需要更换数据库连接方式,如原来采用jdbc连接数据库,则需要修改DBUtil类源代码,如果StudentJDBC链接,但TeacherDAO采用连接池链接,则需要增加一个新的DBUtil类并修改StudentDao或者teacherDao的源代码,使之及诚信的数据库连接类,这将违背开闭原则,系统扩展性较差
迪米特法则又称为最少知识原则,她有多重定义方法,其中几种电信定义如下
简单来说,迪米特法则就是指一个软件实体应当尽可能少的与其他实体发生相互作用,这样,当一个模块修改时就会尽量少的影响其他的模块,扩展会相对容易,这是对软件实体之间通信的限制,他要求限制软件实体之间的通信的宽度和深度。
在迪米特法则中,对于一个对象,其朋友包括以下几类
任何一个对象,如果满足上面的条件之一,就是当前对象的朋友,否则就是陌生人。
实例说明
某系统界面类(如form1,form2)与数据访问类如dao1,dao2等类之间的调用关系较为复杂