单一职责原则(Single  Responsibility Principle)简称SRP原则。


定义

  • 应该有且仅有一个原因引起类的变更。


优点

  • 可以降低类的复杂度,一个类只负责一项职责,其逻辑肯定要比负责多项职责简单的多;

  • 提高类的可读性,提高系统的可维护性;

  • 变更引起的风险降低,变更是必然的,如果单一职责原则遵守的好,当修改一个功能时,可以显著降低对其他功能的影响。


说明

  • 单一职责原则不只是面向对象编程思想所特有的,只要是模块化的程序设计,都适用单一职责原则;

  • 单一职责原则要根据项目的实际情况去划分职责粒度,职责粒度划分太细容易引起职责扩散容易增加管理复杂度,职责粒度划分太粗则容易增高变更引起的风险;


示例

背景

假设有一做产品的小公司,因为是创业阶段,由于资金有限,公司机制不健全并且每个职工负责的职能较多,职工种类和职责分配如下表:

职工种类
职责分工
研发职工 需求分析,产品定义,产品设计,产品研发
销售职工 产品销售

假设公司由老板亲自驱动研发和销售的职工进行运作,运作流程为产品需求分析->产品定义->产品设计->产品开发->产品销售;

代码

首先看普通的代码实现:

package SRP.Example;//公司职工类public abstract class Staff{	private String name; //员工姓名	private int salary;  //员工薪资		//构造函数	public Staff(String name)	{		this.name = name;	}		//获取员工姓名	public String getName()	{		return name;	}		//获取员工薪资	public int getSalary()	{		return salary;	}		//设置员工薪资	public void setSalary(int salary)	{		this.salary = salary;	}}

package SRP.Example;//研发职工职责接口public interface IDevResponsibility{	public void requirementsAnalysis(); //需求分析	public void productDefine();        //产品定义	public void productDesign();        //产品设计	public void productDevelop();       //产品开发}
package SRP.Example;//销售职工职责接口public interface ISaleResponsibility{	public void productSale();  //产品销售}
package SRP.Example;//研发职工类public class DevelopStaff extends Staff implements IDevResponsibility{		public DevelopStaff(String name)	{		super(name);	}	@Override	public void requirementsAnalysis()	{		System.out.println(getName() +"进行产品需求分析");	}		@Override	public void productDefine()	{		System.out.println(getName() + "进行产品定义");			}		@Override	public void productDesign()	{		System.out.println(getName() +"进行产品设计");			}		@Override	public void productDevelop()	{		System.out.println(getName() + "进行产品开发");			}	}
package SRP.Example;//销售职工类public class SaleStaff extends Staff implements ISaleResponsibility{		public SaleStaff(String name)	{		super(name);	}	@Override	public void productSale()	{		System.out.println(getName() +"进行产品销售");		}	}
package SRP.Example;//公司老板public class Bosss{	public static void main(String args[])	{		Staff zhangsan = new DevelopStaff("张三");		zhangsan.setSalary(28000);		Staff lisi = new DevelopStaff("李四");		lisi.setSalary(15000);		Staff wangwu = new SaleStaff("王五");		wangwu.setSalary(8000);				IDevResponsibility devResponsibility = (IDevResponsibility) zhangsan;		IDevResponsibility productResponsibility = (IDevResponsibility) lisi;				//此时张三,李四具有的职责是同样的,只是老板的分工不同				productResponsibility .requirementsAnalysis();		productResponsibility .productDefine();		productResponsibility .productDesign();		devResponsibility.productDevelop();					ISaleResponsibility saleResponsibility = (ISaleResponsibility) wangwu;		saleResponsibility.productSale();	}}

执行结果:

李四进行产品需求分析

李四进行产品定义

李四进行产品设计

张三进行产品开发

王五进行产品销售

分析

对于上述代码,如果公司不断发展要增加一类产品职员,用来负责产品的需求分析,产品定义和产品设计。那么上述代码应该如何应对需求变更?一般情况下的做法是先生成一个Staff类的ProductStaff子类,让后让ProductStaff子类实现研发职责接口IDevResponsibility中:

public void requirementsAnalysis();     //需求分析

public void productDefine();            //产品定义

public void productDesign();            //产品设计

然后空实现:

public void productDevelop();           //产品开发

最后在Boss类中去掉DevelopStaff的相关职责功能,然后增加新的ProductStaff对象替代DevelopStaff中去掉的职责,具体代码如下:

package SRP.Example;//产品职工类 public class ProductStaff extends Staff implements IDevResponsibility{		public ProductStaff(String name)	{		super(name);	}	@Override	public void requirementsAnalysis()	{		System.out.println(getName() + "进行产品需求分析");	}		@Override	public void productDefine()	{		System.out.println(getName() + "进行产品定义");	}		@Override	public void productDesign()	{		System.out.println(getName() + "进行产品设计");	}		@Override	public void productDevelop()	{		System.out.println(getName() + "不做任何工作");	}	}
package SRP.Example;//公司老板public class Bosss{	public static void main(String args[])	{		Staff zhangsan = new DevelopStaff("张三");		zhangsan.setSalary(28000);		Staff lisi = new ProductStaff("李四");		lisi.setSalary(15000);		Staff wangwu = new SaleStaff("王五");		wangwu.setSalary(8000);						IDevResponsibility devResponsibility = (IDevResponsibility) zhangsan;		IDevResponsibility productResponsibility = (IDevResponsibility) lisi;				//此时张三,李四职责从需求上讲是不一致的,但是程序却表现为一致的		//devResponsibility.requirementsAnalysis();   //此时仍然可以调用		productResponsibility .requirementsAnalysis();		//devResponsibility.productDefine();          //此时仍然可以调用		productResponsibility .productDefine();		//devResponsibility.productDesign();          //此时仍然可以调用		productResponsibility .productDesign();		devResponsibility1.productDevelop();		//productResponsibility .productDevelop();    //此时仍然可以调用					ISaleResponsibility saleResponsibility = (ISaleResponsibility) wangwu;		saleResponsibility.productSale();	}}

运行结果:

李四进行产品需求分析

李四进行产品定义

李四进行产品设计

张三进行产品开发

王五进行产品销售

修改该之后的代码也能完成任务,可是代码会变得晦涩难懂,此时的研发职工,仍然可以调用产品职工的相关职责,但是老板需要知道这些不用再去调用了;而且在看ProductStaff类的时候会变得理解困难为什么一个产品职工要实现研发职工的接口呢?

优化

鉴于以上问题,我们就需要对接口进行单一职责处理;首先将接口根据实际的分析进行职责粒度细化,本示例中,我们分析道可能会有一个产品职工种类出现,因此在接口设计阶段把职责首先划分为3类:产品职工职责,研发员工职责和销售员工职责;

package SRP.Example;//研发职工职责接口设计public interface IDevResponsibility{	public void productDevelop();        //产品开发}
//销售职工工职责接口设计public interface ISaleResponsibility{	public void productSale();  //产品销售}
package SRP.Example;//产品职工职责接口设计public interface IProductResponsibility{	public void requirementsAnalysis();    //需求分析	public void productDefine();           //产品定义	public void productDesign();           //产品设计}
package SRP.Example;//研发职工类public class DevelopStaff extends Staff implements IDevResponsibility,IProductResponsibility{		public DevelopStaff(String name)	{		super(name);	}	@Override	public void requirementsAnalysis()	{		System.out.println(getName() +"进行产品需求分析");	}		@Override	public void productDefine()	{		System.out.println(getName() + "进行产品定义");			}		@Override	public void productDesign()	{		System.out.println(getName() +"进行产品设计");			}		@Override	public void productDevelop()	{		System.out.println(getName() + "进行产品开发");			}	}

当变更发生时

package SRP.Example;//公司老板public class Bosss{	public static void main(String args[])	{		Staff zhangsan = new DevelopStaff("张三");		zhangsan.setSalary(28000);		Staff lisi = new ProductStaff("李四");		lisi.setSalary(15000);		Staff wangwu = new SaleStaff("王五");		wangwu.setSalary(8000);				IDevResponsibility devResponsibility = (IDevResponsibility) zhangsan;		IProductResponsibility productResponsibility = (IProductResponsibility) lisi;		//此时张三,李四,职责从需求上讲是不一致的,并且程序上也表现为不一致		//这样Boss类就不会调用错误对象的错误功能		//devResponsibility1.requirementsAnalysis();  //此时将不能调用		productResponsibility.requirementsAnalysis();		//devResponsibility1.productDefine();//此时将不能调用		productResponsibility.productDefine();		//devResponsibility1.productDesign();//此时将不能调用		productResponsibility.productDesign();		//productResponsibility.productDevelop();//此时将不能调用		devResponsibility.productDevelop();					ISaleResponsibility saleResponsibility = (ISaleResponsibility) wangwu;		saleResponsibility.productSale();	}}

此时的代码将会显得更加逻辑合理,更加便于维护;


总结

  • 接口设计尽量按照单一职责原则进行设计

  • 类设计尽量按照只有一个原因引起类的变化进行设计

  • 设计是有限度的,不能无限的考虑未来的情况

单一职责设计的前提是建立在客户对使用对象有充分了解的情况下的,实际上为了提高了客户的工作效率而增加了客户的工作并且方便了自己的管理。