软件架构设计原则

开闭原则

开闭原则(Open-Closed Principle,OCP)是指一个软件实体,应该对扩展的开放的,对于修改是关闭的。开闭就是指拓展与修改两个行为。什么意思呢?就是说对于一个Java对象来讲,你可以去继承它方法和属性,对继承类进行拓展,但是不可以直接修改它的方法和属性。这样提高了软件系统的可复用性和可维护性。

开闭原则作为面向对象设计的基本原则,直接对接了面向对象的三大特性,继承、封装、多态。可以在不修改源代码的情况下实现新增功能。

简单的举个例子,宠物 Pets,有名字、年龄大小、毛色等属性

public class Pets{		private String name;  	private Integer age;    private String color;}

整个宠物圈,可能有小猫、小狗、小猪等等一些宠物。例如下面来建立一个一个猫的宠物类型,它除了上面的属性之外,还有一个动作就是吃。这个时候可以继承Pets类然后给小猫一个吃的方法。

public class CatPets extends Pets{		public void eat(){    }}

对于小狗、小猪也是以同样的方式创建,这个时候我们可以知道,猫也有很多的更细的分类。这个时候我们对于每个猫的修改就是继承CatPets进行更加细致的分类了。而不是去修改顶层的Pets类型。

依赖倒置原则

依赖倒置原则(Dependence Inversion Principle,DIP)是指在设计代码结构的时候,高层次的模块不应该依赖低层次的模块,二者都应该依赖其抽象。抽象内容不应该依赖具体的细节性的内容。通过依赖倒置,可以减少类对象与类对象之间的耦合性。这样可以提高系统的稳定性,提高代码的可读性和可维护性,并且能够降低修改程序所造成的后续风险。

拿上面的宠物的例子来讲,先创建一个人的对象Person类

public class Person{		// 养猫  	public void keepCarPets(){    }    // 养狗    public void keepDogPets(){    }}

这个人非常喜欢养宠物,他养了猫和狗两种宠物。但是如果这个时候他看到一个宠物猪也比较好,他想养宠物猪怎么去做呢?就需要在Person类中增加 keepPigPets()的方法。这样的做法就是修改了高层次的代码,如果这样修改的话每次他想养新的宠物的时候,就要去新增一个方法,如果修改的内容较多的话,就会是一个非常复杂的工程了。

这个时候就可以通过下面这种方式来实现了

public interface KeepPets{		public void keeyPets(Pets pets){    }}

定义一个接口类,然后这个人继承这个接口类,接口类中有一个方法就是keepPets(Pets pets)养宠物,这个时候,如果想去养新的宠物的时候就可以通过如下的方式实现了

public class Person interface KeepPets{ 		  public void keeyPets(Pets pets){    }}

public class Test{		public static void main(String[] args){    		Person person = new Person();        person.keeyPets(new CatPets());      	person.keeyPets(new DogPets());      	person.keeyPets(new PigPets());    }  }

这个时候就可以看到,无论想养多少宠物的时候都不会再害怕了。只需要告诉养什么就可以了,而不需要修改底层的代码。实际上这就是依赖注入。注入的方式还有很多,通过构造函数注入,通过Setter方法注入等方式。

通过构造器注入的时候,在调用的期间每次都会创建新的实例,如果是一个全局单实例的对象话就只能通过Setter方式进行注入。

切记:以抽象为基准比以细节为基准搭建起来的架构要稳定很多,因此在拿到需求之后一定要面向接口进行编程,先设计顶层再设计细节。

单一职责原则

单一职责(Simple Responsibility Pinciple,SRP)是指不要存在多余一个导致类变更的原因。什么意思呢?假设我们又一个类负责的是两个功能,一旦发生需求变化的时候,修改其中一个功能就会导致另一个功能也发生了故障。这样一来,一个类就存在了两个导致类变更的原因。那么如何解决这个问题呢?解耦。后期需求变更之后相互之后不会产生影响。这样的设计方式可以大大降低类的复杂度,提高可读性,提高系统的可维护性,降低了变更代码之后引起的风险。总体来说就是一个类只负责一个功能。

接口隔离原则

接口隔离原则(Interface Segregation Principle,ISP)是指用多个专门的接口,而不是单一的使用一个接口。客户端不应该依赖他不需要的接口。在实现这个设计原则的时候需要注意以下的几点。

  1. 一个类对另一个类的依赖应该建立在最小的接口上
  2. 建立一个单一的接口,不需要一个庞大的总接口
  3. 尽量让接口细化,减少接口中的方法,但并不是越少越好。

接口隔离原则符合常说的高内聚、低耦合的设计思想。可以让类具有更好的可读性、可扩展性和可维护性。在设计接口的时候需要花点时间去思考,需要的业务模型,包括以后可能发生的变化的预判。

迪米特原则

迪米特原则(Law of Demeter LoD)是指一个对象应该对其他对象保持最小的了解。所以又叫最少知道原则。尽量降低类与类之间的耦合度。该原则主要强调:只和自己的朋友交流,不要和陌生人说话。出现在成员变量、方法和输入、输出参数中的类都可以称为朋友类,而出现在方法体内部的类则不属于朋友类。

例如,你作为一个学校的校长,你想要知道某个班的学生有多少人,你不需要亲自去数,而是只需要找到对应班级的班主任即可。如下

有一个学生类Student

public class Student{}

有一个班主任类

public class HeadMaster{			public Integer countStudent(List<Student> studentList){   						return classStudent.size();      }}

一个校长类

public class SchoolMaster{			public Integer studentNumber(HeadMaster headMaster){        		List<Student> studentList = new ArrayList<>();      			headMaster.countStudent(studentList);      }}

这个时候就实现了,校长与老师的关联,校长并不需要直接与每个学生发生关联。

里式替换原则

里式替换原则(Liskov Substitution Principle,LSP)是指如果每个类型为T1的对象O1,都有类型为T2的对象O2,使得以T1定义的所有程序P在所有的对象O1都替换成O2的时候,程序P的行为没有发生变化,那么类型T2就可以直接转换为T1。

这个定义看上去有点抽象,简单的理解为,一个软件实体如果适用于一个父类,那么一定使用与其子类,所有的引用了父类的地方必须能够透明的使用其子类对象,子类对象能够替代父类对象,但是程序的逻辑不变。可以简单的理解为子类可以继承父类的功能,但不能改变父类原有的功能。这也就是很多的地方会有一个注解@Override 的原因。

使用里式替换原则有如下的有点

1、约束继承泛滥,是开闭原则的一种体现

2、加强程序的健壮性,同时变更时可以做到非常好的兼容性,提高程序的可维护性,降低了需求变更的时候引入的风险。

合成复用原则

合成复用原则(Composite/Aggregate Reuse Principle,CARP)是指在开发过程中尽量使用对象组合、聚合而不是通过继承关系达到可复用的目的。这样可以使得系统更加灵活,降低类与类之间的耦合度,一个类的变化不会对其他类产生影响。

继承被称为白箱复用,相当于把所有父类的细节都暴露给子类,组合的方式则被称为黑箱复用。无法获取到类以外的对象的实现细节。但是这一设计需要严格遵守OOP模型。

总结

能将设计原则结合到自己的项目中,可以极大的提升代码的复用率,降低系统耦合度,提高程序拓展性,为后续的升级迭代打下坚实的基础。

声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2022年5月2日
下一篇 2022年5月2日

相关推荐