前言:毫无疑问 ,学习一些设计模式,对我们的编程水平的提高帮助很大。写这个博客的时候自己刚开始学习设计模式,难免有错,欢迎评论指正。
我学设计模式的第一本书是“大话设计模式”。
1.为什么要学设计模式?
设计模式的存在就是为了抵御需求变更。学会了这些思想,开始一个项目的时候考虑的更多,当用户提出变更的时候项目改动更少。
2.怎么才能学会设计模式?
我不知道,不过轮子哥(vczh)文章中的一句话,我觉得对,就是:“设计模式就是因为情况复杂了所以才会出现的,所以我们只能通过复杂的程序来学习设计模式。你不管看别人的程序也好,自己写程序练习也好,那必须要复杂,复杂到你不用设计模式就做不下去,这才能起到学习设计模式的作用”。所以,我准备选择一些自己用到的设计模式,通过写代码的方式去熟悉它们。
一.简单工厂模式(Simple Factory Pattern)
场景描述:制作一个计算器。
1.实现加减乘除。
2.以后有其它算法的时候,容易维护。
工厂类
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 //Author:cuishiyu 7 //2016.08.13 8 namespace OperationByFactory.Class 9 { 10 //运算工厂类 11 class OperationFactory 12 { 13 public static Operation createOperation(string operate) 14 { 15 Operation oper = null; 16 switch (operate) 17 { 18 case "+": 19 oper = new OperationAdd(); 20 break; 21 case "-": 22 oper = new OperationSub(); 23 break; 24 case "*": 25 oper = new OperationMul(); 26 break; 27 case "/": 28 oper = new OperationDiv(); 29 break; 30 } 31 return oper; 32 } 33 } 34 }
运算类
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 //Author:cuishiyu 7 //2016.08.13 8 namespace OperationByFactory.Class 9 { 10 //运算类 11 class Operation 12 { 13 private double _numberA = 0; 14 private double _numberB = 0; 15 16 public double NumberA 17 { 18 get 19 { 20 return _numberA; 21 } 22 23 set 24 { 25 _numberA = value; 26 } 27 } 28 29 public double NumberB 30 { 31 get 32 { 33 return _numberB; 34 } 35 36 set 37 { 38 _numberB = value; 39 } 40 } 41 42 public virtual double GetResult() 43 { 44 double result = 0; 45 return result; 46 } 47 } 48 }
加减乘除类
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 //Author:cuishiyu 7 //2016.08.13 8 namespace OperationByFactory.Class 9 { 10 //加法类 11 class OperationAdd:Operation 12 { 13 public override double GetResult() 14 { 15 return NumberA + NumberB; 16 } 17 } 18 }
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 //Author:cuishiyu 7 //2016.08.13 8 namespace OperationByFactory.Class 9 { 10 //减法类 11 class OperationSub:Operation 12 { 13 public override double GetResult() 14 { 15 return NumberA - NumberB; 16 } 17 } 18 }
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 //Author:cuishiyu 7 //2016.08.13 8 namespace OperationByFactory.Class 9 { 10 //乘法类 11 class OperationMul:Operation 12 { 13 public override double GetResult() 14 { 15 return NumberA * NumberB; 16 } 17 } 18 }
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 //Author:cuishiyu 7 //2016.08.13 8 namespace OperationByFactory.Class 9 { 10 //除法类 11 class OperationDiv:Operation 12 { 13 public override double GetResult() 14 { 15 if(NumberB== 0) 16 throw new Exception("除数不能为0"); 17 return NumberA / NumberB; 18 } 19 } 20 }
总结:简单工厂模式很简单,重要的是。这个设计模式是怎么一步步形成的、和这样做的好处有哪些。
1.可移植性号,无论是控制台程序,Windows程序,Web程序,都可以用这段代码。
2.扩展性好,更安全,以后增加平方,立方,开根号等运算的时候,增加一个相应的类,然后再Switch里增加分支就好了。同时也不用担心程序员修改原先写好的加减乘除类,使得原先的代码不会被有意或者无意的修改,所以更安全。
3.(1)编程尽可能避免重复的代码。(2)对业务进行封装,尽可能的让业务逻辑和页面逻辑分开,让它们的耦合度下降,这样更容易维护和扩展。
4.大家可以熟悉一下UML类图,画出来之后理解更直观。
二.策略模式(strategy Pattern)
场景描述:商场的收银软件,收银员根据客户所购买的商品单价和数量进行收费。
1.要考虑到打折的情况(比如过节打8折)。
2.要考虑满A返B的情况(比如满300返100)。
3.考虑以后发生其它情况是,尽量做到代码容易更改,安全的更改。
注:部分代码是伪代码
抽象策略类
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace PromotionByStrategy 8 { 9 //抽象策略类 10 abstract class CashSuper 11 { 12 public abstract double acceptCatch(double money); 13 } 14 }
返利收费子类
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace PromotionByStrategy 8 { 9 //返利收费子类 10 class CashReturn : CashSuper 11 { 12 private double moneyCondition = 0.0d; 13 private double moneyReturn = 0.0d; 14 public CashReturn(string moneyCondition, string moneyReturn) 15 { 16 this.moneyCondition = double.Parse(moneyCondition); 17 this.moneyReturn = double.Parse(moneyReturn); 18 } 19 public override double acceptCatch(double money) 20 { 21 double result = money; 22 if (money >= moneyCondition) 23 { 24 result = money - Math.Floor(money / moneyCondition) * moneyReturn; 25 } 26 return result; 27 } 28 } 29 }
打折收费子类
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace PromotionByStrategy 8 { 9 //打折收费子类 10 class CashRebate : CashSuper 11 { 12 private double moneyRebate = 1d; 13 14 //构造函数传入打折信息 15 public CashRebate(string moneyRebate) 16 { 17 this.moneyRebate = double.Parse(moneyRebate); 18 } 19 20 public override double acceptCatch(double money) 21 { 22 return money * moneyRebate; 23 } 24 } 25 }
正常收费子类
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace PromotionByStrategy 8 { 9 //正常收费子类 10 class CashNormal : CashSuper 11 { 12 public override double acceptCatch(double money) 13 { 14 return money; 15 } 16 } 17 }
策略和简单工厂的结合
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace PromotionByStrategy 8 { 9 //策略和简单工厂的结合 10 class CashContext 11 { 12 //收钱的父类 13 CashSuper cs = null; 14 15 /// <summary> 16 /// 构造函数初始化收费类型对象 17 /// </summary> 18 /// <param name="type"></param> 19 public CashContext(string type)//传入一个收费的类型 20 { 21 switch (type)//根据不同的类型实例化不同的收款算法对象 22 { 23 case"正常收费": 24 cs = new CashNormal(); 25 break; 26 case "满A减B": 27 cs = new CashReturn("A", "B"); 28 break; 29 case "打X折": 30 cs = new CashRebate("0.X"); 31 break; 32 } 33 } 34 35 public double GetResult(double money) 36 { 37 return cs.acceptCatch(money); 38 } 39 } 40 }
客户端的主要程序
1 double tatal = 0.0d; 2 //客户端的主要程序 3 //传入算法,和价格*数量,得到应收的钱 4 public void btnOK_Click() 5 { 6 CashContext csuper = new CashContext("传入收款的算法"); 7 double totalPrices = 0.0d; 8 totalPrices = csuper.GetResult(Convert.ToDouble("价格*数量")); 9 }
总结:在分析一个项目中,遇到不同的时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性。这个代码还可以优化,后面会用反射进行优化
三.单一职责原则(SRP)
注:三、四、五主要分享设计模式中用到的一些原则
就一个类而言,因该仅有一个引起它变化的原因。一个类只行使一个功能,后面维护的时候会方便许多。
四.开放-封闭原则(The Open-Closeed Principle)
对软件实体来说(类、模块。函数)都要求满足:
两大特征:对扩张是开放的(Open for extension),对更改是封闭的(Closed for modification)。
在具体的实现过程中,可按照下面做:
1.我们在最初编码的时候,假设变化不会发生。当发生变化的时候,我们就创建抽象来隔离以后发生的同类变化。
2.当面对需求的时候,对程序的改动,是通过增加代码而不是修改现有的代码实现。
3.拒绝不成熟的抽象,和抽象本身同样重要。
五.依赖倒转原则
1.依赖倒转原则:抽象不应该依赖细节,细节应该依赖抽象。
简单解释就是,针对接口编程而不是针对实现编程。
A.高模块不应该依赖低层模块。两个都应该依赖抽象。
B.抽象不应该依赖细节,细节应该依赖抽象。
2.里氏代换原则:子类型必须能够替换掉他们的父类型。
简单解释就是,一个软件实体如果使用的是一个父类的话,那么一定使用于其子类,而且它察觉不出父类对象和子类对象的区别。
也就是说,在软件里面,把父类都替换成它的子类,程序行为没有变化。
六.装饰模式
场景描述:写一个给人搭配不同服饰的系统,类似QQ秀,或者游戏皮肤之类的。
Person类
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace DressByDecorator 8 { 9 //Person类 10 class Person 11 { 12 public Person() 13 { } 14 15 private string name; 16 17 public Person(string name) 18 { 19 this.name = name; 20 } 21 22 public virtual void Show() 23 { 24 Console.WriteLine("装扮的{0}",name); 25 } 26 } 27 }
服饰类,继承Person类
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace DressByDecorator 8 { 9 //服饰类 10 class Finery : Person 11 { 12 protected Person componnet; 13 14 //打扮 15 public void Decorate(Person component) 16 { 17 this.componnet = component; 18 } 19 20 public override void Show() 21 { 22 if (componnet != null) 23 { 24 componnet.Show(); 25 } 26 } 27 } 28 }
具体的服饰类,下面举两个例子。
鞋子类,继承服饰类。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace DressByDecorator 8 { 9 //鞋子类 10 class Shose:Finery 11 { 12 public override void Show() 13 { 14 Console.WriteLine("鞋子"); 15 base.Show(); 16 } 17 } 18 }
T恤类,继承服饰类。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace DressByDecorator 8 { 9 //T恤类 10 class TShirts:Finery 11 { 12 public override void Show() 13 { 14 Console.WriteLine("T恤"); 15 base.Show(); 16 } 17 } 18 }
下面是main函数里的实现
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace DressByDecorator 8 { 9 //Person类 10 class Person 11 { 12 public Person() 13 { } 14 15 private string name; 16 17 public Person(string name) 18 { 19 this.name = name; 20 } 21 22 public virtual void Show() 23 { 24 Console.WriteLine("装扮的{0}",name); 25 } 26 } 27 }
装饰模式(Decorator),动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更加灵活。
总结:这个例子中,每一个类都形式自己的功能。他们公有的功能都写在了父类。可以实现动态的添加更多的衣服。