设计模式七大原则—开闭原则
重要性
有过一些实际开发工作的朋友一定对某个场景会深有体会,那就是客户经常会对现有的功能提出新的需求要我们改动,并且要快速完成。如果你的代码没有很好的遵循“开闭原则”,并且顶着工期的缩减,那我们对需求变化的修改,“往往就像在一个草稿纸上反复的涂抹”,随着不断的变化修改代码就会显得很乱,可能到最后你连自己的代码都看不懂了,还可能影响现有的功能(“赔了夫人又折兵”)
定义
开闭原则在定义描述上其实非常的简短,那就是:“对扩展开放,对修改关闭”,该原则是编程种最基本、最重要的设计原则。其实在经过实际的开发工作后,大家都自然而然的会体会到这个开闭原则的思想:就是我们在对现有功能进行调整修改的时候,我们的调整修改尽量做到,对现有的功能不受影响,这样才能保障我们系统的稳定性。
如果要做到这一点,我们的程序设计就必须遵循开闭原则,即软件模块在变动的情况下,是通过新增扩展单独的代码来实现变动,而不是去修改现有的代码来完成变动。开闭原则并不是特指某种技术,他是建立在面向对象编程的运用基础之上的一种思想,所以没有固定的套路,要实现该原则,那么你就要保障你的程序能够“对扩展开放,对修改关闭”即可。
打个比方
“对扩展开放,对修改关闭”,我们打个比方来理解以下这个较为生硬的概念。比方说你现在有台电脑它的磁盘空间不够,因此此时如果要增加现有的磁盘空间,目前的电脑就需要把机箱拆掉将磁盘返厂处理,如果你要求的新增空间特别大,很可能还需要换主板。那么对于这种情况,实际上它就没有做到对“修改关闭”,因为我们的变动需要改变现有的内容。
如果说电脑在设计之初就考虑到了可能发生的变化,因此设计了外部的扩展接口,那么我们就只需要买一个新的硬盘,插入到扩展接口就可以完成磁盘空间的增加。那么对于这种情况,它就是满足了开闭原则的“对扩展开放,对修改关闭”,这个道理在程序设计上同样如此。
代码示例
下面通过代码层面来加深对的理解,通过两个示例来看看“使用”开闭原则和“未使用”开闭原则的区别,以及如何在代码层面体现出开闭原则。
1.“未使用”开闭原则
1 //电脑类 2 class Computer 3 { 4 //读取设备 5 public void ReadDevice(int type) 6 { 7 if (type==1) 8 { 9 RunMouse();//运行鼠标 10 } 11 else if (type == 2) 12 { 13 RunKeyboard();//运行键盘 14 } 15 else if (type == 3) 16 { 17 RunHeadset(); //运行耳机 18 } 19 } 20 21 //运行鼠标 22 public void RunMouse() 23 { 24 Console.WriteLine("运行鼠标"); 25 } 26 27 //运行键盘 28 public void RunKeyboard() 29 { 30 Console.WriteLine("运行键盘"); 31 } 32 33 //运行耳机 34 public void RunHeadset() 35 { 36 Console.WriteLine("运行耳机"); 37 } 38 39 } 40 41 42 internal class Program 43 { 44 static void Main(string[] args) 45 { 46 Computer computer = new Computer(); 47 computer.ReadDevice(1); 48 } 49 }
代码示例简单也比较贴近实际生活场景,其中主要是通过电脑类中“读取设备”的方法来读取不同的设备(鼠标、键盘、耳机),该方法根据传入的类型参数,来判断来读取具体的设备。
对于该示例而言,它实际上就没有遵循开闭原则,那就是没有“对扩展开放,对修改关闭”。为什么这么说?因为如果现在电脑类需要增加读取的设备,对于这个示例只能修改现有电脑类的代码,也就是新增设备运行方法,并加入新的else if判断类型读取,并且并不支持单独的扩展方式对其进行修改。如果无法预料到所有新的读取设备,那么你的电脑类将“有无宁日”,代码可能会像下图:
2.“使用”开闭原则
1 public abstract class Device 2 { 3 public abstract void Run(); 4 } 5 6 public class Mouse : Device 7 { 8 public override void Run() 9 { 10 Console.WriteLine("运行鼠标"); 11 } 12 } 13 14 public class Keyboard : Device 15 { 16 public override void Run() 17 { 18 Console.WriteLine("运行键盘"); 19 } 20 } 21 public class Headset : Device 22 { 23 public override void Run() 24 { 25 Console.WriteLine("运行耳机"); 26 } 27 } 28 29 30 //电脑类 31 class Computer 32 { 33 //读取设备 34 public void ReadDevice(Device device) 35 { 36 device.Run(); 37 } 38 39 } 40 41 internal class Program 42 { 43 static void Main(string[] args) 44 { 45 Computer computer = new Computer(); 46 computer.ReadDevice(new Headset()); 47 } 48 }
该示例将经常变化的部分单独的抽离了出来,声明了一个抽象设备类,并且根据不同的设备创建继承该抽象类的实现类。那么这样一来就将设备的变化与电脑类隔离开了,后面不管新增还是删除设备,电脑类都不用进行修改,而新增设备只用创建一个继承抽象设备类的实现类即可。因此也遵循了开闭原则的“对扩展开放,对修改关闭”。