文档章节

软件设计原则

黄勇
 黄勇
发布于 2013/09/25 11:25
字数 2425
阅读 4610
收藏 127
点赞 17
评论 14

了解设计模式的朋友们,想必都听说过“六大设计原则”吧。其实最经典的 23 种设计模式中或多或少地都在使用这些设计原则,也就是说,设计模式是站在设计原则的基础之上的。所以在学习设计模式之前,很有必要对这些设计原则先做一下了解。

GoF(四人帮),传说中的四位大神们,他们联手搞出了一套设计模式,堪称 OOD(面向对象设计)的经典之作!震惊了整个软件开发领域。但这四个老家伙非常怪异,总是喜欢显摆一些高深的理论,甚至有时候不说人话,十分让人费解。

除了最经典的六大设计原则以外,还有一些其他的设计原则也非常重要,本文会一起列举,不断收集,不断更新。

我将尽可能地解释这些晦涩的理论,希望看完本文之后,会让您对这些设计原则稍微加深一些理解。若有不正确的地方,恳请大家指正!

一、六大设计原则

先看一幅图吧:

这幅图清晰地表达了六大设计原则,但仅限于它们叫什么名字而已,它们具体是什么意思呢?下面我将从原文、译文、理解、应用,这四个方面分别进行阐述。

1. 单一职责原则(Single Responsibility Principle - SRP)

原文:

There should never be more than one reason for a class to change.

译文:

永远不应该有多于一个原因来改变某个类。

理解:

对于一个类而言,应该仅有一个引起它变化的原因。说白了就是,不同的类具备不同的职责,各施其责这就好比一个团队,大家分工协作,互不影响,各做各的事情。

应用:

当我们做系统设计时,如果发现有一个类拥有了两种的职责,那就问自己一个问题:可以将这个类分成两个类吗?如果真的有必要,那就分吧。千万不要让一个类干的事情太多!

2. 开放封闭原则(Open Closed Principle - OCP)

原文:

Software entities like classes, modules and functions should be open for extension but closed for modifications.

译文:

软件实体,如:类、模块与函数,对于扩展应该是开放的,但对于修改应该是封闭的。

理解:

简言之,对扩展开放,对修改封闭。换句话说,可以去扩展类,但不要去修改类。

应用:

当需求有改动,要修改代码了,此时您要做的是,尽量用继承或组合的方式来扩展类的功能,而不是直接修改类的代码。当然,如果能够确保对整体架构不会产生任何影响,那么也没必要搞得那么复杂了,直接改这个类吧。

3. 里氏替换原则(Liskov Substitution Principle - LSP)

原文:

Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.

译文:

使用基类的指针或引用的函数,必须是在不知情的情况下,能够使用派生类的对象。

理解:

父类能够替换子类,但子类不一定能替换父类。也就是说,在代码中可以将父类全部替换为子类,程序不会报错,也不会在运行时出现任何异常,但反过来却不一定成立。

应用:

在继承类时,务必重写(Override)父类中所有的方法,尤其需要注意父类的 protected 方法(它们往往是让您重写的),子类尽量不要暴露自己的 public 方法供外界调用。

该原则由麻省理工学院的 Barbara Liskov 女士提出,她是美国第一位获取计算机博士学位的女性,曾经也获得过计算机图灵奖。

4. 最少知识原则(Least Knowledge Principle - LKP)

原文:

Only talk to you immediate friends.

译文:

只与你最直接的朋友交流。

理解:

尽量减少对象之间的交互,从而减小类之间的耦合。简言之,一定要做到:低耦合,高内聚。

应用:

在做系统设计时,不要让一个类依赖于太多的其他类,需尽量减小依赖关系,否则,您死都不知道自己怎么死的。

该原则也称为“迪米特法则(Law of Demeter)”,由 Ian Holland 提出。这个人不太愿意和陌生人说话,只和他走得最近的朋友们交流。

5. 接口隔离原则(Interface Segregation Principle - ISP)

原文:

The dependency of one class to another one should depend on the smallest possible interface.

译文:

一个类与另一个类之间的依赖性,应该依赖于尽可能小的接口。

理解:

不要对外暴露没有实际意义的接口。也就是说,接口是给别人调用的,那就不要去为难别人了,尽可能保证接口的实用性吧。她好,我也好。

应用:

当需要对外暴露接口时,需要再三斟酌,如果真的没有必要对外提供的,就删了吧。一旦您提供了,就意味着,您将来要多做一件事情,何苦要给自己找事做呢。

