单一职责原则:一个模块的功能只能因为一个原因进行修改。
单一职责介绍
我们假设一个场景:
有一个动物类,它会呼吸空气,用一个类描述动物呼吸这个场景:
class Animal{
public function breathe(string $animal){
echo $animal . "呼吸空气";
}
}
$animal = new Animal();
$animal->breathe("牛");
$animal->breathe("羊");
$animal->breathe("猪");
当有一天我们发现并不是所有的动物都呼吸空气,例如水生的动物他们呼吸的是水,我们就需要进行如下的代码改动。
class Animal{
public function breathe(string $animal,string $type){
if ($type == "terrestrial") {
echo $animal . "呼吸空气";
} else {
echo $animal . "呼吸水";
}
}
}
$animal = new Animal();
$animal->breathe("牛","terrestrial");
$animal->breathe("羊","terrestrial");
$animal->breathe("猪","terrestrial");
$animal->breathe("鱼","aquatic");
上述代码的改动就违反了单一原则,breathe 方法会因为两个因素造成修改,分别是 terrestrial 和 aquatic 。
这种违反会带来什么问题,最直观的问题就是会更容易造成 bug, 比如我们 terrestrial 需要增加新的功能,在些新功能的时候引入了一些 bug ,这将导致 aquatic 的代码块也无法执行,具体例子如下:
class Animal{
public function breathe(string $animal,string $type){
if ($type == "terrestrial") {
t=1;// 引入了错误
echo $animal . "呼吸空气";
} else {
echo $animal . "呼吸水";
}
}
}
$animal = new Animal();
$animal->breathe("牛","terrestrial");
$animal->breathe("羊","terrestrial");
$animal->breathe("猪","terrestrial");
$animal->breathe("鱼","aquatic"); // 因为引入了错误导致程序无法执行
我们可以通过初始化 Terrestrial 和 Aquatic 两个类分别处理陆地动物和水中动物,这样即便是我们 Terrestrial 在处理新需求时引入了 bug 也不会导致 Aquatic 的异常。
class Terrestrial{
public function breathe(string $animal){
echo $animal . "呼吸空气";
}
}
class Aquatic{
public function breathe(string $animal){
echo $animal . "呼吸水";
}
}
try {
$terrestrialAnimal = new Terrestrial();
$terrestrialAnimal->breathe("牛");
$terrestrialAnimal->breathe("羊");
$terrestrialAnimal->breathe("猪");
} catch(\Throwable $e) {
// 在实际场景中 Terrestrial 和 Aquatic 通常不会再一个地方调用,这里为了演示方便通过 catch 模拟真实场景
}
$aquaticAnimal = new Aquatic();
$aquaticAnimal->breathe("鱼");
单一职责的优势
增加代码复用: 单一职责会把代码封装的粒度变的更小,这样更有利于代码的复用。
降低单元测试难度: 符合单一职责设计的代码可以更简单的进行单元测试
降低 BUG 的产生: 符合单一职责的模块,能够限制修改代码的影响范围,不会因为某处代码的修改,引起另一处产生了 BUG。
总结
单一职责是面向对象设计的基本原则之一,了解了单一职责能够让我们的代码更健壮,更容易复用和扩展。
在需要修改原有代码的场景下,我们需要更加注意此次的更改有没有破坏模块的单一职责,以免造成程序架构的破坏。