设计模式之设计原则(上)
我们这次分享讲解具体设计模式之前给大家分享一下设计模式原则,很多人有可能会疑惑为什么要用设计模式啊?设计模式有什么好处,设计模式就是很多前辈们在他们设计软件时在大量的编码中总结出来的一些思路,因为大家都知道有一个好的框架思路是一个软件成功与否的关键。个人理解设计模式是面向对象的更优的代码风格及封装。
我们平时编码时会遇到如下一些问题:
1)系统僵化,不可修改或不可扩展(修改难或者扩展难)。
2)过分复杂或者重复代码太多,往往看代码时不知道从哪里看起,也不知道程序会跑到哪里。
3)不可复用,公共部分剥离不出来,只能到处拷贝。
4)不够稳定:经常出错,然后修改,再出错,继续改。
5)系统不稳定,最后程序员自己都不敢相信自己写的系统。
我个人是编码中也遇到以上的这些问题,不知道大家有没有遇到过啊。
为了解决这些问题在面向对象编码过程中规定一些规则,这就是设计模式原则,对于设计模式原则也有很多说法,但是我个人学习的有六项,分享给大家:
1)开放-封闭原则(Open Closed Principle):简称OCP :该原则的核心思想就是想说明软件应该是可以扩展,而不可以修改的。
对扩展开放:有新的需求或改变时可以对软件进行扩展,以适应新的情况。
对修改封闭:类一旦设计完成就独立工作,而不再对类进行任何修改,但并不意味着不做任何修改,低层模块的变更,必然要有高层模块进行耦合,否则就是一个孤立无意义的代码片段了 。
实现方式:抽象、多太、继承、接口
给大家简单的计算器例子:
1 public static void Normal() 2 3 { 4 5 6 7 Console.WriteLine("请输入数字A"); 8 9 string numberA= Console.ReadLine(); 10 11 Console.WriteLine("请输入运算符"); 12 13 string sign=Console.ReadLine(); 14 15 Console.WriteLine("请输入数字B"); 16 17 string numberB=Console.ReadLine(); 18 19 decimal result = 0; 20 21 switch (sign) 22 23 { 24 25 case"+": 26 27 result =Convert.ToDecimal(numberA) + Convert.ToDecimal(numberB); 28 29 break; 30 31 case "-": 32 33 result = Convert.ToDecimal(numberA) - Convert.ToDecimal(numberB); 34 35 break; 36 37 case "*": 38 39 result = Convert.ToDecimal(numberA) * Convert.ToDecimal(numberB); 40 41 break; 42 43 } 44 45 Console.WriteLine("计算结果:"+result); 46 47 }
调用代码如下:
1 static void Main(string[] args) 2 3 { 4 5 Normal(); 6 7 Console.ReadLine(); 8 9 }
输出结果如下:
以上代码从功能上来说也能实现,但是我们可以发现如果想在添加一个两个数字相除的功能,必须修改Normal方法,如果过几天客户还需要添加平方根之类的需求还需要修改,无限的修改,在这里编码简单,所以出错也会很少,如果该功能要有上千行代码的话在添加功能时会带来很大的麻烦,还会破坏以前的代码,有一个人不小心把以前的代码动了一下,把别的功能给影响了,导致以前的测试通过的代码还需要重新测试。
下面在看看这个例子:
1 public abstract class Operate 2 3 { 4 5 6 7 //获取计算结果的抽象 8 9 public abstract decimal GetResult(decimal numberA, decimal numberB, string sign); 10 11 } 12 13 14 15 public class OperatePlus:Operate 16 17 { 18 19 public override decimal GetResult(decimal numberA, decimal numberB, string sign) 20 21 { 22 23 return numberA + numberB; 24 25 } 26 27 } 28 29 public class OperateMinus : Operate 30 31 { 32 33 public override decimal GetResult(decimal numberA, decimal numberB, string sign) 34 35 { 36 37 return numberA - numberB; 38 39 } 40 41 } 42 43 public class OperateMultiply : Operate 44 45 { 46 47 public override decimal GetResult(decimal numberA, decimal numberB, string sign) 48 49 { 50 51 return numberA*numberB; 52 53 } 54 55 }
以上代码类图如下:
客户端调用代码如下:
1 static void Main(string[] args) 2 3 { 4 5 Operate operate=new OperatePlus(); 6 7 decimal result=operate.GetResult(10, 20, "+"); 8 9 Console.WriteLine("计算结果:"+result); 10 11 Console.ReadLine(); 12 13 }
输出结果:
计算结果都一样,但是该方法如果遇到修改需求,添加一个数字相除时不需要修改以前的代码,只需要在扩展一个相除方法。
代码类图如下:
从类图可以看出以前的代码不需要修改,添加一个方法即可实现。
总结:从上面的两个例子的对比能明显的看出来好处,所以开放封闭原则不是说不能修改任何代码,这样的话新的需求没法弄,所以该原则其实是针对方法需要抽象变化,对于客户端来说抽象编码,这样会尽可能的减少代码的修改,但是不会失去扩展的功能,但是需要注意的是抽象也不能滥用哦,要适当的使用。
单一职责原则(Single Responsibility Principle)简称SRP:该原则的核心思想是一个类最好只做一件事情,只有一个引起变化的原因。
单一职责原则可以看作是低耦合、高内聚在面向对象原则上的引申,将职责定义为引起变化的原因,以提高内聚性来减少引起变化的原因。职责过多,可能引起它变化的原因就越多,这将导致职责依赖,相互之间就产生影响,从而极大的损伤其内聚性和耦合度。单一职责,通常意味着单一的功能,因此不要为类实现过多的功能点,以保证实体只有一个引起它变化的原因。
单一职责原则带来的好处:
1)类的复杂性降低,实现什么职责都有清晰明确的定义。
2)可读性高,大家想想50行代码可读性高呢还是1000行代码可读性高呢?
3)可维护行高,这个应该不用我说。
4)变更引起的风险降低,每一个类负责一个一件事情,变更该类对其他类的影响很少。
这里举一个简单的例子:
1 Interface Modem 2 3 { 4 5 Void dial(string pno); 6 7 Void hangup(); 8 9 Void Send(char c); 10 11 Void Recv(); 12 13 }
从上面例子中可以看出来Modem接口明显有两个职责:连接管理和数据通信,所以应该按照职责分离出来
1 Interface DataChancel 2 3 { 4 5 Void Send(char c); 6 7 Void Recv(); 8 9 } 10 11 Interface Connection 12 13 { 14 15 Void dial(string pno); 16 17 Void hangup(); 18 19 }
注意:单一职责不是从类的功能点多少来划分的,而是从类引起变化的原因来分的,一个类引起变化的原因只有一个,如果一个变化引起多个职责发生变化,那么这些职责应该被封装到一个类。