软件设计原则能够说是无数前辈在踩过无数坑以后总结出来的提醒后人遵循的一些基本思想、规范、模式。遵循这些原则,有利于咱们作出良好的设计,好比达到高内聚低耦合、模块划分清晰、源码可读性可维护性良好的效果。下面将对最广为人知的八大原则进行说明,有些原则还会给出代码示例。前端
6.1 KISS原则ajax
KISS是“keep it simple, stupid”的简称,这种观点认为,一个简单的系统每每比复杂的系统运转得更好,所以,在进行系统设计时应尽可能保持简单,避免没必要要的复杂性。 编程
这里强调的是没必要要的复杂性,有时候一些复杂性不可避免,但咱们不管在产品设计仍是架构设计方面,在保持功能完整性的同时,应该尽可能作减法,坚守KISS原则。后端
6.2 DRY原则设计模式
DRY是“Don't repeat yourself ”的简称,这个原则的目标在于经过抽象的手段来减小重复的模式,避免冗余,包括设计、代码等方面的冗余,强调系统中的每一个实体元素都是单一的、无歧义的。DRY原则运用好的话,系统中任何元素的修改都不会致使逻辑上跟它无关的元素的修改,而逻辑上相关的元素即便被修改,也是以统一的可预测的方式被修改。服务器
怎么作到遵照DRY原则呢?本人认为要依靠抽象思惟,咱们在设计系统和数据结构时,尽可能理解透彻系统中所存在的实体及其关系,将共性的内容抽出来成为一个抽象层,具体的实体再从抽象层派生,这里其实就对应了面向对象里面继承的概念,继承使得代码能够被复用。数据结构
6.3 开闭原则架构
开闭原则也叫Open-Close原则,大体思想是:软件实体(包括类、模块、函数等)应该对扩展开放,而对修改关闭。以类为例来讲明,就是你能够从一个基类派生新的类做为子类,子类能够扩展基类的功能,但不能修改基类的代码。开闭原则也是最基本的原则,这一章中全部其它原则自己都遵照了开闭原则。框架
现实中,不少软件中的插件,就是开闭原则思想的具体实践。一般容许你经过添加插件的方式来扩展软件的行为,而不容许修改软件的一些基础功能。若是容许你修改软件的基本功能,那么有一天,这个软件具有的功能和软件原来声称的功能截然不同,想一想都以为是件诡异的事情。前后端分离
6.4 里氏替换原则
里氏替换原则(Liskov substitution)能够表述为:全部出现基类的地方均可以由子类来代替,而且替换后不须要修改其它代码系统还能够继续正常工做。这样看来,里氏替换原则也符合开闭原则,不修改基类,而是将子类替换基类,就可以达到扩展系统功能的目的。
怎么运用这个原则呢?我的认为在设计系统时,要关注系统实体间的关系(好比是不是继承关系)和扩展性,这两个特性结合起来的地方可能就是使用了里氏替换原则。
6.5 依赖倒置原则
依赖倒置原则是软件模块解耦的一种方式,其核心思想是:高层模块不该该依赖于底层模块,它们都应该依赖于抽象,也就是抽象不能依赖于细节,细节应该依赖抽象。
先后端分离架构就是一个例子,前端只管使用ajax请求服务端的API,不在意服务器端API服务是如何实现和部署的,只须要API接口遵照协议(好比HTTP)就行了。
6.6 单一职责原则
顾名思义,单一职责原则的核心思想是:一个类,只作一件事情,只有一个理由引起它的改变。这也和Unix的设计哲学之一“Do One Thing and Do It Well”有殊途同归之妙,即只作一件事,并把它作好。
这个原则提及来最简单,而实际上执行起来不容易,稍微不当心就容易违背它。只作一件事,对应到软件设计里边,就是一个模块(类、函数)尽可能少作事情,但要作好,这样也符合咱们常说的高内聚低耦合的设计要求,因此在单一原则里边,咱们须要注意的是模块(类、函数)的边界和职责划分,思考怎样划分才能符合该原则,这里就不举例子了。
6.7 接口隔离原则
接口隔离原则是面向对象设计的一个原则,倡导将接口分离,即用户不须要实现他使用不到的接口。好比一个接口定义有两个方法:
interface ShapeInterface {
public function area();
public function volume();
}
而有时候实现类只使用到其中一个方法,但编程语言规范又迫使派生类去实现基类的两个接口。若是遵循接口隔离原则,这时候应该将接口分解为两个接口,而后实现类根据须要能够只实现其中一个接口,若是须要两个接口方法都实现,就去实现两个接口好了。代码以下:
interface ShapeInterface {
public function area();
}
interface SolidShapeInterface {
public function volume();
}
class Cuboid implements ShapeInterface, SolidShapeInterface {
public function area() { }
public function volume() {}
}
原则是一种思想,并不局限于某种编程语言,上面以Java代码为例来讲明,其余语言中能够根据各个语言的不一样规范采用不一样的方式来是接口符合这个原则。
6.8 最少知识原则
最少知识原则(Least Knowledge Principle)也叫迪米特法则(Law of Demeter),其来源于1987年荷兰大学的一个叫作Demeter的项目。Craig Larman把Law of Demeter又称做“不要和陌生人说话”,其核心思想是提倡一个模块只知道本身模块内部的东西,对其它模块知之甚少,与其它模块的交互都是经过接口来实现,这个原则能够引导咱们完成低耦合的系统设计。
上面说到的是模块,那其实咱们能够小到一个类,一个函数,均可以遵循最少知识原则。好比咱们尽可能将类的成员变量声明为私有的,只能经过类的公共方法来访问该变量,外部不能直接使用该变量。在函数里面,咱们尽可能使用局部变量,少用全局变量,由于局部变量只在这个函数内有效,全局变量能够共用,使得函数的耦合性变高了。所以,提倡使用局部变量,也是遵循了最少知识原则。
6.9 好莱坞明星原则
好莱坞明星原则叫IOC(Inversion of control)原则或者控制反转原则,英文里比较形象的说法就是“Don’t call me, I will call you back”,源于这么一个现象:好莱坞的经纪人们通常不但愿你去联系他们,而是他们会在须要的时候来联系你,由于主动权确实是掌握在他们手上,耍大牌不用商量。
传统的编程方式是,用户代码直接调用底层库函数,而控制反转原则是底层框架反过来调用用户编写的代码,相似基于事件的编程。当事件触发时,底层框架代码被调用,而后再反过来调用用户自定义的事件处理程序。这样来看的话,就是框架提供了程序的引擎和接口定义,用户代码负责接口实现,应用程序的开发模式就掌控在框架开发者的手中。引用台湾著名架构师高焕堂的话,就是好莱坞明星原则保证了强盛,保证了底层框架的龙头地位。
这里咱们举个简单的例子吧,好比咱们在进行GUI编程时,一般要为控件编写一个OnCreate函数,可是这个函数咱们从没有主动去调用它,而是框架在适当的时机来调用,这就是将控制权交给了底层框架,完成了控制反转。
6.10 面向接口原则
面向对象设计模式中有一个模式叫桥接模式(Bridge pattern),提倡基于接口编程,英文里边的说法是:Program to an interface, not an implementation。由于同一个接口能够衍生出各类不一样的实现,接口和实现分离使得用户程序能够根据不一样的状况选择不一样的实现,实现的修改独立于接口的调用者,这样当须要修改实现代码时,接口调用者是无感知的,这样也就下降了软件的耦合度。
下面咱们以Java代码为例来看一下如何面向接口编程,首先,咱们有一个显示接口,定义以下:
interface displayModule {
public void display();
}
显示器类,实现displayModule接口
public class Monitor implements displayModule{
public void display() {
System.out.println(“Display through Monitor”);
}
}
投影仪类,实现displayModule接口
public class Projector implements displayModule
{
public void display(){
System.out.println(“Display through projector”);
}
}
主机类,聚合了显示接口
public class Computer
{
// 面向接口编程
displayModule dm;
Public void setDisplayModule
(displayModule dm)
{
this.dm=dm;
}
public void display()
{
dm.display();
}
}
程序主函数,相似于一个装配器
public static void main(String args[])
{
Computer cm = new Computer();
displayModule dm = new Monitor();
displayModule dm1=new Projector();
cm.setDisplayModule(dm);
cm.display();
cm.setDisplayModule(dm1);
cm.display();
}
上面的例子中,Computer类聚合了displayModule 接口,而后经过一个方法setDisplayModule来设置具体的接口实现对象,好比显示器或者投影仪。主程序就相似于装配器,程序做者就像装配工人,根据须要装配不一样的插件(显示器或者投影仪对象)。若是Computer类聚合的不是接口而是某个实现类,也就是直接聚合某个插件,这样就不须要方法setDisplayModule了,可是失去了灵活性,装配工人也就没法根据须要装配不一样的插件了。
节选自本人做品:《漫谈中小企业研发技术栈》第六章。
欢迎关注公众号: