首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

GoF23种设计模式总结及软件设计7大原则

设计模式总结及软件设计七大原则

设计模式系列总览

前言

软件设计7大原则

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

里氏替换原则(Liskov Substitution Principle,LSP)

依赖倒置原则(Dependence Inversion Principle,DIP)

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

接口隔离原则(Interface Segregation Principle,ISP)

迪米特法则(Law of Demeter,LoD)

合成复用原则(Composite Reuse Principle,CRP)

设计模式总结

创建型设计模式

结构型设计模式

行为型设计模式

总结

设计模式系列总览

前言

前面我们已经介绍完了全部的GoF23种设计模式,而介绍过程中其实也可以发现很多设计模式都是很相似的,设计模式的思想都是相通的,而且设计模式大都不是单独出现的,一般都是你中有我,我中有你,而设计模式的学习主要还是体会设计模式的思想,有些时候我们并不需要严格遵循设计模式的写法,只要能把设计模式的思想融入到日常的代码当中,自然就可以提升代码的质量。

本文主要会对GoF23种模式进行总结归纳,并且同时也会介绍一个软件设计的七大原则。

软件设计7大原则

在软件开发过程中,为了提高系统的可维护性和可复用性,可扩展性以及灵活性,产生了7大设计原则,这些原则也会贯穿体现在我们前面介绍的设计模式中,设计模式会尽量遵循这些原则,但是也可能为了某一个侧重点从而牺牲某些原则,在我们日常开发中也只能说尽量遵守,但是并不必刻意的为了遵守而遵守,要有所侧重。

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

开闭原则由勃兰特·梅耶(Bertrand Meyer)在其 1988 年的著作《面向对象软件构造》(Object Oriented Software Construction)中提出:在一个软件实体(如类,函数等)应该对扩展开放,对修改关闭。

开闭原则强调的是用抽象构建框架,用实现扩展细节,这样就可以提高软件系统的可复用性和可维护性。

开闭原则是面向对象设计的最基本设计原则,而遵守开闭原则的核心思想就是面向抽象编程。

举个例子,超市里面的商品需要出售。

1、新建一个商品接口:

2、新建一个白菜商品类:

这时候到了晚上了,白菜要打折清仓,只卖1.98。这时候应该怎么做?

直接改白菜商品类的getSalePrice方法可行吗?不可行,可能会影响到其他地方。

那直接改接口呢,新增一个打折方法可行吗?假如有几千种商品,我就只有白菜这一个商品需要打折呢,那么这显然也不合理,再不然就直接在白菜类里面单独新增一个打折方法,这些方法看似可行,但是都违背了开闭原则中的对修改关闭。所以我们的做法是再新建一个白菜打折类:

这样子就符合了开发原则,扩展灵活,后面如果有其他商品需要打折可以一样处理

里氏替换原则(Liskov Substitution Principle,LSP)

里氏替换原则由麻省理工学院计算机科学实验室的芭芭拉·利斯科夫(Barbara Liskov)在 1987 年的“面向对象技术的高峰会议”(OOPSLA)上发表的一篇文章《数据抽象和层次》(Data Abstraction and Hierarchy)里提出:继承必须确保超类所拥有的性质在子类中仍然成立。也就是说如果对每一个类型为T1的对象o1都有类型为T2的对象o2,使得以T1所定义的程序P在所有的对象o1都替换成为o2时,程序P的行为没有发生改变。

在具体一点就是说如果一个软件实体适用于一个父类的话,那么一定适用于子类,所有引用了父类的地方都必须能透明的使用其子类对象。具体的可以总结为以下几条原则:

1、子类可以实现父类的抽象方法,但是不能覆盖父类的非抽象方法。

2、子类中可以增加自己的特有方法。

3、当子类方法重载父类的方法时,方法的前置条件(即方法的输入/入参)要比父类方法输入的参数更宽松

4、当子类实现父类的方法(重载/重写/实现抽象方法),方法的后置条件(即方法的输出/返回值)要比父类更严格或者相等

遵循里氏替换原则有如下优点:

1、可以约束继承的泛滥,也是开闭原则的一种体现

2、加强了程序的健壮性,同时在变更时也做到了非常好的兼容性,提高了程序的维护性,扩展性,降低了需求变更时引入的风险。

举个栗子,我们以鸟类飞翔为例:

这时候我们有一个鹰类需要继承Bird:

最后我们再看看测试类:

当我们用子类替换父类的时候,因为父类的方法被重写了,所以替换之后输出结果发生了改变,这就违背了里氏替换原则。

依赖倒置原则(Dependence Inversion Principle,DIP)

依赖倒置原则是Object Mentor公司总裁罗伯特·马丁(Robert C.Martin)于1996年在C++ Report上发表的文章中提出。

依赖倒置原则指的是在设计代码结构时,高层模块不应该依赖低层模块,而是都应该依赖其抽象。抽象不应该依赖细节,细节应该依赖抽象。通过依赖倒置原则可以减少类与类之间的耦合性,提高系统的稳定性,提高代码的可读性和可维护性,而且能够降低修改程序所带来的的风险。

举个栗子:

比如说有一家超市里面一开始只卖青菜:

然后心在业务开始扩大了,要卖肉了,这时候怎么办呢,可以再加一个方法,但是这么一来底层要改,调用者也要改,不利于维护,所以应该不依赖于具体实现来编程。

进行如下改写:

新建一个商品接口:

然后新建一个白菜类:

然后将超市类改写:

这时候超市已经面向接口了,而不面向具体(白菜),如果扩张业务,想要卖肉类,直接新增一个肉类就好了

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