6. 依赖倒置原则(Dependence Inversion Principle - DIP)

原文:

High level modules should not depends upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions.

译文:

高层模块不应该依赖于低层模块,它们应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。

理解:

应该面向接口编程,不应该面向实现类编程。面向实现类编程,相当于就是论事,那是正向依赖(正常人思维);面向接口编程,相当于通过事物表象来看本质,那是反向依赖,即依赖倒置(程序员思维)。

应用:

并不是说,所有的类都要有一个对应的接口,而是说,如果有接口,那就尽量使用接口来编程吧。

将以上六大原则的英文首字母拼在一起就是 SOLID(稳定的),所以也称之为 SOLID 原则。

只有满足了这六大原则,才能设计出稳定的软件架构!但它们毕竟只是原则,只是四人帮给我们的建议,有些时候我们还是要学会灵活应变,千万不要生搬硬套,否则只会把简单问题复杂化,切记!

二、补充设计原则

1. 组合/聚合复用原则(Composition/Aggregation Reuse Principle - CARP)

当要扩展类的功能时,优先考虑使用组合,而不是继承。这条原则在 23 种经典设计模式中频繁使用,如:代理模式、装饰模式、适配器模式等。可见江湖地位非常之高!

2. 无环依赖原则(Acyclic Dependencies Principle - ADP)

当 A 模块依赖于 B 模块,B 模块依赖于 C 模块,C 依赖于 A 模块,此时将出现循环依赖。在设计中应该避免这个问题,可通过引入“中介者模式”解决该问题。

3. 共同封装原则(Common Closure Principle - CCP)

应该将易变的类放在同一个包里,将变化隔离出来。该原则是“开放-封闭原则”的延生。

4. 共同重用原则(Common Reuse Principle - CRP)

如果重用了包中的一个类,那么也就相当于重用了包中的所有类,我们要尽可能减小包的大小。

5. 好莱坞原则(Hollywood Principle - HP)

好莱坞明星的经纪人一般都很忙,他们不想被打扰,往往会说:Don't call me, I'll call you. 翻译为:不要联系我,我会联系你。对应于软件设计而言,最著名的就是“控制反转”(或称为“依赖注入”),我们不需要在代码中主动的创建对象,而是由容器帮我们来创建并管理这些对象。

三、其他设计原则

