大白话设计模式七大原则
Posted 程序员大帝
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了大白话设计模式七大原则相关的知识,希望对你有一定的参考价值。
点击蓝色“程序员大帝 ”关注我哟
加个“星标”,及时阅读最新技术文章
每日鸡汤,好喝
前言
设计模式是前辈们用毕生心血专业填坑换来的经验,把这些经验加工精简,就成了设计模式,也就是套路。有了套路,就能让我们加快软件的开发速度和扩展性(听起来怎么好像是 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也无法继续开发。
同时如果和陌生人交往时,要抱有一定的戒心,有互相相信的朋友作为中间人最好。
如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用,而是通过引入一个第三者发生间接交互。
程序员大帝
原创不易,感谢点赞、在看和转发的热心小伙伴~
以上是关于大白话设计模式七大原则的主要内容,如果未能解决你的问题,请参考以下文章