单一职责
定义:每个软件模块都有一个且只有一个需要被改变的理由。
反例
public class Employee {
// calculatePay() 实现计算员工薪酬
public Money calculatePay();
// save() 将Employee对象管理的数据存储到企业数据库中
public void save();
// postEvent() 用于促销活动发布
public void reportHours();
}
以上的Employee类具备三个功能,计算薪酬、保存数据、活动发布。此时如果要增加一个功能,那么就需要改动Employee,并且有可能对其他的方法造成影响。
具体实现
// 财务行为
public class PayCalculator {
public Money calculatePay();
}
// 数据管理员行为
public class EmployeeSaver {
public void save();
}
// 销售行为
public class HourReporter {
public String reportHours();
}
每个功能独立一个类,保证每个类的改动互不影响。
核心
单一职责原则在架构层面用于奠定架构边界的变更轴心
开闭原则
定义:软件系统想要容易被改变,应该保持对扩展开放,对修改关闭。
这里的对扩展开放对修改关闭并不是指所有的类都不能修改,而且核心的类要做到能够扩展但是不允许修改。
示例
class FinacialReportInterceptor {
@Resource
private FinacialReportController finacialReportController;
@Resource
private FinacialData finacialData;
public void finacialReport() {
finacialReportController.screenPresenter();
finacialData.save();
}
}
class FinacialReportController {
public void screenPresenter() {
WebView web = new WebView();
}
public void printPresenter() {
PDFView pdf = PDFView()
}
}
class WebView {
}
class PDFVied {
}
class FinacialData {
public void save() {
}
}
以上的代码中FinacialReportInterceptor依赖FinacialReportController和FinacialData,FinacialReportInterceptor是整个逻辑的核心,这种单向的依赖关系改动FinacialReportController或者FinacialData并不会影响Interceptor的逻辑。
但是如果要新增核心逻辑是只要在Interceptor中新增一个逻辑即可,核心逻辑依赖的功能则依赖Controller和Data。
这样就保证了在Interceptor对扩展开放,对修改关闭(如果要修改只能修改依赖的controller或者data)
核心
系统易于扩展同时限制每次被修改的影响范围
里氏替换原则
定义:如果想用可替换的组件来构建软件系统,那么这些组件必须遵守同一个约定,以便让这些组件可以互相替换。
如何理解这句定义呢?
我的理解是要构建软件系统首先要有一个确定的标准,以该标准作为基础构建组件,那么基于相同的标准构建的组件就可以互相替换,这个在Java中就是接口和实现的关系。
Java中接口定义对外的实现标准,一个接口可以有多个实现,但是每个实现都必须按照接口定义的标准来实现具体的功能(比如接口方法的入参、出参),这样组件之间就可以基于接口的标准互相替换。
示例
public interface Shape {
Integer area();
}
public class Circle implements Shape {
@Override
public Integer area() {
}
}
public class Rectangle implements Shape {
@Override
public Integer area() {
}
}
示例中Circle和Rectangle按照Shape的标准实现了area,两个实现逻辑上是可以互相替换的。
核心
应用与软件架构层面
接口隔离原则
定义:软件设计中避免不必要的依赖
反例
public class OPS {
public void op1() {
}
public void op2(){
};
public void op3(){
};
}
public class User1 extends OPS{
public void test() {
this.op1();
}
}
public class User2 {
public void test() {
this.op2();
}
}
public class User3 {
public void test() {
this.op3();
}
}
以上代码中User1虽然不需要调用op2、op3,但在源代码层次上也与它们形成依赖关系,User1虽然不需要调用op2、op3,但在源代码层次上也与它们形成依赖关系。
接口隔离改动
public class OPS implements U1Ops,U2Ops,U3Ops{
@Override
public void op1() {
}
@Override
public void op2(){
}
@Override
public void op3(){
}
}
-- 通过设计独立的接口供外部使用,底层的实现通过OPS做具体的功能实现,这样外部只感知具体的方法不感知具体的实现
public interface U1Ops {
void op1();
}
public interface U2Ops {
void op2();
}
public interface U3Ops {
void op3();
}
public class User1 {
@Resource
private U1Ops u1Ops;
public void test() {
u1Ops.op1();
}
}
public class User2 {
@Resource
private U2Ops u2Ops;
public void test() {
u2Ops.op2();
}
}
public class User3 {
@Resource
private U3Ops u3Ops;
public void test() {
u3Ops.op3();
}
}
以上代码中通过接口隔离具体功能实现类,接口明确具体的功能,使用者只要明确接口能提供的功能即可。
这样就明确了该原则的核心【避免不必要的依赖】按需依赖
依赖翻转原则
定义:底层细节的代码应该依赖高层策略的代码(依赖高层的抽象接口)
如何理解这句话呢?
我的理解是当一个类使用另外一个类的实现不应该直接依赖该类,而且要依赖类的接口,避免与实现类强耦合不容易扩展。 具体体现
反例
class Lower {
@Resource
private Higher higher;
public void test() {
higher.test();
}
}
class Higher {
void test() {
}
}
上述代码中Lower直接依赖Higher,如果后续Higher做了调整,需要依赖Higher2那么需要需要修改Lower的以来关系,如果使用接口,则体现如下:
public class Lower {
@Resource
private BaseService service;
public void test() {
//省略使用工厂模式获取具体的实现
service.test();
}
}
public interface BaseService {
void test();
String level();
}
class Higher implements BaseService{
@Override
public void test() {
}
@Override
public String level() {
return "Higher";
}
}
class Higher2 implements BaseService{
@Override
void test() {
}
@Override
public String level() {
return "Higher2";
}
}
以上代码中Higher和Higher2实现了BaseService,在Lower如果要做调整则基于获取的工厂可以动态调整,不需要修改Lower也实现了开闭原则。
架构要关注软件系统内部经常会变动的具体实现模块
- 争取在不修改接口的情况下为软件新增新的功能
- 抽象层尽量做到不做变更
基本守则
应用代码中多使用抽象接口,尽量避免使用多变的实现类
避免大量使用继承,继承关系式依赖关系最强的