前言:在编程过程中,我们总会在不经意间遵循亦或违反了面向对象的设计原则,很多非计算机专业毕业,或者对基础知识不重视的编程人员所编写的代码会出现非常多的漏洞,后期维护拓展变得举步维艰。所以在编写代码之前,掌握面向对象的设计原则是非常必要的,该原则无关语言,是面向对象编程的核心思想。
由于本人主要的编程语言为Java,所以示例中出现的代码为Java版本,但不影响理解。
文中的图例和定义多来自网上,如有雷同纯属意外。
一句话概括是本人对设计原则的简单理解,如有觉得不合适的地方望指出
文章示例代码: 示例代码
1.单一职责原则
-
单一职责原则(Single Responsibility Principle, SRP):一个类只负责一个功能领域中的相应职责,或者可以定义为:就一个类而言,应该只有一个引起它变化的原因。
-
一句话概括:一个类承担的职责不能太多
-
简单实例:
- 从数据库中查询客户信息,并且用图表展示
注:在项目中,我们都会封装很多的工具类,不同类型和职责的工具类都体现的正是单一职责原则,如果一个工具类中既有连接数据库的功能,又有sql查询功能,就违反了单一职责的原则,需要进行重构了.
2.开闭原则
-
开闭原则(Open-Closed Principle, OCP):一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。
-
一句话概括:在迭代维护过程中,不需要对原有代码进行修改就能进行拓展就可以说代码符合开闭原则。
-
简单实例:有一个图表的显示功能,其中包括折线图,饼图等。
- AbstractChart是所有图标类型的抽象基类,子类(PieChart,BarChart必须实现display方法)
- ChartDisplay针对抽象图表类进行编程,并通过setChart()方法由客户端来设置实例化的具体图表对象,在ChartDisplay的display()方法中调用chart对象的display()方法显示图表
- 如果现在要新增一个折线图LineChart,只需继承AbstractChart,重写display方法,使用时像ChartDisplay注入一个LineChart对象即可,无需修改原有代码.

3.里氏代换原则
-
里氏代换原则(Liskov Substitution Principle, LSP):所有引用基类(父类)的地方必须能透明地使用其子类的对象。
-
一句话概括:动物是猫的父类,所以猫一定是动物,但是动物不一定都是猫。
-
里氏代换原则是实现开闭原则的重要方式之一
-
示例:客户可分为普通客户和VIP客户,系统提供发送邮件功能
- 邮件发送类EmailSender类针对抽象客户类Customer编程
- 根据里氏代换原则,发送的对象Customer是所有实现类的父类,所以在使用时可以传入不同的实现类
-
针对基类编程,在程序运行时再确定具体子类。
4.依赖倒转原则
-
依赖倒转原则(Dependency Inversion Principle, DIP):抽象不应该依赖于细节,细节应当依赖于抽象。
-
一句话概括:面向接口编程,而不是针对实现编程。
-
实例:将存储在TXT或Excel文件中的客户信息转存到数据库中,因此需要进行数据格式转换,但是每次的数据来源不一定相同。
- 通过配置xml文件达到不修改代码的情况下改变转换的规则,具体数据转换类名存储在配置文件中,符合依赖倒转原则。
- CustomerDAO针对抽象类DataConvertor编程,而将具体数据转换类名存储在配置文件中,符合依赖倒转原则。
-
程序运行时,具体数据转换类对象将替换DataConvertor类型的对象,符合里氏代换原则。
该示例中使用了开闭原则、里氏代换原则和依赖倒转原则,在大多数情况下,这三个设计原则会同时出现,开闭原则是目标,里氏代换原则是基础,依赖倒转原则是手段,它们相辅相成,相互补充,目标一致,只是分析问题时所站角度不同而已。
5.接口隔离原则
-
接口隔离原则(Interface Segregation Principle, ISP):使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。
-
一句话概括:一个接口应该承担相对独立但完成的功能,不应该有不相关的混入,例如对数据库增删改查不应该和连接数据库放在同一个接口。
-
实例:有一个数据显示模块,transformToXML()用于将数据转换成XML格式,方法createChart()用于创建图表,方法displayChart()用于显示图表,方法createReport()用于创建文字报表,方法displayReport()用于显示文字报表。
-
将多个功能抽象成不同的接口,由使用者实现多个接口达到不同的功能
-
在使用接口隔离原则时,需要注意控制接口的粒度,接口不能太小,如果太小会导致系统中接口泛滥,不利于维护;接口也不能太大,太大的接口将违背接口隔离原则,灵活性较差,使用起来很不方便。一般而言,接口中仅包含为某一类用户定制的方法即可,不应该强迫客户依赖于那些它们不用的方法。
6.合成复用原则
- 合成复用原则(Composite Reuse Principle, CRP):尽量使用对象组合,而不是继承来达到复用的目的。
- 一句话概括:复用时尽量组合关联关系,而不是继承关系
在代码中通常以成员变量的形式体现
- 示例:数据库连接由mysql拓展为oracle,同时保持原有代码的可用.
- CustomerDAO和DBUtil之间的关系为关联关系,采用依赖注入的方式将DBUtil对象注入到CustomerDAO中
- OracleUtil为DBUtil的子类,不同数据库连接通过继承DBUtil实现getConnection()方法拓展不同的连接方式.
-
CustomerDAO中只需注入子类对象(如OracleDBUtil)即可使用子类拓展的方法
7.迪米特法则
-
迪米特法则(Law of Demeter, LoD):一个软件实体应当尽可能少地与其他实体发生相互作用。
-
实例:当一个按钮(Button)被单击时,对应的列表框(List)、组合框(ComboBox)、文本框(TextBox)、文本标签(Label)等都将发生改变
- 引入一个专门用于控制界面控件交互的中间类(Mediator)来降低界面控件之间的耦合度。
- 界面控件之间不再发生直接引用,而是将请求先转发给中间类,再由中间类来完成对其他控件的调用。
-
当需要增加或删除新的控件时,只需修改中间类即可,无须修改新增控件或已有控件的源代码。
网友评论