1. 不要重复你自己(Don't repeat yourself - DRY)

不要让重复的代码到处都是,要让它们足够的重用,所以要尽可能地封装。

2. 保持它简单与傻瓜(Keep it simple and stupid - KISS)

不要让系统变得复杂,界面简洁,功能实用,操作方便,要让它足够的简单,足够的傻瓜。

3. 高内聚与低耦合(High Cohesion and Low Coupling - HCLC)

模块内部需要做到内聚度高,模块之间需要做到耦合度低。

4. 惯例优于配置(Convention over Configuration - COC)

尽量让惯例来减少配置,这样才能提高开发效率,尽量做到“零配置”。很多开发框架都是这样做的。

5. 命令查询分离(Command Query Separation - CQS)

在定义接口时,要做到哪些是命令,哪些是查询,要将它们分离,而不要揉到一起。

6. 关注点分离(Separation of Concerns - SOC)

将一个复杂的问题分离为多个简单的问题,然后逐个解决这些简单的问题,那么这个复杂的问题就解决了。难就难在如何进行分离。

7. 契约式设计(Design by Contract - DBC)

模块或系统之间的交互,都是基于契约(接口或抽象)的,而不要依赖于具体实现。该原则建议我们要面向契约编程。

8. 你不需要它(You aren't gonna need it - YAGNI)

不要一开始就把系统设计得非常复杂,不要陷入“过度设计”的深渊。应该让系统足够的简单,而却又不失扩展性,这是其中的难点。

© 著作权归作者所有

共有 人打赏支持
黄勇

黄勇

粉丝 6171
博文 121
码字总数 215797
作品 1
浦东
CTO(技术副总裁)
加载中

评论(14)

amita
amita

引用来自“黄勇”的评论

引用来自“BillHuang”的评论

在代码中可以将子类全部替换为父类?是否讲反了?

多谢,确实写反了。现已纠正!里氏替换原则:父类能够替换子类,但子类不一定能替换父类。也就是说,在代码中可以将父类全部替换为子类,程序不会报错,也不会在运行时出现任何异常,但反过来却不一定成立。
看上去还是不太对劲。可以改成:父类能被替换为子类,但子类不一定能被替换为父类。
JFinal
JFinal
看到最少知识原则子标题以为与 JFinal 主张的最少概念原则是一回事,点进去看了一下却不是。本文是最有价值的博文,是内功心法,赞一个 79
叫悟空的猴子
叫悟空的猴子
关于里氏替换原则的解释->理解:
父类能够替换子类,但子类不一定能替换父类。也就是说,在代码中可以将父类全部替换为子类,程序不会报错,也不会在运行时出现任何异常,但反过来却不一定成立。这两句不是前后矛盾吗?
花落重来
花落重来
看着看着发现头像变了,看来你目前在电脑旁。3
黄勇
黄勇

引用来自“西部倒贴”的评论

引用来自“黄勇”的评论

引用来自“LovingJava”的评论

命令查询分离 是什么?能解释一下应用场景吗? 小白路过!

我是这样理解的,方法一般分为两种:一种是“命令”,另一种是“查询”。“命令”可改变对象的状态,可理解为“写操作”。“查询”不会改变对象的状态,只是一种查询而已,可理解为“读操作”。我们要尽量将“命令”与“查询”进行分离。希望我的回答能够帮助到您!

定义接口时命令与查询分离, 你说的分离是指功能级别的分离吧,代码不用分类吧?

没错!但具体情况还需具体分析。
zbbmaster
zbbmaster

引用来自“黄勇”的评论

引用来自“LovingJava”的评论

命令查询分离 是什么?能解释一下应用场景吗? 小白路过!

我是这样理解的,方法一般分为两种:一种是“命令”,另一种是“查询”。“命令”可改变对象的状态,可理解为“写操作”。“查询”不会改变对象的状态,只是一种查询而已,可理解为“读操作”。我们要尽量将“命令”与“查询”进行分离。希望我的回答能够帮助到您!

定义接口时命令与查询分离, 你说的分离是指功能级别的分离吧,代码不用分类吧?
LovingJava
LovingJava

引用来自“黄勇”的评论

引用来自“LovingJava”的评论

命令查询分离 是什么?能解释一下应用场景吗? 小白路过!

我是这样理解的,方法一般分为两种:一种是“命令”,另一种是“查询”。“命令”可改变对象的状态,可理解为“写操作”。“查询”不会改变对象的状态,只是一种查询而已,可理解为“读操作”。我们要尽量将“命令”与“查询”进行分离。希望我的回答能够帮助到您!

这种场景 在提高系统性能 操作数据库的时候,对其读写分离 达到性能优化。老黄的解释我一看就明白了!
黄勇
黄勇

引用来自“LovingJava”的评论

命令查询分离 是什么?能解释一下应用场景吗? 小白路过!

我是这样理解的,方法一般分为两种:一种是“命令”,另一种是“查询”。“命令”可改变对象的状态,可理解为“写操作”。“查询”不会改变对象的状态,只是一种查询而已,可理解为“读操作”。我们要尽量将“命令”与“查询”进行分离。希望我的回答能够帮助到您!
LovingJava
LovingJava
命令查询分离 是什么?能解释一下应用场景吗? 小白路过!
RyanHoo
RyanHoo

引用来自“黄勇”的评论

今天补充了一些设计原则,还有哪些设计原则,请大家也来补充一下吧!@BillHuang @RyanHoo

精华!
设计模式六大原则(6):开闭原则

遵循开闭原则设计出的模块具有两个主要特征: 对于扩展是开放的(Open for extension)。这意味着模块的行为是可以扩展的。当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改...

LCZ777
2014/06/29
0
0
设计原则-开闭原则

开闭原则是面向对象的可复用设计的第一块基石, 它是最重要的面向对象设计原则. 开闭原则: 一个软件实体应当对扩展开放, 对修改关闭. 即软件实体应该尽量在不修改原有代码的情况下进行扩展. 在...

hell03W
2016/11/09
7
0
设计模式之基础知识

设计模式简介 设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式...

白志华
2015/10/20
64
0
写给大家看的设计书——读后笔记

《写给大家看的设计书》介绍了设计的四个基本原则:亲密性、对齐、重复、对比。作为一个软件“设计师”,我也来聊聊读过这本书之后,我对这四个原则的一点理解。 亲密性 亲密性原则是指:内涵...

winters1224
2017/09/20
0
0
java设计模式概述以及6大原则

设计模式(Design Patterns) ——可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代...

Carbenson
2015/08/31
110
0
设计模式(一)

设计模式(Design Patterns) ——可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代...

小风89
2016/07/18
4
0
架构设计目录

架构引用维基百科:软件体系结构是构建计算机软件实践的基础。与建筑师设定建筑项目的设计原则和目标,作为绘图员画图的基础一样,一个软件架构师或者系统架构师陈述软件构架以作为满足不同客...

zting科技
2017/01/10
0
0
Unix设计哲学原则(摘自《Unix编程艺术》)

看了《Unix编程艺术》,这个里面讲的观念对现有产品和项目的设计有很多借鉴意义,建议大家都能读读(不过中文版翻译的有点烂,看的有点纠结)。这里总结下几种原则。 Unix哲学的原则: --模块...

IT熊猫
2015/01/21
338
0
开放关闭原则

1.开放封闭原则(OCP,Open Closed Principle)是所有面向对象原则的核心。软件设计本身所追求的目标就是封装变化、降低耦合,而开放封闭原则正是对这一目标的最直接体现。其他的设计原则,很...

金馆长1
2015/12/13
16
0
Android也架构之一:白话软件设计中的六大原则

要了解软件设计,了解整个架构,就必须学习软件设计中的六大原则。基于所有的设计模式都是建议于这些软件设计原则的基础上发展而来的。 1、单一职责原则(Single Responsibility Principle,...

理工男海哥
2012/08/19
0
1

没有更多内容

加载失败,请刷新页面

加载更多

下一页

CoreText进阶(七)-添加自定义View和对其

CoreText进阶(七)-添加自定义View和对其 效果  实现代码如下: - (void)viewDidLoad { [super viewDidLoad]; self.edgesForExtendedLayout = UIRectEdgeNone; self.view.bac......

aron1992
4分钟前
0
0
Python爬虫 爬取百合网的女人们和男人们

学Python也有段时间了,目前学到了Python的类。个人感觉Python的类不应称之为类,而应称之为数据类型,只是数据类型而已!只是数据类型而已!只是数据类型而已!重要的事情说三篇。 据书上说...

p柯西
15分钟前
0
0
在Java中,你真的会日期转换吗

1.什么是SimpleDateFormat 在java doc对SimpleDateFormat的解释如下: SimpleDateFormatis a concrete class for formatting and parsing dates in a locale-sensitive manner. It allows fo......

Java小铺
24分钟前
0
0
Linux系统梳理---系统搭建(二):tomcat的安装和使用

上一章讲到JDK的安装使用,这一章主要记录下服务器tomcat的安装以及部署一个项目. 1.下载tomcat,这里下载的是apache-tomcat-8.5.32.tar.gz 2.创建文件夹,便于管理,和JDK一样,在usr目录下创建t...

勤奋的蚂蚁
34分钟前
0
0
ES15-聚合

1.Terms Aggregation 分组聚合 2.Filter Aggregation 过滤聚合

贾峰uk
35分钟前
0
0
【2018.07.19学习笔记】【linux高级知识 20.27-20.30】

20.27 分发系统介绍 20.28 expect脚本远程登录 20.29 expect脚本远程执行命令 20.30 expect脚本传递参数

lgsxp
38分钟前
0
0
10.32/10.33 rsync通过服务同步~10.35 screen工具

通过服务的方式同步要编辑配置文件:[root@linux-xl ~]# vim /etc/rsyncd.confport=873log file=/var/log/rsync.logpid file=/var/run/rsyncd.pidaddress=192.168.43.21[tes...

洗香香
41分钟前
0
0
与女儿谈商业模式 (3):沃尔玛的成功模式

分类:与女儿谈商业模式 | 标签: 经济学 沃尔玛 陈志武 2007-05-10 09:09阅读(11279)评论(30) 与女儿谈商业模式 (3):沃尔玛的成功模式 陈志武 /文 沃尔玛(Wal-Mart)是另一个有意思的财...

祖冲之
48分钟前
0
0
网页加载速度优化方法总结

1、减少请求 最大的性能漏洞就是一个页面需要发起几十个网络请求来获取诸如样式表、脚本或者图片这样的资源,这个在相对低带宽和高延迟的移动设备连接上来说影响更严重。 2、整合资源 对开发...

Jack088
53分钟前
0
0
dubbo学习

https://blog.csdn.net/houshaolin/article/details/76408399

喵五郎
今天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部