zl程序教程

您现在的位置是:首页 >  后端

当前栏目

漫谈模式之几大设计原则

模式 设计 原则 漫谈
2023-06-13 09:17:39 时间

本文来简单介绍一下设计模式采用的几大原则。

单一职责原则

含义

单一职责原则(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或者其他数据库的链接实现,这样更加灵活。

再如:

当一个抽象可能有多个实现时,通常用继承来协调它们。抽象类定义对该抽象的接口,而具体的子类则有不同的方式加以实现。但是此方法有时候不够灵活。继承机制将抽象部分与它的实现部分固定在一起,使得难以对抽象部分和实现部分独立地进行修改、扩充和重用。

这个我在《漫谈设计模式之桥接模式》中已经做了详细的说明,有兴趣的同学可以去扩展看看。

小结

面向对象的一般原则,和其关系如下如图所示: