基本概念
单一职责原则(Single Responsibility Principle,SRP)
定义:应该有且仅有一个原因引起类的变更(There should never be more than one reason for a class to change)
优点:
- 降低类的复杂度;
- 提高类的可读性,因为类的职能单一,看起来比较有目的性
- 提高系统的可维护性,降低变更程序引起的风险
具体代码演示
例如下面的工厂类,负责 将原料进行预处理然后加工成产品X和产品Y:
public class Factory {
private String preProcess(String material){
return "*"+material+"*";
}
public String processX(String material) {
return preProcess(material) +"加工成:产品X";
}
public String processY(String material) {
return preProcess(material) +"加工成:产品Y";
}
}
现因市场需求,优化产品X的生产方案,需要改变原料预处理的方式,修改如下:
private String preProcess(String material){
return "#"+material+"#";
}
public class Client {
public static void main(String args[]) {
Factory factory = new Factory();
System.out.println(factory.processX("原料"));
System.out.println(factory.processY("原料"));
}
}
运行结果如下:
修改前:
*原料*加工成:产品X
*原料*加工成:产品Y
修改后:
#原料#加工成:产品X
#原料#加工成:产品Y
从运行结果中可以发现,在使产品X可以达到预期生产要求的同时,也导致了产品Y的变化,但是产品Y的变化并不在预期当中,这便导致程序运行错误甚至崩溃;
为了避免这种问题的发生,我们在软件开发中,应当注重各职责间的解耦和增强功能类的内聚性,来实现类的职责的单一性。
切断职责P1与职责P2之间的关联(解耦),然后将职责P1封装到功能类T1中,将职责P2封装到功能类T2中,使职责P1与职责P2分别由各自的独立的类来完成(内聚)。
按照单一职责原则可以将上面的工厂类按照以下方式进行分解:
定义一个抽象工厂类AFactory,有预加工preProcess和加工process方法:
public abstract class AFactory {
protected abstract String preProcess(String material);
public abstract String process(String material);
}
由工厂类FactoryX继承自AFactory,专门生产产品X:
public class FactoryX extends AFactory{
@Override
protected String preProcess(String material) {
return "*"+material+"*";
}
@Override
public String process(String material) {
return preProcess(material) +"加工成:产品X";
}
}
由工厂类FactoryY继承自AFactory,专门生产产品Y:
public class FactoryY extends AFactory{
@Override
protected String preProcess(String material) {
return "*"+material+"*";
}
@Override
public String process(String material) {
return preProcess(material) +"加工成:产品Y";
}
}
场景类中调用如下:
public class Client {
public static void main(String args[]) {
produce(new FactoryX());
produce(new FactoryY());
}
private static void produce(AFactory factory) {
System.out.println(factory.process("原料"));
}
}
现在优化产品X的生产方案,像之前那样改变原料预处理的方式,只需要改变类FactoryX中preProcess方法即可;
例如下面的工厂类负责将原料多次加工处理后生产产品X:
public class Factory {
private String preProcess(String material) {
return "*" + material + "——>";
}
private String process(String material) {
return preProcess(material) + "加工——>";
}
private String packaging(String material) {
return process(material) + "包装——>";
}
public String processX(String material) {
return packaging(material) + "产品X";
}
}
场景类中调用:
public class Client {
public static void main(String[] args) {
Factory factory = new Factory();
System.out.println(factory.processX("原料"));
}
}
现因业务拓展,工厂增加生产产品Y,产品Y与产品X除了包装不同之外,其它都一样,秉着代码复用,少敲键盘,高效完成工作的原则,对工厂类Factory进行拓展:
public class Factory {
private String preProcess(String material) {
return "*" + material + "——>";
}
private String process(String material) {
return preProcess(material) + "加工——>";
}
private String packaging(String material) {
return process(material) + "包装——>";
}
public String processX(String material) {
return packaging(material) + "产品X";
}
public String processY(String material) {
return packaging(material) + "产品Y";
}
}
场景类Client中,对代码进行调用:
public class Client {
public static void main(String[] args) {
Factory factory = new Factory();
System.out.println(factory.processX("原料"));
System.out.println(factory.processY("原料"));
}
}
运行结果如下:
*原料——>加工——>包装——>产品X
*原料——>加工——>包裝——>产品Y
因业务拓展,工厂除了生产产品X,还生产半成品,简单包装一下就可以了,不需要贴上产品X的商标。
因为有现有的预处理,加工,包装方法,为了方便起见,直接将类Factory中的packaging方法,有private改为public,然后由不同的场景类调用,代码如下:
public class Factory {
private String preProcess(String material) {
return "*" + material + "——>";
}
private String process(String material) {
return preProcess(material) + "加工——>";
}
public String packaging(String material) {
return process(material) + "包装——>";
}
public String processX(String material) {
return packaging(material) + "产品X";
}
}
public class Client1 {
public static void main(String[] args) {
Factory factory = new Factory();
System.out.println(factory.processX("原料"));
}
}
public class Client2 {
public static void main(String[] args) {
Factory factory = new Factory();
System.out.println(factory.packaging("原料"));
}
}
运行Client1与Client2后,结果如下:
*原料——>加工——>包装——>产品X
*原料——>加工——>包装——>
单一职责要求我们在编写类,抽象类,接口时,要使其功能职责单一纯碎,将导致其变更的因素缩减到最少。
按照这个原则对于工厂类Factory我们重新调整一下实现方案
首先,将四个职责抽取成以下四个接口:
//预处理接口
public interface IPreProcess {
String preProcess(String material);
}
//加工接口
public interface IProcess {
String process(String material);
}
//包装接口
public interface IPackaging {
String packaging(String semiProduct);
}
//产出成品接口
public interface IFactory {
String process(String product);
}
然后,有四个职责类分别实现这四个接口,如下:
//原料预处理类
public class PreProcess implements IPreProcess{
@Override
public String preProcess(String material) {
return "*" + material + "——>";
}
}
//加工类
public class Process implements IProcess {
private IPreProcess preProcess;
public Process(IPreProcess preProcess) {
this.preProcess = preProcess;
}
@Override
public String process(String material) {
return this.preProcess.preProcess(material) + "加工——>";
}
}
//包装类
public class Packaging implements IPackaging {
private IProcess process;
public Packaging(IProcess process) {
this.process = process;
}
@Override
public String packaging(String material) {
return this.process.process(material) + "包装——>";
}
}
//产出成品类
public class FactoryX implements IFactory{
private Packaging packaging;
public FactoryX(Packaging packaging) {
this.packaging = packaging;
}
@Override
public String process(String material) {
return this.packaging.packaging(material)+ "产品X";
}
}
场景类中调用代码如下:
public class Client {
public static void main(String[] args) {
IFactory factoryX = new FactoryX(new Packaging(new Process(new PreProcess())));
System.out.println(factoryX.process("原料"));
IFactory factoryY = new FactoryY(new Packaging(new Process(new PreProcess())));
System.out.println(factoryY.process("原料"));
}
}
如果因市场需求,优化产品X的生产方案,改变原料预处理的方式,可以增加一个原料预处理接口IPreProcess的实现类PreProcessA,如下:
//原料预处理类
public class PreProcessA implements IPreProcess{
@Override
public String preProcess(String material) {
return "#" + material + "——>";
}
}
public class Client {
public static void main(String[] args) {
IFactory factoryX = new FactoryX(new Packaging(new Process(new PreProcessA())));
System.out.println(factoryX.process("原料"));
IFactory factoryY = new FactoryY(new Packaging(new Process(new PreProcess())));
System.out.println(factoryY.process("原料"));
}
}
如果想生产产品X的半成品的话,不需更改生产代码,只需在场景类中直接调用即可:
public class Client {
public static void main(String[] args) {
IFactory factoryX = new FactoryX(new Packaging(new Process(new PreProcessA())));
System.out.println(factoryX.process("原料"));
IFactory factoryY = new FactoryY(new Packaging(new Process(new PreProcess())));
System.out.println(factoryY.process("原料"));
IPackaging packagingX = new Packaging(new Process(new PreProcess()));
System.out.println(packagingX.packaging("原料"));
}
}
即使哪天市场需求再变化,再优化产品X的生产方案,改变原料预处理的方式只需再添加个类,实现预处理接口IPreProcess即可,如下:
//原料预处理类
public class PreProcessB implements IPreProcess{
@Override
public String preProcess(String material) {
return "%" + material + "——>";
}
}
场景类更改如下:
public class Client {
public static void main(String[] args) {
IFactory factoryX = new FactoryX(new Packaging(new Process(new PreProcessB())));
System.out.println(factoryX.process("原料"));
IFactory factoryY = new FactoryY(new Packaging(new Process(new PreProcess())));
System.out.println(factoryY.process("原料"));
IPackaging packagingX = new Packaging(new Process(new PreProcessA()));
System.out.println(packagingX.packaging("原料"));
}
}
运行结果如下:
%原料——>加工——>包装——>产品X
*原料——>加工——>包装——>产品Y
#原料——>加工——>包装——>
总而言之,软件设计实际上就是发现职责并将职责相互分离,所以在编写代码时更多的应该考虑单一职责这种设计模式