漫谈模式之几大设计原则
本文来简单介绍一下设计模式采用的几大原则。
单一职责原则
含义
单一职责原则(Single Responsibility Principle,SRP):一个类只负责一个功能领域中的相应职责或可以定义为:就一个类而言,应该只有一个引起它变化的原因。
举例
做一个简单的程序用做图表展示客户列表数据,其中包括图表的创建和展示、数据库连接和获取客户列表的方法,如下图所示:
从上图可以看出,CustomerDataChart其职责不够单一,其包括数据库连接、数据库操作、图表创建和展示,这些功能应该拆分到不同的类中,比如:
这样修改以后,数据库连接就由DBUtils来完成;CustomDao定义获取客户列表方法,其具体实现由DBUtil来完成;CustomerDataChart则包括图表的创建和展示,展示的内容则由CustomerDao来完成;经过上述调整,每个类都有单一的清晰的职责。
开闭原则
意图
开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代 码,而是要扩展原有代码,实现一个热插拔的效果。
先来看一个示例:
还是图表展示,其支持饼图和柱状图的展示:
如果我们需要增加一个其他图形的展示,我们需要增加else if的条件来进行扩展,这样原来的代码就别修改。
那么为了满足开闭原则,需要怎么做呢?那就要对系统采用抽象化设计。抽象化是开闭原则的关键。
经过抽象化之后,ChartDisplay的变量就成为AbstarctChart,如果有新的Chart需要扩展,如增加一个AbcChart,那么AbcChart只要继承AbstractChart即可,如果ChartDisplay需要使用AbcChart进行图表展示,则其只要setChart(AbcChart)即可,这就满足了扩展,且不会对原有代码进行修改。
里氏替换原则
意图
里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。里氏代换原 则中说,任何基类可以出现的地方,子类一定可以出现。
比如,我们有一个功能用于给普通用户和VIP用户发送邮件的功能,如下图所示:
我们可以看到,其实CommonCustomer和VipCustomer发送邮件很类似。我们可以采用里氏替换原则,可以采用如下具体步骤:
- 建立抽象;通过抽象来建立规范;
- 具体实现在运行时取代抽象;保证了系统的扩展性和灵活性;
修改后如下图所示,抽象一个Customer,EmailSender中send方法的参数为Customer即可。
这样,
Customer commomCus = new CommonCustomer();EmailSender.send(commomCus );就可以发送普通用户邮件;
同样,
Customer vipCus = new VipCustomer();EmailSender.send(vipCus );就可以发送VIP用户邮件;
依赖倒转原则
意图
面向接口编程,依赖于抽象而不依赖于具体。写代码时用到具体类时,不与具体类交互,而与具体类的上层接口交互。
比如,有一个小功能,读取文件中的用户名单,然后添加到数据库中。可以采用如下方式实现:
上述示例中,CustomDao与具体的文件数据转换类进行交互式,其不符合依赖倒转原则。依据依赖倒转原则,需要与具体类的上层接口或者抽象类交互,那么我们可以采用如下方式进行。
定义一个抽象类DataConvertor,其具有两个子类TXTDataConvertor和ExcelDataConvertor完成具体的数据转换。
至于CustomDao采用哪一种类型进行数据转换,其可以采用配置的方式进行。
这样,程序将不在于具体的实现类进行直接交互。关于依赖倒转原则,相信使用Spring来进行开发的同学深有体会。
接口隔离原则
意图
每个接口中不存在子类用不到却必须实现的方法,如果不然,就要将接口拆分。使用多个隔离的接口,比使用单个接口(多个接口方法集合到一个的接口)要好。
举个例子,自己实现ArrayList和LinkedList,我们可以定义一个List接口,
我们来实现LinkedList:
然后再来实现ArrayList:
想想这样做,有什么问题吗?
我们可以看到poll和peek方法,在ArrayList中用不到,但是我们却一定要实现它们,这就是因为List接口太大导致的。
我们可以创建一个新的接口:
然后移除之前List中的poll和peek方法:
这样,ListLinked的实现就变成:
ArrayList的实现为:
这思想不就是和JDK源代码中ArrayList和LinkList的思想一致吗~~
迪米特法则
意图
一个类对自己依赖的类知道的越少越好。也就是说无论被依赖的类多么复杂,都应该将逻辑封装在方法的内部,通过 public 方法提供给外部。
一个软件实体应该尽可能少地和其它实体产生作用。
比如,我们很多人一起在线聊天,我说话不需要和每个人都说一下进行交互,只需要一个聊天室,就能将大家联系在一起。迪米特法则思想的体现,可以在中介者模式中体现,具体可以参考: 漫谈模式之中介者模式
七. 合成复用原则
意图
尽量使用合成复用,而不是采用继承来达到复用的目的。
比如,CustomerDao可以采用继承DBUtil完成数据库操作。
采用复用的方式,CustomerDao只要委托DBUtil来完成数据库操作,DBUtil其可以采用Oracle、MySQL或者其他数据库的链接实现,这样更加灵活。
再如:
当一个抽象可能有多个实现时,通常用继承来协调它们。抽象类定义对该抽象的接口,而具体的子类则有不同的方式加以实现。但是此方法有时候不够灵活。继承机制将抽象部分与它的实现部分固定在一起,使得难以对抽象部分和实现部分独立地进行修改、扩充和重用。
这个我在《漫谈设计模式之桥接模式》中已经做了详细的说明,有兴趣的同学可以去扩展看看。
小结
面向对象的一般原则,和其关系如下如图所示:
相关文章
- Android基于DataBinding+Koin实现MVVM模式页面快速开发框架
- java 工厂模式例子_java 工厂模式简单介绍及例子[通俗易懂]
- 什么是MVC软件架构模式_mvc架构的设计思路
- 【说站】php常见运行模式详解
- 7个原则,看懂「深色模式」设计
- 【2022新书】C++软件设计:高质量软件的设计原则和模式
- Windows反调试技术简单讲解(用户模式篇)
- Java面试:单例模式
- Fintoch分投趣模式系统开发/区块链DAPP合约
- MVC模式的设计思想详解架构师
- java设计模式之状态模式详解架构师
- java策略模式详解编程语言
- Python新式类 单例模式与作用域(四)详解编程语言
- 模式探索Linux的特殊分支模式(linux的分支)
- Linux系统教程:单用户模式破解root密码
- Linux Munge:最安全的登录模式(linux munge)
- 以Oracle关系模式设计的数据一目了然(oracle关系模式)
- MySQL中使用BS模式进行数据管理(mysql中bs模式)
- Redis自减实现的高效抢购模式(redis自减实现抢购)
- 微软 Win11 界面 UI 设计首批适配截图工具、计算器和邮件等 App:支持黑暗模式,功能更丰富
- javascript模式设计之工厂模式详细说明
- JavaScript面向对象设计二构造函数模式
- node.jschat程序如何实现Ajaxlong-polling长链接刷新模式