软件设计中的一些原则介绍
软件设计原则介绍:
软件设计的目的:代码复用性、可扩展性、可维护性。
高内聚低耦合(High Cohesion & Low coupling)
高内聚,就是对软件系统中元素职责相关性和集中度高的功能放一起。在编写代码时,代码内的功能职责相关性高的放在一起。
与之相反 - 低耦合,低耦合是对元素与元素之间的连接、依赖的程度轻重描述。这里元素是指函数、类(对象),也可以指功能,系统,子系统,组件,模块等。
低耦合就是在编写软件时,尽量做到类与类、文件与文件、模块与模块,系统与系统之间尽量的独立存在,互不干扰。
DRY原则(Don't Repeat Yourself)
DRY 原则是指在代码设计编写中避免重复代码。
在代码编写中不应该出现重复的逻辑、属性等,对某些功能已存在代码应该重复使用,不应该在多个实例中拷贝相同的代码片段。
避免重复代码可以提高代码的可维护性,当需求变更时只需要在一处修改就能生效。
关注点分离(Separation of concerns,SoC)
在计算机编程里,关注点分离(Separation of concerns,SoC),是将计算机程序分隔为不同部分的设计原则,每一部分会有自己关注的焦点,围绕焦点来进行内容的聚集。
在编码设计中,体现关注点分离原则的有程序的模块化。区分关注的焦点,围绕与焦点相关的操作,把它们封装在具有明确边界的程序代码段中。
还有分层设计也体现了关注点分离这一原则,比如 MVC 分层,M 模型层、V 表示层、C 控制层 。
关注点分离的目的是要简化程序开发和维护。
KISS原则(Keep It Simple, Stupid)
kiss 原则(Keep It Simple, Stupid)就是简单优于复杂。
不管是程序设计还是系统设计都应该尽量保持简单,不要过度设计,不要增加无用功能,因为这些都可能导致系统复杂度增加。
面向对象的SOLID原则
单一职责原则(Single Responsible Principle, SRP)
单一职责原则(Single Responsibility Principle,SRP)又称单一功能原则,一个类或模块只负责一项功能和业务逻辑。马丁把这里的职责指引起类变化的原因,单一职责规定一个类有且只有一个引起它变化的原因,否则类应该被拆分。
这个原则提出是由罗伯特·C·马丁(Robert Cecil Martin)在他的《敏捷软件开发,原则,模式和实践》一书中,一篇名为〈面向对象设计原则〉的文章中给出。马丁表述该原则是基于《结构化分析和系统规格》一书中的内聚原则(Cohesion)上。
该原则提出了一个类不应该承担太多职责。如果一个类承担太多职责,那么类中一个职责的变化也可能引起其它职责的变化。承担太多职责也会导致类过于复杂,不利于程序变更和维护。
开闭原则(Open Closed Principle,OCP)
开闭原则(Open Closed Principle,OCP):软件实体应该对扩展开放,对修改关闭。
这个原则是说当应用的需求改变时,优先通过扩展现有代码功能,使其满足需求。也即是说当增加或修改需求时,可以通过增加代码来满足需求的变化,而不是修改原来的代码满足需求变化。
实现方法的话,可以通过 “抽象约束、封装变化”,即是通过接口或抽象类来为软件实体定义一个相对稳定的抽象层,将相同的可变因素封装在相同的具体实现类中。因为抽象灵活适应强,进行合理的抽象,可以保持程序软件架构的稳定。把易变的程序部分,用继承扩展抽象的实现类来进行扩展,当需求变化时,从抽象来实现一个扩展功能就可以了。
里氏替换原则(Liskov Substitution Principle,LSP)
里氏替换原则(Liskov Substitution Principle,LSP):继承必须确保超类所拥有的性质在子类中仍然成立。
这个原则说明了使用继承的原则,反映了父类和子类的关系。子类可以扩展父类的功能,而不应该改变父类原有的功能。父类可以被子类替换。它也是实现开闭原则的重要方式之一。也是保证类扩展后不会给已有的程序系统引入新的错误。
具体实现方法的话,需求变化时,子类继承父类,在子类中添加新的方法应对需求变化,尽量不要重写父类中的方法。
依赖倒置原则(Dependence Inversion Principle,DIP)
依赖倒置原则(Dependence Inversion Principle,DIP):上层模块不应该依赖下层模块,两者应该依赖抽象;抽象不应该依赖细节,细节应该依赖抽象。
对上面这句话直接理解就是:面向接口(抽象)编程,不要面向实现编程
。通过引入抽象来减少程序间依赖,降低模块与模块、程序与程序之间的耦合。这也是上面 “高内聚低耦合” 原则中低耦合实现的重要方式。
在程序设计中,细节往往易变多变(实际上是需求变化多端引起程序细节变化),而良好抽象则相对稳定。因此通过良好的抽象来进行程序设计,比以细节为基础来进行程序设计往往要稳定。当然这是一个辩证关系,对细节了解足够多,才能进行良好抽象。
具体到面向对象语言设计中,抽象指的就是接口或抽象类,细节则是具体的实现类。利用接口或抽象类来制定规范或契约,而不涉及具体操作细节,把实现细节的任务交给实现类来完成。
接口隔离原则(Interface Segregation Principle,ISP)
接口隔离原则(Interface Segregation Principle,ISP):一个类对另一个类的依赖应该建立在最小的接口上。另外的定义就是程序不应该被迫依赖它不需要的方法。
建立在最小接口上,那可以对接口进行拆分处理,把庞大的接口拆分成更小接口和更具体的接口,让类实现仅依赖它需要的接口。而不是建立一个庞大的接口供所有依赖它的类去使用。拆分时候也要遵循高内聚低耦合原则和单一职责原则。
具体实现方法,就是上面说的接口拆分尽量小,但也不要无限小还是要掌握好度。一个接口只服务于一个业务逻辑,只提供调用者需要的方法。遵循内聚性,尽量减少与外界交互。
迪米特法则(Law of Demeter,LoD)
迪米特法则(Law of Demeter,LoD)又叫最少知道原则,只与你直接的朋友谈,不要跟陌生人说话。它的含义就是如果两个软件实体能不直接通信,就不要相互调用,可以通过第三方来实现调用。这个与前面的 “低耦合” 又相关,就是降低类与类、模块与模块之间的耦合度。
迪米特法则就是降低类与类之间的耦合度,提高类的相对独立性,提高复用性。
合成复用原则(Composite Reuse Principle,CRP)
合成复用原则(Composite Reuse Principle,CRP),它又叫聚合/组合原则。在程序设计时,尽量使用组合/聚合关系来实现程序功能,其次才考虑使用继承关系来实现功能。
合成复用同里氏替换两者相辅相成,都是开闭原则的具体实现规范。
为什么建议先用组合,后用继承,继承的一些缺点:
- 子类与父类耦合高。子类与父类具有某种连接,父类的实现细节继承给了子类,它们之间就出现连接,就耦合了。
- 限制了复用的灵活性。从父类继承而来的实现是静态,编译时已经定义,在运行时不可能发生改变。
控制反转IoC/依赖注入DI
控制反转(Inversion of Control,IoC),这个原则也是 “高内聚低耦合” 中低耦合的一种实现。
在面向对象程序设计中,应用程序都是通过两个或更多类,通过彼此合作来实现业务逻辑,这使得每个对象都需要获取与其合作的对象(也就是它依赖的对象)的引用,如果获取这个对象是靠自身实现,那么代码耦合度将变高。
具体实现方式有:依赖注入DI和依赖查找。
依赖注入在 Spring 框架中使用广泛,依赖注入实现方式又有好多种:
- 基于接口。实现特定接口以供外部容器注入所依赖类型的对象。
- 基于 set 方法。
- 基于构造函数。
- 基于注解。
依赖查找的方式,比如将框架提供的方法集中放在某个文件中,使用时在进行调用。
依赖稳定原则
前面提到的依赖接口,依赖抽象,都是依赖稳定的原则。
软件系统的构建要依赖稳定、可靠、变化小的程序。