面向对象的设计原则(一)


       在面向对象的设计过程中,首先需要考虑的是如何同时提高一个软件系统的可维护性和可复用性。这时,遵从面向对象的设计原则,可以在进行设计方案时减少错误设计的产生,从不同的角度提升一个软件结构的设计水平。同时面向对象设计原则也是用于评价一个设计模式的使用效果的重要指标之一,在设计模式的学习中,经常会看到诸如“XXX模式符合XXX原则”、“XXX模式违反了XXX原则”这样的语句,以此来评判设计模式的优势与不足。这里将针对如下的7种常见的面向对象设计原则进行阐述:

  • 单一职责原则
  • 开闭原则
  • 里氏代换原则
  • 依赖倒转原则
  • 接口隔离原则
  • 迪米特法则
  • 合成复用原则

1.单一职责原则(Single Responsibility Principle, SRP)

   

   单一职责原则是最简单的面向对象设计原则,它用于控制类的粒度大小,其定义如下:

就一个类而言,应该仅有一个引起它变换的原因。

    对于单一职责原则,可以理解为一个类只负责一个功能领域中的相应职责,即一个类不要负责太多“杂乱”的工作。在软件系统中,如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当变化发生时设计或遭受到意想不到的破坏。以项目开发为例,如果项目组成员每个人的职责都很明确,可以专心开发自己负责的模块,则项目成果的质量往往很高。相反,如果职责不清晰,分工就会混乱,极有可能出现A负责N个模块,而B则可以在一旁开心地打酱油的情况。这样,如果A所负责的模块中有多个模块集中暴露问题时,A就很难高质量地将问题从容解决。

    单一职责原则可以用一句比较通俗的话来表达,即自己负责领域的事情都做,不是自己领域的事不去插手。单一职责原则是实现高内聚、低耦合的指导方针,它是最简单但又最难运用的原则。需要设计人员发现类的不同职责并将其分离,而发现类的多重职责需要设计人员具有较强的分析设计能力和相关实践经验。

    下面通过一个简单的实例来对单一原则进行说明。

    公司员工每个月最盼望的日子便是发工资的那一天,与此同时,财务需要统计每个人的薪资情况并做成报表,然后以工资条的形式给到员工。针对薪资报表这一功能,给出了如下的初始方案:


   函数说明:

  • getConnection()--用于连接数据库
  • findSalaryList(intemployeeId)--根据员工的ID获取该员工本月的薪资详情,如五险一金、个税等
  • createChart()--根据员工薪资创建报表
  • displayChart()--用于显示薪资报表

    从初始设计方案来看,SalaryChart承担了数据库连接、信息查询、图表操作等多项职责。当其他类也需要进行数据库连接、信息查询、图表操作时,为满足其他类的操作需求,对应的方法都需要做出对应的修改。此时,引起该类变化的原因已经不止一个,违反了单一职责原则。为解决该问题,需要对类进行拆分,使其职责明确。重新设计如下:


   拆分类说明如下:

  • SalaryChart:负责表的生成和显示
  • SalaryDAO:负责对数据表的操作
  • DBUtil:负责数据库的连接

    此时,每个类只有一个引起其变化的原因,满足了单一职责的原则。


2.开闭原则(Open-Closed Principle, OCP)

    

   开闭原则是面向对象的可复用设计的第一块基石,它是最重要的面向对象设计原则。开闭原则由Bertrand  Meyer于1988年提出,其定义如下:

一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。

    开闭原则即对扩展开放,对修改封闭。在软件系统开发过程中,软件的需求往往会随着时间的推移而发生变化。因此,进行软件设计时需要考虑怎样的设计才能面对需求的改变却可以相对保持稳定,从而使得系统可以在第一个版本以后不断推出新的版本。这时便可以以开闭原则作为指导。如果一个软件设计符合开闭原则,那么可以非常方便地对系统进行扩展,而且在扩展时无须修改现有代码,使得软件系统在拥有适应性和灵活性的同时具备较好的稳定性和延续性。

    为了满足开闭原则,需要对系统进行抽象化设计,抽象化是开闭原则的关键。在进行软件设计时,一般先评估出最有可能发生变化的类,然后构造抽象来隔离那些变化。当变化发生时,无须对抽象层进行任何改动,只需要增加新的具体类来实现新的业务功能即可,实现在不修改已有代码的基础上扩展系统的功能,达到开闭原则的要求。

    以设计一个计算器为例,假设初始设计方案如下。


   对于加、减、乘、除不同的计算方式以不同的类进行实现,然后在Calculator类中,根据用户输入的操作指令,实例化不同的处理类进行处理并显示结果,其displayResult代码片段如下:

if (operate.equals("+")) {
	OperationAdd oper = new OperationAdd();
	result = oper.getResult();
} else if (operate.equals("-")) {
	OperationSub oper = new OperationSub();
	result = oper.getResult();
} else if (operate.equals("*")) {
	OperationMul oper = new OperationMul();
	result = oper.getResult();
} else if (operate.equals("/")) {
	OperationDiv oper = new OperationDiv();
	result = oper.getResult();
} else {
	System.out.println("操作符输入有误,请重新输入");
}
System.out.println("Result = "+result);

   在该实例中,如果需要添加新的运算,如开方,则除了需要增加一个新的开方运算类外,还需要Calculator类中displayResult方法增加分支判断,明显违反了开闭原则。这时引入抽象化设计,重新设计如下。增加抽象操作类AbstrcatOperation,使加、减、乘、除等操作类成为AbstrcatOperation的子类。而Calculator则通过setOperation方法来由客户端来设置实例化的具体操作类。此时,如果要增加新的运算OperationSqrt,只需要将OperationSqrt作为AbstrcatOperation的子类,并在客户端向Calculator注入一个OperationSqrt对象即可,无需修改现有类库的源代码。


参考文献:
1、大话设计模式

2http://blog.csdn.net/lovelion/article/details/17517213

【作者:墨叶扶风http://blog.csdn.net/yefufeng


猜你喜欢

转载自blog.csdn.net/yefufeng/article/details/78544733