面向对象六大原则
点击上方「蓝字」关注我们
这篇文章主要讲的是面向对象设计中,应该遵循的六大原则。只有掌握了这些原则,才能更好的理解设计模式。
我们接下来要介绍以下6个内容。
单一职责原则——SRP
开闭原则——OCP
里氏替换原则——LSP
依赖倒置原则——DIP
接口隔离原则——ISP
迪米特原则——LOD
0x01: 单一职责原则
单一职责原则的定义是就一个类而言,应该仅有一个引起他变化的原因。也就是说一个类应该只负责一件事情。如果一个类负责了方法M1,方法M2两个不同的事情,当M1方法发生变化的时候,我们需要修改这个类的M1方法,但是这个时候就有可能导致M2方法不能工作。这个不是我们期待的,但是由于这种设计却很有可能发生。所以这个时候,我们需要把M1方法,M2方法单独分离成两个类。让每个类只专心处理自己的方法。
单一职责原则的好处如下:
可以降低类的复杂度,一个类只负责一项职责,这样逻辑也简单很多
提高类的可读性,和系统的维护性,因为不会有其他奇怪的方法来干扰我们理解这个类的含义
当发生变化的时候,能将变化的影响降到最小,因为只会在这个类中做出修改。
0x02: 开闭原则
开闭原则和单一职责原则一样,是非常基础而且一般是常识的原则。开闭原则的定义是软件中的对象(类,模块,函数等)应该对于扩展是开放的,但是对于修改是关闭的。
当需求发生改变的时候,我们需要对代码进行修改,这个时候我们应该尽量去扩展原来的代码,而不是去修改原来的代码,因为这样可能会引起更多的问题。
这个准则和单一职责原则一样,是一个大家都这样去认为但是又没规定具体该如何去做的一种原则。
开闭原则我们可以用一种方式来确保他,我们用抽象去构建框架,用实现扩展细节。这样当发生修改的时候,我们就直接用抽象了派生一个具体类去实现修改。
0x03: 里氏替换原则
里氏替换原则是一个非常有用的一个概念。他的定义
如果对每一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有对象o1都替换成o2的时候,程序P的行为都没有发生变化,那么类型T2是类型T1的子类型。
这样说有点复杂,其实有一个简单的定义
所有引用基类的地方必须能够透明地使用其子类的对象。
里氏替换原则通俗的去讲就是:子类可以去扩展父类的功能,但是不能改变父类原有的功能。他包含以下几层意思:
子类可以实现父类的抽象方法,但是不能覆盖父类的非抽象方法。
子类可以增加自己独有的方法。
当子类的方法重载父类的方法时候,方法的形参要比父类的方法的输入参数更加宽松。
当子类的方法实现父类的抽象方法时,方法的返回值要比父类更严格。
里氏替换原则之所以这样要求是因为继承有很多缺点,他虽然是复用代码的一种方法,但同时继承在一定程度上违反了封装。父类的属性和方法对子类都是透明的,子类可以随意修改父类的成员。这也导致了,如果需求变更,子类对父类的方法进行一些复写的时候,其他的子类无法正常工作。所以里氏替换法则被提出来。
确保程序遵循里氏替换原则可以要求我们的程序建立抽象,通过抽象去建立规范,然后用实现去扩展细节,这个是不是很耳熟,对,里氏替换原则和开闭原则往往是相互依存的。
0x04: 依赖倒置原则
依赖倒置原则指的是一种特殊的解耦方式,使得高层次的模块不应该依赖于低层次的模块的实现细节的目的,依赖模块被颠倒了。
这也是一个让人难懂的定义,他可以简单来说就是
高层模块不应该依赖底层模块,两者都应该依赖其抽象
抽象不应该依赖细节
细节应该依赖抽象
在Java 中抽象指的是接口或者抽象类,两者皆不能实例化。而细节就是实现类,也就是实现了接口或者继承了抽象类的类。他是可以被实例化的。高层模块指的是调用端,底层模块是具体的实现类。在Java中,依赖倒置原则是指模块间的依赖是通过抽象来发生的,实现类之间不发生直接的依赖关系,其依赖关系是通过接口是来实现的。这就是俗称的面向接口编程。
下面有一个例子来讲述这个问题。这个例子是工人用锤子来修理东西。代码如下:
public class Hammer {
public String function(){
return "用锤子修理东西";
}
}
public class Worker {
public void fix(Hammer hammer){
System.out.println("工人" + hammer.function());
}
public static void main(String[] args) {
new Worker().fix(new Hammer());
}
}
这个是一个很简单的例子,但是如果我们要新增加一个功能,工人用 螺丝刀来修理东西,在这个类,我们发现是很难做的。因为我们Worker类依赖于一个具体的实现类Hammer。所以我们用到面向接口编程的思想,改成如下的代码:
public interface Tools {
public String function();
}
然后我们的Worker是通过这个接口来于与其他细节类进行依赖。代码如下:
public class Worker {
public void fix(Tools tool){
System.out.println("工人" + tool.function());
}
public static void main(String[] args) {
new Worker().fix(new Hammer());
new Worker().fix(new Screwdriver());
}
}
Hammer类与Screwdriver类实现这个接口
public class Hammer implements Tools{
public String function(){
return "用锤子修理东西";
}
}
public class Screwdriver implements Tools{
@Override
public String function() {
return "用螺丝刀修理东西";
}
}
这样,通过面向接口编程,我们的代码就有了很高的扩展性,降低了代码之间的耦合度,提高了系统的稳定性。
0x05: 接口隔离原则
接口隔离原则的定义是
客户端不应该依赖他不需要的接口
换一种说法就是类间的依赖关系应该建立在最小的接口上。这样说好像更难懂。我们通过一个例子来说明。我们知道在Java中一个具体类实现了一个接口,那必然就要实现接口中的所有方法。如果我们有一个类A和类B通过接口I来依赖,类B是对类A依赖的实现,这个接口I有5个方法。但是类A与类B只通过方法1,2,3依赖,然后类C与类D通过接口I来依赖,类D是对类C依赖的实现但是他们却是通过方法1,4,5依赖。那么是必在实现接口的时候,类B就要有实现他不需要的方法4和方法5 而类D就要实现他不需要的方法2,和方法3。这简直就是一个灾难的设计。
所以我们需要对接口进行拆分,就是把接口分成满足依赖关系的最小接口,类B与类D不需要去实现与他们无关接口方法。比如在这个例子中,我们可以把接口拆成3个,第一个是仅仅由方法1的接口,第二个接口是包含2,3方法的,第三个接口是包含4,5方法的。
这样,我们的设计就满足了接口隔离原则。
以上这些设计思想用英文的第一个字母可以组成SOLID ,满足这个5个原则的程序也被称为满足了SOLID准则。
0x06:迪米特原则
迪米特原则也被称为最小知识原则,他的定义
一个对象应该对其他对象保持最小的了解。
因为类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大,所以这也是我们提倡的软件编程的总的原则:低耦合,高内聚。
迪米特法则还有一个更简单的定义
只与直接的朋友通信。首先来解释一下什么是直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖、关联、组合、聚合等。其中,我们称出现成员变量、方法参数、方法返回值中的类为直接的朋友,而出现在局部变量中的类则不是直接的朋友。也就是说,陌生的类最好不要作为局部变量的形式出现在类的内部。
这里我们可以用一个现实生活中的例子来讲解一下。比如我们需要一张CD,我们可能去音像店去问老板有没有我们需要的那张CD,老板说现在没有,等有的时候你们来拿就行了。在这里我们不需要关心老板是从哪里,怎么获得的那张CD,我们只和老板(直接朋友)沟通,至于老板从他的朋友那里通过何种条件得到的CD,我们不关心,我们不和老板的朋友(陌生人)进行通信,这个就是迪米特的一个应用。说白了,就是一种中介的方式。我们通过老板这个中介来和真正提供CD的人发生联系。
总结
到这里,面向对象的六大原则,就写完了。我们看出来,这些原则其实都是应对不断改变的需求。每当需求变化的时候,我们利用这些原则来使我们的代码改动量最小,而且所造成的影响也是最小的。但是我们在看这些原则的时候,我们会发现很多原则并没有提供一种公式化的结论,而即使提供了公式化的结论的原则也只是建议去这样做。这是因为,这些设计原则本来就是从很多实际的代码中提取出来的,他是一个经验化的结论。怎么去用它,用好它,就要依靠设计者的经验。否则一味着去使用设计原则可能会使代码出现过度设计的情况。大多数的原则都是通过提取出抽象和接口来实现,如果发生过度的设计,就会出现很多抽象类和接口,增加了系统的复杂度。让本来很小的项目变得很庞大,当然这也是Java的特性(任何的小项目都会做成中型的项目)。
扫码二维码
获取更多精彩
Java乐园
本文分享自微信公众号 - JAVA乐园(happyhuangjinjin88)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
使用 Metal 技术驾驭 Apple 图形处理器
作者:MahomeAlex, iOS 开发工程师 Sessions: https://developer.apple.com/videos/play/wwdc2020/10602/ 前言 本文内容基于 WWDC 2020 Session 602 Harness Apple GPUs with Metal[1] 整理。 本 Session 主要讲的是 Metal 如何和 Apple 图形处理器特性相结合,为我们呈现完美的高性能 App 和游戏体验。因为讲的是 GPU (硬件),所以我们只谈渲染管线,不涉及代码。 当然希望您有基本的 Metal 和图形渲染知识,以便更好的理解本 Session 的内容。 概况 本 Session 主要讲两个部分。 TBDR GPUs 贴图延迟渲染 TBDR ( Tile Based Deferred Rendering ) 是一种更最高级的渲染架构,负责基本的管线渲染。移动端 GPU 渲染模式发展是: IMR 模式 TBR 模式 TBDR 模式 有兴趣的可以另行了解,这里不做展开。 最新 GPUs 对渲染管线的一些加强。 GPU 的重要性在于: 它是一系列...
- 下一篇
深度揭秘垃圾回收底层,这次让你彻底弄懂她
大家好,我是 yes。 我们知道手动管理内存意味着自由、精细化地掌控,但是却极度依赖于开发人员的水平和细心程度。 如果使用完了忘记释放内存空间就会发生内存泄露,再如释放错了内存空间或者使用了悬垂指针则会发生无法预知的问题。 这时候 Java 带着 GC 来了(GC,Garbage Collection 垃圾收集,早于 Java 提出),将内存的管理交给 GC 来做,减轻了程序员编程的负担,提升了开发效率。 所以并不是用 Java 就不需要内存管理了,只是因为 GC 在替我们负重前行。 但是 GC 并不是那么万能的,不同场景适用不同的 GC 算法,需要设置不同的参数,所以我们不能就这样撒手不管了,只有深入地理解它才能用好它。 关于 GC 内容相信很多人都有所了解。我最早得知有关 GC 的知识是来自《深入理解Java虚拟机》,但是有关 GC 的内容单看这本书是不够的。 当时我以为我懂很多了,后来经过了一番教育之后才知道啥叫无知者无畏。 而且过了一段时间很多有关 GC 的内容都说不上来了,其实也有很多同学反映有些知识学了就忘,有些内容当时是理解的,过一段时间啥都不记得了。 大部分情况是因为这...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7安装Docker,走上虚拟化容器引擎之路
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- CentOS关闭SELinux安全模块
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- Docker安装Oracle12C,快速搭建Oracle学习环境
- SpringBoot2全家桶,快速入门学习开发网站教程
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长