设计模式-单一职责原则-实践运用

单一职责原则-概念

1、单一职责原则是最简单的面向对象设计原则,它用于控制类的粒度大小。

2、单一职责原则定义如下:

单一职责原则(Single Responsibility Principle, SRP):一个类只负责一个功能领域中的相应职责,或者可以定义为:就一个类而言,应该只有一个引起它变化的原因。

 

3、单一职责原则告诉我们:一个类不能太“累”!在软件系统中,一个类(大到模块,小到方法)承担的职责越多,它被复用的可能性就越小,而且一个类承担的职责过多,就相当于将这些职责耦合在一起,当其中一个职责变化时,可能会影响其他职责的运作,因此要将这些职责进行分离,将不同的职责封装在不同的类中,即将不同的变化原因封装在不同的类中,如果多个职责总是同时发生改变则可将它们封装在同一类中。

 

4、单一职责原则是实现高内聚、低耦合的指导方针,它是最简单但又最难运用的原则,需要设计人员发现类的不同职责并将其分离,而发现类的多重职责需要设计人员具有较强的分析设计能力和相关实践经验。

 

设计模式-单一职责原则-实践运用

 

最近,我在优化我以前参与的一个银行接口项目,运用了这个单一职责原则去优化代码。

 

以查询账户余额为例,业务步骤分为以下四个步骤:

(1)组装账户余额查询报文。

(2)发送账户余额查询报文到银行接口,得到银行返回的结果报文。

(3)解析银行返回的结果报文,封装成我们自身业务体系的结果类。

(4)将自身业务体系的结果类返回服务端。

 

未优化之前,在一个账号查询信息服务类当中,这个类承担了太多的职责。伪代码如下:

 

/**
 * 工行:账号查询信息服务
 *
 * @author chenlw
 * @date 2020/03/20
 */
public class IcbcAccountClientServiceImpl {
    /**
     * 查询账户余额
     */
    public void queryBalance(Object accountInfo) {
        // 1、组装账户余额查询报文
        System.out.println("组装账户余额查询报文");
        System.out.println("执行组装账户余额查询报文操作1");
        System.out.println("执行组装账户余额查询报文操作2");
        System.out.println("执行组装账户余额查询报文操作3");
        System.out.println("·····");

        // 2、发送账户余额查询报文到银行接口,得到银行返回的结果报文
        System.out.println("发送账户余额查询报文到银行接口");
        System.out.println("·····");

        // 3、解析银行返回的结果报文,封装成我们自身业务体系的结果类
        System.out.println("解析银行返回的结果报文");
        System.out.println("执行解析银行返回的结果报文操作1");
        System.out.println("执行解析银行返回的结果报文操作2");
        System.out.println("执行解析银行返回的结果报文操作3");
        System.out.println("·····");

        // 4、将自身业务体系的结果类返回服务端
        System.out.println("将自身业务体系的结果类返回服务端");
        System.out.println("·····");
    }
}

从代码当中可以看到,在queryBalance()查询账户余额业务方法当中,这个类方法承担了4个职责:组装账户余额查询报文、发送账户余额查询报文到银行接口、解析银行返回的结果报文、将自身业务体系的结果类返回服务端。

 

那么,类承担职责过多带来的不利影响是什么呢?有以下3个:

第一,不利于代码维护。因为它承担的职责太多,数据耦合度大,改动很可能影响整个业务流程的正常运行。

第二,不利于代码的复用。组装报文、发送报文、解析银行返回报文、返回结果到服务端这4个职责都在一个方法里边,这样代码复用的可能性几乎是没有的。

第三,不利于进行单元测试。4个职责都在一个方法里边,无法对单个职责进行单元测试,这对于提升代码维护性是很不利的。

 

所以,要拆分它的职责,细化职责。

 

拆分职责,细化职责

 

将这4个职责分离到不同的类当中,即将不同的变化原因封装在不同的类中,这一步就是单一职责的实践。

- 将组装报文的职责封装到组装报文类。

- 将发送报文的职责封装到发送报文类。

- 将解析银行返回报文的职责封装到解析银行报文类。

- 将返回结果到服务端的职责封装到结果发送器类。

 

1、组装报文类代码:

/**
 * 工行-查询余额-组装指令类
 *
 * @author chenlw
 * @date 2020/03/20
 */
public class IcbcQueryBalanceAssembler {

    /**
     * 组装查询余额报文
     *
     * @param accountInfo 账户信息
     * @return 查询余额报文
     */
    public Object getQueryBalanceOutput(Object accountInfo) {
        Object output = new Object();
        System.out.println("组装账户余额查询报文");
        System.out.println("执行组装账户余额查询报文操作1");
        System.out.println("执行组装账户余额查询报文操作2");
        System.out.println("执行组装账户余额查询报文操作3");
        System.out.println("·····");
        return output;
    }

}

