搬砖方法论:Single Responsibility Principle(单一职责原则、SRP原则)

语言的差异

前言:语言本身是一件非常不稳定的表达工具,这也是为什么我们在沟通中需要观察对方的表情、肢体动作、给予的隐喻、提供的图像来进一步确定对方想表达的意思,加之语言的使用者和接收者因文化、职业、经历等不确定因素的影响,又会造成相同的语句表达出不同含义,这让语言的精确性再次下降。

定义

当我们用搜索引擎搜索 SRP原则或单一职责原则关键字,定义中使用频率最多的一句话就是:一个类应该只有一个发生变化的原因。

这不仅让读者陷入思索,其中所描述的原因到底是什么?是否可量化?

相同的原因不同的解释

为了进一步解释这个"原因",我们对其定义丰富一下:

  • 一个类应该只有一个发生变化的原因。
  • 每一个类都应该对程序功能的一个部分负责,此时它应该封装。该模块、类或函数的所有服务都应该与该职责紧密结合。
  • 将因相同原因而发生变化的事物聚集在一起。分开由于不同原因而改变的事物。

以上的三条定义说的都是一件事:单一职责原则。
这也能看出,这个发生变化的"原因"是基于一个集合。如果每个函数只做一件事是一个机器的零件,那单一职责中的"职责"也就是所说的“原因”就是这些零件组合起来的功能。

确定单一

既然我们已经从概念上统一了职责到底是什么,那么下一步就是从量化的角度确定如何保证职责单一。

分为如下三步

  • 建模
  • 编码对应的类(笔者开始阶段常用伪代码代替)
  • 将对应的类拆分为多个类直到不能拆分

建模:可以理解为对应需求的梳理和拆分,最终抽象(总结)出这个职责核心是做什么的,要依赖于哪些其他的职责。

应用

我们可以举个例子来说明,我们要做一个菜单界面功能,一般我们会这么写

public class MenuPanel
{
    public MenuPanel()
    {
        var menuData = GetMenuData();
        SetMenuDataAndUpdate(menuData);
    }

    public string GetMenuData()
    {
        // do something...
        return default;
    }

    public void SetMenuDataAndUpdate(string temp)
    {
        // do something...
    }
}

在这个MenuPanel类中负责显示Menu这个菜单界面,但是这个MenuPanel真的是仅仅负责显示吗?答案是否定的。

当得到策划需求的时候,可按照【确定单一】里面所说的3步骤进行如下操作

建模

  • 根据数据库的数据显示对应的菜单。
  • 数据方面需要:请求-验证-解析-整合-筛选等操作。相当于上述代码GetMenuData()函数。
  • UI方面需要:接收数据-数据分类填充-适配布局-注册响应事件等操作。相当于上述代码SetMenuDataAndUpdate()函数。
  • 将上面数据与UI进行衔接操作。相当于上述代码MenuPanel()

拆分对应的类直到不能再拆分

  • 数据处理一个类
  • UI显示一个类
  • 数据与UI衔接一个类

经过拆分后的代码

public class ServerData
{
    public string GetNeedData()
    {
        /*
         * 请求 函数
         * 验证 函数
         * 解析 函数
         * 整合 函数
         * 筛选 函数
         */
        return default;
    }

}

public class MenuPanel
{
    private string m_NeedData;
    public MenuPanel(string needData)
    {
        m_NeedData = needData;
    }

    public void UpdateMenu()
    {
        // do something...
    }
}

public class EnterMenuPanelCommand
{
    public void Excute()
    {
        var serverData = new ServerData();
        var needData = serverData.GetNeedData();
        //TODO:正常情况下不应该直接传入基本类型,应该传入对应的自定义类,为了示例简单暂且代替
        var menuPanel = new MenuPanel(needData);
        menuPanel.UpdateMenu();
    }
}

经过一系列的操作我们得到三个职责:数据处理、UI刷新、数据与UI衔接三个职责。这样每一个类当职责变化,即只有一个发生变化原因时,我们才需要更改对应的类。

总结

SRP原则并不是彻底消灭腐朽代码的银弹,它只是降低出现代码坏的味道的几率,提高代码整洁的系数。SRP原则是一个指导性建议,并非强制要求,也切勿生搬硬套。


更多文章详见主页:www.aihailan.com

发表评论