首页 > 设计模式七大原则,用大白话给面试官讲明白!
头像
程序员大帝
发布于 2020-07-13 14:11
+ 关注

设计模式七大原则,用大白话给面试官讲明白!

**

本文原创首发于我的公众号【程序员大帝】

同时我组织了一个技术学习群,帮助大家备战秋招,公众号内回复加群,群里各个都是人才,说话又好听

**
图片说明

前言

设计模式是前辈们用毕生心血专业填坑换来的经验,把这些经验加工精简,就成了设计模式,也就是套路。有了套路,就能让我们加快软件的开发速度和扩展性(听起来怎么好像是 PUA 的感觉)。

在这里插入图片描述

设计模式包含了大量的编程思想,真正掌握并不容易,在学习每种具体的模式之前,咱们今天先来看看需要遵循的七大原则。相信大家耐心看了之后肯定有收获,码字不易,别忘了「在看」,「转发」哦。

  • 单一职责原则

  • 开闭原则

  • 里氏替换原则

  • 依赖倒转原则

  • 合成复用原则

  • 接口隔离原则

  • 迪米特法则

正文

01 单一职责原则

单一职责原则( Single Responsibility Principle )为我们提供了一个编写程序的准则,目的是把导致其变更因素的缩减到最少(比如你约妹子吃饭,起码提前看看天气预报再约时间)。

如果一个类承担的职责过多,就等于把这些职责耦合在一起。

一个职责的变化可能会影响或损坏其他职责的功能。而且职责越多,这个类变化的几率就会越大,类的稳定性就会越低。

所谓是闻道有先后,术业有专攻。做人也要避免样样都玩,样样都不精,在设计类的时候,要使其功能职责单一纯碎,把本职工作做到最好。

在这里插入图片描述

我们在软件开发中,需要面临很多任务,比如任务 T1,任务 T2,如果让一个类C负责这两个不同的任务。需求变更的时候,要对任务 T1 的代码修改来满足新的业务需求。

可会发现 T1 代码的修改竟然会导致原本能够正常运行的 T2 发生错误,而修复 T2 又不得不回过头再去对T1的修改,这便是因为类 C 的职责不够单一,把任务 T1 与任务 T2 耦合在一起导致的。

02 开闭原则

开闭原则( Open-Closed Principle )明确的告诉我们:软件实现应该对扩展开放,对修改关闭。

简单来说,功能应该通过扩展来实现变化, 而不是通过修改已有的代码来实现变化。

为什么要遵循开闭原则?

(1)开闭原则堪称是最基础的设计原则,是精神领袖。在 Java 语言中,抽象类就是开闭原则的一种非常好的体现。

(2)开闭原则可以提高维护性。一款应用的诞生到运行,开发软件可以说是一次性的工作,真正让人头疼的是在后期的维护与扩展。随着我们的应用越来越流行,用户越来越多,产品经理肯定要不停地提各种需求。

在这里插入图片描述

现在各大公司人员更新迭代速度明显加快,“每年输出一千名阿里工作10年以上的人才”。

连网友都翻出这句话编写段子:

“都是裁员,马老师说的就是那么有大局观。华为:放弃平庸员工。腾讯:结构性优化。百度:鼓励狼性,淘汰小资。蔚来:局部优化,提高运营效率。科大讯飞:提前吃饭的员工需要被优化。京东:淘汰掉因身体原因不能拼搏的员工。”

扯远了,如果你入职接手了别人的项目,如果让你从头开始读一遍原来的代码,然后再根据新的需求修改老代码,这是非常痛苦的事情,简直可以说是精神上的折磨和摧残。

但是如果我们仅仅是进行扩展,那么事情就会变得容易很多。

(3)开闭原则可以提高代码的复用性。在面向对象的设计中,所有的逻辑都是从原子逻辑组合而来,不是在一个类中独立实现一个业务逻辑。只有这样的代码才可以复用,粒度越小,被复用的可能性越大。

如何使用开闭原则?

万物皆发展变化,有变化就要有策略去应对,怎么快速应对呢?这就需要在设计之初考虑到所有可能变化的因素,然后留下接口,等待“可能”转变为“现实”。

使用 Java 编程时,抽象类和接口是这种思想最好的体现。通过对一组事物的通用描述,没有具体的实现,代表它可以有非常多的可能性,可以跟随需求的变化而变化。

同时我们为这些变化创建稳定的接口时,具体可以有两种操作方式:

将相同的变化封装到一个接口或抽象类中。

将不同的变化封装到不同的接口或抽象类中,不应该有两个不同的变化出现在同一个接口或抽象类中。

03 里氏替换原则

Java 中为我们提供了继承的实现,那么在整个系统中,里氏替换原则( Liskov Substitution Principle )指的是,如果一个地方可以接受某个基类,那么必然也可以接受这个基类的子类。

比如一个方法接收的参数是类型P,那么如果将其替换成 T 的子类 C,程序的行为不应该发生变化。

使用里氏原则也要遵循几个行为准则:

(1)假设类 B 继承类 A 时,B 可以添加新的方法完成新增的功能,但不要重写父类 A 的方法,也不要重载父类A的方法。

这里再说重载( overload )重写( override ) 的区别:

重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!

重载是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。

每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。

最常用的地方就是构造器的重载。

(2)如果随着业务的扩展,需要对基类 A 和子类 B 进行重构,应该让它们去继承一个更加通用的基类。

