大师我悟了,我总算搞懂了单一职责!

238 阅读3分钟

单一职责

单一职责是让我最纠结的.

因为单一职责的定义让我很迷惑:一个类或模块应该有一个且只有一个理由去改变。

查了很多的文章,文章中的案例好像在告诉我 一个类只有一个方法 才符合单一职责。

直到我发现了这两篇文章

Single Responsibility Principle in Java

The Single Responsibility Principle

单一职责原则的另一种说法是:

    将因相同原因而发生变化的事物聚集在一起。将那些因不同原因而改变的事物分开。

和这一句话:

任何一个软件模块都应该只对一类行为者负责。(《整洁架构之道》第57页)

行为

所以单一职责不是一个类只有一个方法,是一个类里所有的方法都只为一类行为服务, 当方法发生改变时,改变的原因是同一个原因。

class Request {
  async get(url) {
    return axios.get(url);
  }

  async post(url, body) {
    return axios.post(url, body);
  }

  async delte(url) {
    return axios.get(url);
  }

  async put(url, params) {
    return axios.put(url, params);
  }
}

这个Request就完美符合单一职责的定义。4 个函数均为Http请求,发生变化时变化的原因是相同的, 并且没有不同的变化原因。

什么时候是不符合的呢?

import axios from "axios";
import path from "path";

class File {
  read(filePath: string): string {
    return fs.readFileSync(filePath);
  }

  writeFile(filePath: sting, data: string): bool {
    return fs.writeFileSync(filePath, data);
  }

  deltetFile(filePath: sting): bool {
    return fs.unlinkSync(filePath, data);
  }

  async download(url: string) {
    const result = await axios.get(url);
    if (result.code === 200) {
      const basePath = path.join(___dirname);
      const { fileBuffer, fileName } = result.data;

      const filePath = `${basePath}/${fileName}`;
      const isWrite = fs.writeFileSync(fileName, fileBuffer);
      if (isWrite) {
        return filePath;
      }
      return false;
    } else {
      console.log("请求失败!");
    }
  }
}

Flie 这个Class里有 3 个文件相关的方法,一个Http请求用于下载文件。

Http请求发生改变时,变化的原因跟File 种其他的 3 个函数变化原因不同, 也就是两个改变行为,这样会导致我们有两类关注点 文件 和 Http请求,

如果不去修改随着时间的推移,Flie 这个Class可能会有uploadFile deleteServiceFile reNameServiceFile…… 恭喜你达成成就 "堆砌屎山"

一个Class 只有一个改变行为时,我们只有一个关注点,发生错误时,更容易查找到错误,且更易于测试!

抽离

那么就可以将download方法抽离。

class FileService {
  async download(url: string) {
    const result = await axios.get(url);
    if (result.code === 200) {
      const basePath = path.join(___dirname);
      const { fileBuffer, fileName } = result.data;
      const filePath = `${basePath}/${fileName}`;
      const isWrite = fs.writeFileSync(fileName, fileBuffer);
      if (isWrite) {
        return filePath;
      }
      return false;
    } else {
      console.log("请求失败!");
    }
  }
}

后期增加的与服务端文件相关的方法均可以放在此处。

这里的axios 请求是不是也可以抽出去呢? 当然可以……

职责划分

单一职责最难的在于如何划分职责。

在这个File中我将read writeFile deleteFile 归为一类行为, 当你们看到这个代码的时候肯定会问,能不能再将这3个方法再次拆分呢?

没必要,这样做就属于过度优化了。这3个函数本身就比较简单,其目的本身也只是:操作文件

我们的目的也是提升内聚, 让类专注于自身的行为

class File {
  read(filePath: string): string {
    return fs.readFileSync(filePath);
  }
  writeFile(filePath: sting, data: string): bool {
    return fs.writeFileSync(filePath, data);
  }
  deltetFile(filePath: sting): bool {
    return fs.unlinkSync(filePath, data);
  }
 }

最后借用一句: If you think about this you’ll realize that this is just another way to define cohesion and coupling. We want to increase the cohesion between things that change for the same reasons, and we want to decrease the coupling between those things that change for different reasons.The Single Responsibility Principle

机翻:如果你仔细想想,你会发现这只是定义内聚和耦合的另一种方式。我们想要增加因相同原因而改变的事物之间的内聚力,我们想要减少因不同原因而改变的事物之间的耦合。

参考资料:

Single Responsibility Principle in Java

The Single Responsibility Principle

架构整洁之道