设计模式七大原则—开闭原则

重要性

有过一些实际开发工作的朋友一定对某个场景会深有体会,那就是客户经常会对现有的功能提出新的需求要我们改动,并且要快速完成。如果你的代码没有很好的遵循“开闭原则”,并且顶着工期的缩减,那我们对需求变化的修改,“往往就像在一个草稿纸上反复的涂抹”,随着不断的变化修改代码就会显得很乱,可能到最后你连自己的代码都看不懂了,还可能影响现有的功能(“赔了夫人又折兵”)

 定义

开闭原则在定义描述上其实非常的简短,那就是:“对扩展开放,对修改关闭”,该原则是编程种最基本、最重要的设计原则。其实在经过实际的开发工作后,大家都自然而然的会体会到这个开闭原则的思想:就是我们在对现有功能进行调整修改的时候,我们的调整修改尽量做到,对现有的功能不受影响,这样才能保障我们系统的稳定性。

如果要做到这一点,我们的程序设计就必须遵循开闭原则,即软件模块在变动的情况下,是通过新增扩展单独的代码来实现变动,而不是去修改现有的代码来完成变动。开闭原则并不是特指某种技术,他是建立在面向对象编程的运用基础之上的一种思想,所以没有固定的套路,要实现该原则,那么你就要保障你的程序能够“对扩展开放,对修改关闭”即可。

打个比方

“对扩展开放,对修改关闭”,我们打个比方来理解以下这个较为生硬的概念。比方说你现在有台电脑它的磁盘空间不够,因此此时如果要增加现有的磁盘空间,目前的电脑就需要把机箱拆掉将磁盘返厂处理,如果你要求的新增空间特别大,很可能还需要换主板。那么对于这种情况,实际上它就没有做到对“修改关闭”,因为我们的变动需要改变现有的内容。

如果说电脑在设计之初就考虑到了可能发生的变化,因此设计了外部的扩展接口,那么我们就只需要买一个新的硬盘,插入到扩展接口就可以完成磁盘空间的增加。那么对于这种情况,它就是满足了开闭原则的“对扩展开放,对修改关闭”,这个道理在程序设计上同样如此。

 

代码示例

下面通过代码层面来加深对的理解,通过两个示例来看看“使用”开闭原则和“未使用”开闭原则的区别,以及如何在代码层面体现出开闭原则。

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     }

该示例将经常变化的部分单独的抽离了出来,声明了一个抽象设备类,并且根据不同的设备创建继承该抽象类的实现类。那么这样一来就将设备的变化与电脑类隔离开了,后面不管新增还是删除设备,电脑类都不用进行修改,而新增设备只用创建一个继承抽象设备类的实现类即可。因此也遵循了开闭原则的“对扩展开放,对修改关闭”。

posted @ 2022-05-07 11:42  姜承轩  阅读(1864)  评论(1编辑  收藏  举报