(3)里氏替换原则可以正着用,但是不能反着用,在子类出现的地方,父类未必可以胜任。

04 依赖倒转原则

大家刚听到这些名词都会觉得云里雾里,依赖倒转原则( Dependency Inversion Principle )简单来说就是:

(1)高层模块不应该依赖于低层模块,二者都应该依赖于抽象。

(2)抽象不应该依赖细节;细节应该依赖抽象。

还不明白?

其实大家在平时编程时候已经不自觉地使用了依赖倒转原则,核心思想就是面向接口编程。

接口就是一种抽象,只要不修改接口声明,大家可以放心大胆调用,至于接口的内部实现则无需关心,可以随便重构。

这里,接口就是抽象,而接口的实现就是细节。

只要接口是稳定的,那么任何一个的更改都不用担心其他的受到影响,这就使得无论高层模块还是低层模块都可以很容易地被复用。
在这里插入图片描述

依赖倒转原则是一种通识,具体用哪种语言来编写程序并不重要,设计的出发点就是为了应对变化的问题。

再举一个生活中的例子,电脑里的内存卡槽,其实就是一种接口。只要符合这个接口的要求,无论是用金士顿的内存,还是其它牌子的内存,无论是4G的,还是8G的,都可以很方便、轻松的插到电脑上使用。

在这里插入图片描述

05 接口隔离原则

通过上面几个规则我们已经知道,面向接口编程促进了代码的解耦,并使架构体系更加鲁棒。

但一个东西再好吃也要适度,所以就提出了接口隔离原则( Interface Segregation Principle )。主要目的是为了对接口的使用进行约束规范,它告诉我们要想把接口用好,关键在于隔离。

隔离,指断绝接触、断绝往来。

在这里插入图片描述

那么我们使用接口时,要隔离什么东西呢:

(1)客户端不应该依赖它不需要的接口,这个很容易理解,在实践中也很容易实现。

(2)类间的依赖关系应该建立在最小的接口上,它要求“最小的接口”,也就是该接口中没有多余的方法,所以这里的隔离是指和多余的方法隔离。

综上所述,接口隔离原则告诉我们,不要把一大堆方法塞进一个接口里,导致这个接口变得臃肿无比。应该要根据实际需要,让接口中只有用得上的方法,也就是说要细化我们的接口。

接口隔离原则的要点,就是要细化我们的接口。那么这样做具体有什么好处呢:

  • 避免接口污染
  • 提高灵活性
  • 提供定制服务
  • 实现高内聚

06 合成复用原则

合成复用原则(Composite Reuse Principle)字面意思说的很清楚,写代码时应该尽量使用对象组合,而不是使用继承来达到复用的目的。

使用继承来实现复用非常简单,但没有足够的灵活性,只能在有限的环境中使用。

假设一个子类继承了父类的所有方法,但是如果父类当中有方法被改变了,子类当中就找不到相应的方法,造成子类出错。

为了避免这种情况的出现,第一是父类专门为扩展而设计,几乎不会改变,并提供完整的文档,第二是父类和子类在同一个包下面,都由同一个程序员负责,保证好父类的改变能够及时通知到子类当中。

显而易见,无论哪种方式都不够灵活。

组合复用的耦合度相对较低,可以在运行时动态选择性地调用成员对象进行操作,使系统更加灵活,一个类的变化对其他类造成的影响相对较少。

因此一般首选使用组合/聚合来实现复用;其次才考虑继承,在使用继承时,需要严格遵循里氏代换原则,有效使用继承会有助于对问题的理解,降低复杂度,而滥用继承反而会增加系统构建和维护的难度以及系统的复杂度,因此需要慎重使用继承复用。

07 迪米特原则

迪米特原则(Law of Demeter)也被称为最少知识原则,一个对象应该对其他实体依赖越少越好。迪米特法则的核心在于类间的解耦,只有弱耦合之后类的复用率才会提高。

我们用人际交往来说,大家平时肯定和自己朋友交流得最多,但朋友之间也是有距离的。

对别人不能暴露太多,否则二次修改的时候,会让影响的范围增大,这也要求类间public方法不能肆无忌惮的暴露。

信息的隐藏可以使各个子系统之间脱耦,从而允许它们独立地被开发、优化、使用和修改。

依赖的模块越少,它能够独立在其他地方使用的能力越强。构建一个大规模的系统时,信息的隐藏显得尤其重要。

在这里插入图片描述

如果模块A和模块B都强依赖于模块C,那么A和B的开发进度都可能就会需要跟着C来走,一个功能C无法完成,A和B也无法继续开发。

同时如果和陌生人交往时,要抱有一定的戒心,有互相相信的朋友作为中间人最好。

如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用,而是通过引入一个第三者发生间接交互。

在这里插入图片描述

**

最后欢迎关注公众号「程序员大帝」加群,大家一起交流探讨获得满意Offer

**

公众号内还整理 《Java核心知识点整理.pdf》, 面试突击全都靠它了,好东西还是要分享出来给大家一起学习呀~

内容覆盖很广:Java 核心基础、Java 多线程、高并发、Spring、微服务、Netty 与 RPC、Zookeeper、Kafka、RabbitMQ、HBase、设计模式、负载均衡、分布式缓存、Hadoop、Spark、Storm、云计算等。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

全部评论

(0) 回帖
加载中...
话题 回帖

相关热帖

近期热帖

近期精华帖

热门推荐