热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

在面向对象的设计过程中,首先需要考虑的是如何同时提高一个软件系统的可维护性和可复用性。这时,遵从面向对象的设计原则,可以在进行设计方案时减


       在面向对象的设计过程中,首先需要考虑的是如何同时提高一个软件系统的可维护性和可复用性。这时,遵从面向对象的设计原则,可以在进行设计方案时减少错误设计的产生,从不同的角度提升一个软件结构的设计水平。同时面向对象设计原则也是用于评价一个设计模式的使用效果的重要指标之一,在设计模式的学习中,经常会看到诸如“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、大话设计模式

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

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




推荐阅读
  • 浅析python实现布隆过滤器及Redis中的缓存穿透原理_python
    本文带你了解了位图的实现,布隆过滤器的原理及Python中的使用,以及布隆过滤器如何应对Redis中的缓存穿透,相信你对布隆过滤 ... [详细]
  • 本文详细介绍了Java反射机制的基本概念、获取Class对象的方法、反射的主要功能及其在实际开发中的应用。通过具体示例,帮助读者更好地理解和使用Java反射。 ... [详细]
  • 本文介绍如何使用 Python 的 DOM 和 SAX 方法解析 XML 文件,并通过示例展示了如何动态创建数据库表和处理大量数据的实时插入。 ... [详细]
  • 如果应用程序经常播放密集、急促而又短暂的音效(如游戏音效)那么使用MediaPlayer显得有些不太适合了。因为MediaPlayer存在如下缺点:1)延时时间较长,且资源占用率高 ... [详细]
  • 基于iSCSI的SQL Server 2012群集测试(一)SQL群集安装
    一、测试需求介绍与准备公司计划服务器迁移过程计划同时上线SQLServer2012,引入SQLServer2012群集提高高可用性,需要对SQLServ ... [详细]
  • IOS Run loop详解
    为什么80%的码农都做不了架构师?转自http:blog.csdn.netztp800201articledetails9240913感谢作者分享Objecti ... [详细]
  • 本文回顾了作者初次接触Unicode编码时的经历,并详细探讨了ASCII、ANSI、GB2312、UNICODE以及UTF-8和UTF-16编码的区别和应用场景。通过实例分析,帮助读者更好地理解和使用这些编码。 ... [详细]
  • 单片微机原理P3:80C51外部拓展系统
      外部拓展其实是个相对来说很好玩的章节,可以真正开始用单片机写程序了,比较重要的是外部存储器拓展,81C55拓展,矩阵键盘,动态显示,DAC和ADC。0.IO接口电路概念与存 ... [详细]
  • oracle c3p0 dword 60,web_day10 dbcp c3p0 dbutils
    createdatabasemydbcharactersetutf8;alertdatabasemydbcharactersetutf8;1.自定义连接池为了不去经常创建连接和释放 ... [详细]
  • 网站访问全流程解析
    本文详细介绍了从用户在浏览器中输入一个域名(如www.yy.com)到页面完全展示的整个过程,包括DNS解析、TCP连接、请求响应等多个步骤。 ... [详细]
  • [转]doc,ppt,xls文件格式转PDF格式http:blog.csdn.netlee353086articledetails7920355确实好用。需要注意的是#import ... [详细]
  • javascript分页类支持页码格式
    前端时间因为项目需要,要对一个产品下所有的附属图片进行分页显示,没考虑ajax一张张请求,所以干脆一次性全部把图片out,然 ... [详细]
  • 本文详细介绍了 Charles 工具的下载、安装、配置及使用方法,特别针对 HTTP 和 HTTPS 协议的数据抓取进行了说明。 ... [详细]
  • Spring – Bean Life Cycle
    Spring – Bean Life Cycle ... [详细]
  • WinMain 函数详解及示例
    本文详细介绍了 WinMain 函数的参数及其用途,并提供了一个具体的示例代码来解析 WinMain 函数的实现。 ... [详细]
author-avatar
手机用户2702932415_836
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有