单一职责原则由罗伯特·C.马丁(Robert C. Martin)于《敏捷软件开发:原则、模式和实践》一书中提出。

单一职责原则指的是不要存在多于一个导致类变更的原因。假如我们有一个类里面有两个职责,一旦其中一个职责发生需求变更,那我们我们修改其中一个职责就有可能导致另一个职责出现问题,在这种情况应该把两个职责放在两个Class对象之中。

单一职责可以降低类的复杂度,提高类的可读性,提高系统的可维护性,也降低了变更职责引发的风险。

举个栗子:

比如说超市里面的商品需要进货然后再卖出去,这就是两件事。

新建一个超市商品类:

这时候一个方法里面有两个功能,假如业务逻辑非常复杂,那么一个功能发生变化需要修改有很大的风险导致另一个功能也发生异常。所以我们应该进行如下改写,将这两个职责拆分成两个类:

接口隔离原则(Interface Segregation Principle,ISP)

接口隔离原则是2002年由罗伯特·C.马丁提出的。接口隔离原则指的是用多个专门的接口,而不使用单一的一个总接口,客户端不应依赖它不需要的接口。

接口隔离原则符合我们所说的高内聚低耦合的设计思想,从而使得类具有很好的可读性、可扩展性和可维护性,我们在设计接口的时候应该注意以下几点:

1、一个类对其他类的依赖应建立在最小的接口之上

2、建立单一的接口,不建立庞大臃肿的接口

3、尽量细化接口,接口中的方法应适度

我们以常见的动物的行为来举个栗子:

这个动物接口里面包含了三个行为接口:地上走,水里游,天上飞。但是是不是所有动物都有这三种行为呢?显然不是,比如狗肯定不能天上飞,鱼只能水里游,这样没用的行为只能空着什么都不做了:

而如果鱼,那就得空着两个方法什么也不能做了,这就是一个臃肿的接口设计,如果遵循接口隔离原则,那么应该这么改写:

新建三个接口,每个动作都对应一个接口:

这时候动物狗就可以这么写:

这样就实现了接口隔离,不会具备一些无用的行为。

迪米特法则(Law of Demeter,LoD)

迪米特法则又叫作最少知道原则(Least Knowledge Principle,LKP),产生于1987年美国东北大学(Northeastern University)的一个名为迪米特(Demeter)的研究项目,由伊恩·荷兰(Ian Holland)提出。

迪米特法则是指一个对象对其他对象应该保持最少的了解,尽量降低类与类之间的耦合。

举个栗子,比如说上面的超市售卖的商品青菜,老板(Boss)想知道卖出去了多少斤:

首先新建一个青菜商品:

这时候普通做法可以在Boss类里面集成Cabbage类,这样就可以拿到售卖记录,但是这就违背了迪米特法则,因为老板不应该直接和商品打交道,要不然商品一多,老板哪有闲情自己一个个去查,所以一般老板可以找对应的经理获取结果。

我们再新建一个经理类:

最后再新建Boss类:

可以看到Boss完全不需要和青菜打交道,找经理就好了,这就是迪米特法则,不该知道的不要知道,我只要让该知道的人知道就好了,你想知道那你就去找那个该知道的人。而实际上中介者模式就是一种典型的遵守了迪米特法则的设计模式。

合成复用原则(Composite Reuse Principle,CRP)

合成复用原则又叫组合/聚合复用原则(Composition/Aggregate Reuse Principle,CARP)。指的是在软件复用时,要尽量先使用组合(has-a)或者聚合(contains-a)等关联关系来实现,这样可以使系统更加灵活,降低类与类之间的耦合度,一个类的变化对其他类造成的影响相对较少。

继承通常也称之为白箱复用,相当于把所有的实现细节暴露给子类。组合/聚合也称之为黑箱复用,对类以外的对象是无法获取到实现细节的。

这个原则还是非常好理解的,像我们开发中经常用的依赖注入,其实就是组合,所以在这里就不再举例子了。

设计模式总结

学完设计模式之后,其实我们也应该知道,设计模式就是在某种场景下,针对某种问题的某种解决方法,而GoF23种设计模式源于《设计模式》一书:《Design Patterns:Elements of Resuable Object-Oriented Software》是由Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides四个人一起合著的,发表于1995年。这四位作者也被称之为“四人组(Gang of Four)”,所以这本书也被称之为:四人组(或GoF)书,而这本书里面就介绍了23种设计模式,这就是我们常说的GoF23种设计模式。

23种设计模式大致可以分为三大类,创建型,结构型和行为型。

创建型设计模式

创建型设计模式顾名思义就是用来创建对象的,回忆23种设计可以很容易得出其主要有如下设计模式(加粗的表示高频使用设计模式):工厂方法模式,抽象工厂模式,单例模式,建造者模式,原型模式。

结构型设计模式

结构型模式主要包括以下模式(加粗的表示高频使用设计模式):代理模式,门面模式,装饰器模式,享元模式,组合模式,适配器模式,桥接模式。

行为型设计模式

结构型模式主要包括以下模式(加粗的表示高频使用设计模式):模板方法模式,策略模式,责任链模式,状态模式,迭代器模式,命令模式,备忘录模式,中介者模式,解释器模式,观察者模式,访问者模式。

总结

本文主要介绍了软件设计的7大原则,并分别通过一个例子给予了示范,最后我们将23种设计模式分为了创建型,结构型和行为型三大类,并对每种设计模式分别用一句话进行了简单的概括。

------------END-----------

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20210216A02M4000?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券