2、发送报文类代码:

 

/**
 * 发送报文到银行接口类
 *
 * @author chenlw
 * @date 2020/03/20
 */
public class IcbcCommandSender {
    /**
     * 发送账户余额查询报文到银行接口
     *
     * @param output 账户余额查询报文
     * @return 银行接口返回报文
     */
    public Object sendQueryBalance(Object output) {
        Object bankResult = new Object();
        System.out.println("发送账户余额查询报文到银行接口");
        System.out.println("·····");
        return bankResult;
    }
}

3、解析银行报文类代码:

 

/**
 * 工行-查询余额-银行返回报文解析类
 *
 * @author chenlw
 * @date 2020/03/20
 */
public class IcbcQueryBalanceResolver {
    /**
     * 解析 余额查询银行返回报文
     *
     * @param input 余额查询银行返回报文
     * @return 自身业务体系的结果
     */
    public Object resolveQueryBalanceInput(Object input) {
        Object outResult = new Object();
        System.out.println("解析银行返回的结果报文");
        System.out.println("执行解析银行返回的结果报文操作1");
        System.out.println("执行解析银行返回的结果报文操作2");
        System.out.println("执行解析银行返回的结果报文操作3");
        System.out.println("·····");
        return outResult;
    }
}

4、结果发送器类代码:

/**
 * 指令结果发送器
 *
 * @author chenlw
 * @date 2020/03/20
 */
public class CommandResultSender {

    /**
     * 将自身业务体系的结果类返回服务端
     *
     * @param outResult 自身业务体系的结果
     */
    public void sendResultToServer(Object outResult) {
        System.out.println("将自身业务体系的结果类返回服务端");
        System.out.println("·····");
    }

}

拆分职责,细化职责后的账号查询信息服务类代码如下:

 

/**
 * 工行:账号查询信息服务
 *
 * @author chenlw
 * @date 2020/03/20
 */
public class IcbcAccountClientServiceImpl1 {

    private IcbcQueryBalanceAssembler icbcQueryBalanceAssembler = new IcbcQueryBalanceAssembler();
    private IcbcCommandSender icbcCommandSender = new IcbcCommandSender();
    private IcbcQueryBalanceResolver icbcQueryBalanceResolver = new IcbcQueryBalanceResolver();
    private CommandResultSender commandResultSender = new CommandResultSender();

    /**
     * 查询账户余额
     */
    public void queryBalance(Object accountInfo) {
        // 1、组装账户余额查询报文
        Object output = icbcQueryBalanceAssembler.getQueryBalanceOutput(accountInfo);
        // 2、发送账户余额查询报文到银行接口,得到银行返回的结果报文
        Object input = icbcCommandSender.sendQueryBalance(output);
        // 3、解析银行返回的结果报文,封装成我们自身业务体系的结果类
        Object outResult = icbcQueryBalanceResolver.resolveQueryBalanceInput(input);
        // 4、将自身业务体系的结果类返回服务端
        commandResultSender.sendResultToServer(outResult);
    }
}

从优化后的代码可以看到,这4个职责,已经优化为4行代码,即分别调用对应职责类的方法。

 

单一职责,该如何运用,在这里就体现的比较清楚了,即:

- 组装报文类:只负责报文的组装。

- 发送报文类:只负责将报文发送到银行接口端。

- 解析银行报文类:只负责解析银行返回的报文,将其转化成将自身业务体系的结果类。

- 结果发送器:只负责将结果返回给服务端。

 

那么我们再看看优化后的代码解决了什么痛点呢?

第一,优化后的代码已经比较松散耦合了,不同的职责封装在不同的类当中,代码可维护性和扩展性得到了提升,可读性也比较好。

第二,有利于代码的复用。因为职责已经分离了,这个类只做这个事情,就可以很好地复用代码。例如有其他业务也用到这个组装余额查询报文的职责,它可以直接调用这个类的方法,这样就实现了代码的复用。

第三,有利于进行单元测试。分离职责后,可以针对这4个职责进行更加细化的单元测试,确保功能的正确性。

 

以上,就是我对“单一职责原则”的实践活动。

 

总结

1、单一职责原则,确实是一个比较简单却又是很难运用的原则,需要在实际工作当中多思考多实践,多总结使用经验。

2、单一职责原则,这是一个架构层面的原则运用。运用好了,项目的可维护性、可扩展性、可读性将会得到很大的提升,这对于提升我们的项目架构设计水平、积累项目架构设计经验也是很有帮助的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值