设计模式六个原则

一、开闭原则

是什么?

对扩展开放,对修改关闭
在程序需要业务扩展的时候,不要去修改原有的代码;

怎么做?

想要达成这样的效果,我们需要使用接口和抽象类;

为什么?

  • 因为抽象灵活性好,适应性广,只要抽象的合理,可以基本保持软件架构的稳定;
  • 当软件需要发生变化时,只需要根据需求重新派生一个实现类来扩展就可以了;

例子

比如我们玩英雄联盟,每个英雄都有各种皮肤,可以有皮肤A、皮肤B等等;
那么我们按以下方式搭建类,以后出了皮肤C直接实现抽象皮肤类即可完成扩展,不需要修改原有代码;
![在这里插入图片描述

二、里氏代换原则

是什么?

任何基类可以出现的地方,子类一定可以出现,反过来则不成立;

举个例子,我们假设狗是动物的子类;
那么我喜欢动物,我一定也喜欢狗,因为狗也是动物;反过来则不行;


简单来说就是子类可以扩展父类的功能,但不能改变父类原有的功能;
子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。

在Java中的体现

在编译阶段,Java编译器会检查一个程序是否符合里氏代换原则,这是一个与实现无关的、纯语法意义上的检查,但Java编译器的检查是有局限的。


比如很常见的访问修饰符的限制

子类方法权限要大于等于父类的访问权限
因为只有这样,基类才一定可以用派生类代替。如果派生类的访问权限变小了,这个原则就没法成立了。

看下图,我们都知道public是大于default的;
在这里插入图片描述
在这里插入图片描述

特点

里氏代换原则是实现开闭原则的重要方式之一;

在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象(多态)

三、依赖倒转原则

是什么?

  • 高层模块不应该依赖低层模块,两者都应该依赖其抽象;
  • 抽象不应该依赖细节,细节应该依赖抽象。

简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。

个人理解

我觉得跟针对接口编程,不针对现实编程十分接近,或者说就是一样的;

之所以叫做依赖倒转原则,是因为我们不是先从上层模块出发,而是先从底层模块出发;

举个例子,比如我们开一个汉堡店,里面有各种汉堡,比如鸡腿堡、牛肉堡、老八秘制小汉堡等等

那么画出依赖图应该如下;在这里插入图片描述
但是显然我们不想让汉堡店依赖于这么多种繁杂的汉堡,也就是不想依赖于实现类,那么我们应该倒转思路,从底层出发,先抽取出汉堡这个抽象的tag,然后再向上构建汉堡店;

即是下图;
在这里插入图片描述

好处

这段话来源于《设计模式》这本书

只根据抽象类中定义的接口来操纵对象有以下两个好处

  1. 客户无需知道他们使用对象的特定类型,只需要知道对象有客户所期待的接口;
  2. 客户无需知道他们使用的对象是用什么类来实现的,只需要知道定义接口的抽象类;

这将极大地减少子系统实现之间的相互依赖关系;

接口隔离原则

是什么?

客户端不应该被迫依赖于它不使用的方法;一个类对另一个类的依赖应该建立在最小的接口上。

个人理解

比如说我们要设计一把伞A,我们期望这把伞能够有三个特点,防雨、防晒、美观;

那如果我们抽象的伞的功能有防雨、防晒、美观

那如果现在需要另一款伞B,它只具有防雨、防晒的功能,如果B直接去实现这个抽象伞,就会违背了接口隔离原则;

因此我们可以将防雨、防晒、美观这三个功能分别用一个接口来表示,需要什么功能就实现过去;


注意点

  • 接口尽量小,但是要有限度。对接口进行细化可以提高程序设计灵活性是事实,但是如果过小,则会造成接口数量过多,使设计复杂化。

个人理解

说着要尽量小,但是在没有经验的情况下,是很难做到的;
那我觉得,比起做大,还不如做小一点呢;
因为加法总是比减法容易的;

与单一原则的区别

我觉得是十分相近的,但是单一原则更像是需求上和功能上的改进,需要将不同的功能严格的分隔开。接口隔离更多的是避免不必要的浪费,避免产生不需要的方法。

迪米特法则

迪米特法则又叫最少知识原则;

  • 只和你的直接朋友交谈,不跟“陌生人”说话;

  • 减少对朋友的了解

这里的朋友指的是:
出现在成员变量,方法输入,输出参数中的类称为成员朋友类,而出现在方法体内部的类不属于朋友类。

如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发
该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。


个人理解

首先是只跟朋友交谈

比如说科任老师有许多繁杂的事务要处理,像一些收作业、点名的琐事交给课代表来处理,而不直接与普通学生交谈;

因此科任老师和普通学生算陌生人,而与课代表算朋友;

接着是减少对朋友的了解,就是每个类尽量减少自己对外暴露的方法

比如说有两个类,一个是人,一个是咖啡机;

咖啡机制作咖啡通过三个步骤

  1. 加咖啡豆
  2. 加水
  3. 制造咖啡

而对于人来说,我们只想要获取咖啡,而不想关心是如何获取咖啡的;

也就是说,咖啡机对外只需要暴露制作咖啡的方法,而至于加咖啡豆、加水设置为私有即可;

总结

我觉得这个法则就是想让我们变成“肥宅”,肥在内部、宅在外头;肥在内部,也就是不需要的东西都搞成私有的,而宅在外头,则是暴露尽量少的接口给外头;

合成复用原则

是什么?

尽量先使用组合,实在不行再使用继承关系来实现;

这是面对对象系统中两种最常用的技术,类继承对象组合

类继承允许我们根据其他类的实现来定义一个类的实现,这种通过生成子类的复用通常称为"白箱复用",之所以称为白箱,是因为子类是可以看到父类的具体实现;
而对象组合是类继承之外的另一种复用选择,新的更复杂的功能可以通过组装或组合对象来获得;对象组合要求被组合的对象具有良好定义的接口;这种复用风格称为"黑箱复用",因为对象的内部细节是不可见的;

为什么?

继承复用虽然有简单和易实现的优点,但是缺点也是比较明显的;

  1. 继承复用破坏了类的封装性,因为继承会将父类的实现细节暴露给子类;
  2. 子类与父类的耦合度高,父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护
  3. 它限制了复用的灵活性,从父类继承而来的实现是静态的,在编译时已经定义,所以在运行时不可能发生变化

对于第二点来说,一个可用的解决办法就是只继承抽象类,因为抽象类通常具有较少的实现;


而对象组合的优点如下

  1. 对其他对象的引用是在运行时动态定义的;
  2. 因为对象只能通过接口访问,因此不破坏封装性;
  3. 因为对象的实现是基于接口写的,因此在实现上具有较少的依赖关系;
  4. 优先使用对象组合有助于类和类继承层次保持较小规模;

例子

汽车按“动力源”划分可分为汽油汽车、电动汽车等;按“颜色”划分可分为白色汽车、和红色汽车等。

如果通过继承的方式来实现,那么类图如下;在这里插入图片描述
从上面类图我们可以看到使用继承复用产生了很多子类,如果现在又有新的动力源或者新的颜色的话,就需要再定义新的类;

接着我们尝试将继承换成组合;

在这里插入图片描述
这样显然更加适合;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值