C#中设计模式的学习
来源:互联网 发布:趣学python编程 百度云 编辑:程序博客网 时间:2024/05/22 03:37
参照大神的设计模式的相关书籍,简单做一下整理,方便日后查看,感谢大神,膜拜大神
主要的设计模式为:简单工厂模式、策略模式、工厂模式、装饰模式、代理模式、原型模式、建造者模式、模板方法模式、观察者模式
面向对象的编程引入了类,类的引入是为了封装,分类的基础是抽象,具有相同属性和功能的对象的抽象集合才是类。
基础知识:1.Math.Round:四舍六入五取偶 2.Math.Ceiling:只要有小数都加1 3.Math.Floor:总是舍去小数
1、简单工厂模式
超市的收银系统(简单的)
客户端
//confirm private void button1_Click(object sender, EventArgs e) { //点击确认后进行结算 try { string type = comboBox1.SelectedItem.ToString(); CashSuper cs = CashFactory.createCashMoney(type); double totalPrice = 0.0d; totalPrice = cs.payMoney(Convert.ToDouble(textBox_price.Text) * Convert.ToDouble(textBox_quantity.Text)); listBox_list.Items.Add("单价" + textBox_price.Text + "数量" + textBox_quantity.Text + " " + comboBox1.SelectedItem + "合计" + totalPrice); } catch (Exception) { MessageBox.Show("请输入正确的数值"); } } //reset private void button2_Click(object sender, EventArgs e) { textBox_quantity.Text = ""; textBox_price.Text = ""; listBox_list.Items.Clear(); comboBox1.SelectedItem = ""; }
抽象类
namespace WindowsLearnModel2{ public abstract class CashSuper { //支付的父类,一个抽象类,方法不实现,需要子类来实现 //父类要有属性和方法 public abstract double payMoney(double money); }}
父类
namespace WindowsLearnModel2{ //工厂类 class CashFactory { //静态类调用需要类名 public static CashSuper createCashMoney(string type) { CashSuper cs = null; switch(type) { case "原价": cs =new CashNormal(); break; case "满300减100": cs =new CashReturn("300","100"); break; case "打7折": cs = new CashDiscount("0.7"); break; } return cs; } }}
子类1
namespace WindowsLearnModel2{ //一个打折的子类 class CashDiscount:CashSuper { private double moneyDiscount=1d; //折扣率属性 //构造函数,可以没有返回值,但其他方法必须有返回值 public CashDiscount(string moneyDiscount) { this.moneyDiscount=double.Parse(moneyDiscount); } public override double payMoney(double money) { return money*moneyDiscount; } }}
子类2
namespace WindowsLearnModel2{ class CashNormal:CashSuper { public override double payMoney(double money) { return money; } }}
子类3
namespace WindowsLearnModel2{ class CashReturn:CashSuper { private double moneyCondition = 0.0d; private double moneyReturn = 0.0d; public CashReturn(string moneyCondition,string moneyReturn) { this.moneyCondition = double.Parse(moneyCondition); this.moneyReturn = double.Parse(moneyReturn); //将double类型的字符串转为double类型 //类似于Convert.ToDouble() } public override double payMoney(double money) { double result = money; if(money>moneyCondition) { result = money - Math.Floor(money / moneyCondition) * moneyReturn; } return result; } }}
对于简单工厂模式,若要增加新的打折方式,需要添加新的子类,在客户端的选择框中添加新的类型
2、策略模式
算法的改变不影响用户?算法是随时可能变化的,将这些变化点封装起来,减少大的改动。
父类
namespace WindowsLearnModel2{ public abstract class CashSuper { //支付的父类,一个抽象类,方法不实现,需要子类来实现 //父类要有属性和方法 public abstract double payMoney(double money); }}
子类1
namespace WindowsLearnModel2{ class CashNormal:CashSuper { public override double payMoney(double money) { return money; } }}
子类2
namespace WindowsLearnModel2{ //一个打折的子类 class CashDiscount:CashSuper { private double moneyDiscount=1d; //折扣率属性 //构造函数,可以没有返回值,但其他方法必须有返回值 public CashDiscount(string moneyDiscount) { this.moneyDiscount=double.Parse(moneyDiscount); } public override double payMoney(double money) { return money*moneyDiscount; } }}
子类3
namespace WindowsLearnModel2{ class CashReturn:CashSuper { private double moneyCondition = 0.0d; private double moneyReturn = 0.0d; public CashReturn(string moneyCondition,string moneyReturn) { this.moneyCondition = double.Parse(moneyCondition); this.moneyReturn = double.Parse(moneyReturn); //将double类型的字符串转为double类型 //类似于Convert.ToDouble() } public override double payMoney(double money) { double result = money; if(money>moneyCondition) { result = money - Math.Floor(money / moneyCondition) * moneyReturn; } return result; } }}
对算法进行封装
namespace WindowsLearnModel2{ //将实例化具体策略的过程由客户端转移到Context类中,简单工厂的应用 //策略模式封装了变化,一大特点 class CashContext { CashSuper cs = null; //构造函数 public CashContext(string type) { switch(type) { case "原价": cs = new CashNormal(); break; case "满300减100": cs = new CashReturn("300","100"); break; case "打7折": cs = new CashDiscount("0.7"); break; default: break; } } public double GetResult(double money) { return cs.payMoney(money); } }}
客户端
namespace WindowsLearnModel2{ public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void bindingSource1_CurrentChanged(object sender, EventArgs e) { } //confirm private void button1_Click(object sender, EventArgs e) { //点击确认后进行结算,以相同的方式调用所有的算法 try { string type = comboBox1.SelectedItem.ToString(); CashContext cc = new CashContext(type); double totalPrice = 0.0d; totalPrice = cc.GetResult(Convert.ToDouble(textBox_price.Text) * Convert.ToDouble(textBox_quantity.Text)); listBox_list.Items.Add("单价" + textBox_price.Text + "数量" + textBox_quantity.Text + " " + comboBox1.SelectedItem + "合计" + totalPrice); } catch (Exception) { MessageBox.Show("请输入正确的数值"); } } //reset private void button2_Click(object sender, EventArgs e) { textBox_quantity.Text = ""; textBox_price.Text = ""; listBox_list.Items.Clear(); comboBox1.SelectedItem = ""; } }}
3、单一职责
就一个类而言,应该只有一个引起它变化的原因
游戏的逻辑和form界面应该分离,这样在不同的平台上游戏逻辑可以复用。在软件设计中,需要发现职责并把职责相互分离,应该判断是否分离类出来。在设计中需要考虑类的职责分离。
4、开放封闭原则
系统在开始设计时需求确定,也不能保证不再修改,面对需求的变化时,要做到代码相对容易修改,这时软件设计之初应该考虑的。
软件的实体(类、模块、函数等)可以扩展但是不可以修改。
5、依赖倒转原则
抽象不应依赖于细节,细节应该依赖于抽象
高层次模块不应该依赖于低层次模块,两个都应该依赖抽象
- 里氏代换原则:子类型必须能够替换掉它们的父类型。
如果程序编写中考虑如何针对抽象编程而不是针对细节编程,即程序中所有的依赖关系都是终止于抽象类或者接口,那就是面向对象设计,反之就是过程化设计。
6、装饰模式
动态的给对象添加一些额外的职责。相对子类来说,装饰模式比子类更方便增加功能。
为已有的功能动态的添加功能的一种方式,把每次要装饰的功能放在一个类中,并让这个类包装它所需要的对象,因此在需要执行特殊行为时,客户代码就可以在运行时根据需要有选择的、按顺序的使用装饰功能包装对象。
把类的核心和装饰功能区分开,并去除相关类中重复的装饰逻辑。
注意:最好保证装饰类之间彼此独立,使其以任意的顺序进行组合。
下面的装饰就可以自由组合,注意类的虚函数和抽象函数的使用
component类
namespace ComponentDecorate{ class CPersion { //必须包含参数为0个的构造函数 因为子类会默认调用父类的无参数的构造函数, //若没有,报错 public CPersion() { } private string name; public CPersion(string name) { this.name = name; } public virtual void Show() { Console.WriteLine("decorate {0}",name); } }}
decorate类
namespace ComponentDecorate{ abstract class CDecorate:CPersion { protected CPersion component; //打扮方法 public void Decorate(CPersion component) { this.component=component; } public override void Show() { if(component!=null) { component.Show(); } } }}
ConcreteDecorate类1
namespace ComponentDecorate{ class CTshirts:CDecorate { public override void Show() { Console.WriteLine("T shirt"); base.Show(); } }}
ConcreteDecorate类2
namespace ComponentDecorate{ class CDress:CDecorate { public override void Show() { Console.WriteLine("Dress"); base.Show(); } }}
客户端:
namespace ComponentDecorate{ class Program { static void Main(string[] args) { CPersion per =new CPersion("Vivo"); Console.WriteLine("This first clothes:\n"); CTshirts ct = new CTshirts(); CDress cd = new CDress(); cd.Decorate(per); ct.Decorate(cd); ct.Show(); Console.Read(); } }}
7、代理模式
为其他对象提供一种代理以控制对这个对象的访问。
简单介绍一下代理模式:代理模式定义了一个真正对象和代理对象继承的接口,在真正对象出场时代理出现。
定义一个接口,
代理和主角都会要实现,一个是真正实现,一个是间接实现
接口定义了方法,继承接口的需要实现,接口的成员(方法+自动属性)不需要在修饰符,默认public
interface IGiveGift { void GiveDolls(); void GiveFlowers(); void GiveChocolate(); }
目标:
class Girl { string name; //自动属性 public string Name { get { return name; } set { name = value; } } }
代理作用于目标:
class Proxy:IGiveGift { RealSubject realsubject; //notes public Proxy(Girl real) { realsubject=new RealSubject(real); } public void GiveDolls() { realsubject.GiveDolls(); } public void GiveFlowers() { realsubject.GiveFlowers(); } public void GiveChocolate() { realsubject.GiveChocolate(); } }
真正对象通过代理作用于目标:
class RealSubject : IGiveGift { Girl girl; public RealSubject(Girl girl) { this.girl = girl; } public void GiveDolls() { Console.WriteLine(girl.Name+",give you a beautiful dollar"); } public void GiveFlowers() { Console.WriteLine(girl.Name+",give you a colorful flowers"); } public void GiveChocolate() { Console.WriteLine(girl.Name+",give you chocolate, wish you like it"); } }
客户端代码,代理作用于目标,其实是真正对象作用于目标:
class Program { static void Main(string[] args) { Girl xiaofang = new Girl(); xiaofang.Name = "XiaoFang"; Proxy pro = new Proxy(xiaofang); pro.GiveChocolate(); pro.GiveDolls(); pro.GiveFlowers(); Console.Read(); } }
..
特别注意:上面定义的类都没有加修饰符public,若不加都不加,若加都加;
8、工厂方法模式
定义了一个用于创建对象的接口,让子类决定实例化哪个类,工厂方法使类的实例化延迟到其子类。
先使用简单工厂类实现计算器的运算,在使用工厂方法类使用计算器
简单工厂类的实现
产生工厂类
public class OperateFactory { public static Operate CreateOperate(string operate) { Operate oper= null; switch(operate) { case "+": oper = new OperateAdd(); break; case "-": oper = new OperateMinus(); break; case "*": oper = new OperateMulti(); break; case "/": oper = new OperaterDiv(); break; default: break; } return oper; } }
简单工厂类
public abstract class Operate { private double numberA; private double numberB; public double NumberA { get { return numberA; } set { numberA = value; } } public double NumberB { get { return numberB; } set { numberB= value; } } public virtual double GetResult() { double result = 0.0; return result; } //若子类没有重写父类的虚方法,父类对象必须强制转换为子类,才可以调用子类的方法; //若子类重写了父类的虚方法,则直接调用父类的方法即可。 }
加法类
public class OperateAdd : Operate { public override double GetResult() { double result = 0; result=NumberA+NumberB; return result; } }
减法类
public class OperateMinus : Operate { public override double GetResult() { double result = 0; result = NumberA -NumberB; return result; } }
乘法类
public class OperateMulti : Operate { public override double GetResult() { double result = 0; result = NumberA * NumberB; return result; } }
除法类
public class OperaterDiv : Operate { public override double GetResult() { double result = 0; if(NumberB==0) { throw new Exception("Cannot div zero"); } else { result = NumberA / NumberB; } return result; } }
客户实现
class Program { static void Main(string[] args) { Operate op; op = OperateFactory.CreateOperate("*"); op.NumberA = 10; op.NumberB = 70; Console.WriteLine("{0}",op.GetResult()); Console.Read(); } }
简单工厂类如果需要扩展,增加新的运算方法,需要在产生工厂类中增加新的运算+新的算法类
工厂方法的实现
使用工厂方法模式在增加新的运算时,不需要更改很多地方。
运算类
public abstract class Operate { private double numberA; private double numberB; public double NumberA { get { return numberA; } set { numberA = value; } } public double NumberB { get { return numberB; } set { numberB= value; } } public virtual double GetResult() { double result = 0.0; return result; } //若子类没有重写父类的虚方法,父类对象必须强制转换为子类,才可以调用子类的方法; //若子类重写了父类的虚方法,则直接调用父类的方法即可。 }
加法运算类
public class OperateAdd : Operate { public override double GetResult() { double result = 0; result=NumberA+NumberB; return result; } }
减法运算类
public class OperateMinus : Operate { public override double GetResult() { double result = 0; result = NumberA -NumberB; return result; } }
乘法运算类
public class OperateMulti : Operate { public override double GetResult() { double result = 0; result = NumberA * NumberB; return result; } }
除法运算类
public class OperaterDiv : Operate { public override double GetResult() { double result = 0; if(NumberB==0) { throw new Exception("Cannot div zero"); } else { result = NumberA / NumberB; } return result; } }
定义一个工厂接口
interface IFactory { Operate CreateOperate(); }
每个运算都定义一个工厂类
加法工厂类
public class AddFactory : IFactory { public Operate CreateOperate() { return new OperateAdd(); } }
减法工厂类
public class MinusFactory : IFactory { public Operate CreateOperate() { return new OperateMinus(); } }
乘法工厂类
public class MultiFactory : IFactory { public Operate CreateOperate() { return new OperateMulti(); } }
除法工厂类
public class DivFactory : IFactory { public Operate CreateOperate() { return new OperaterDiv(); } }
工厂模式修改的时候不需要太幅度改变;实现方法:先定义了一个运算类,在定义具体的子运算类,产生运算类;工厂模式在此基础上,定义了运算工厂接口+具体子运算工厂。
工厂模式集中封装了对象的创建,在更换对象时,不需要做大的改动即可。缺点,每增加一个运算类,就要增加一个相应的运算工厂类。
9、原型模式.:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的实例。
原型模式就是从一个对象再创建另外一个可定制的对象,而且不需知道任何创建的细节。
在初始信息不发生变化的情况下,克隆是最好的方法,即隐藏了对象创建的细节,又提高了性能。相当于不用重新初始化对象,而是动态的获得对象运行时的状态。
简历抽象类,包含一些初始化
public abstract class Resume { private string name; private string sex; private string age; private string home; private string school; public Resume(string Name) { this.name = Name; } public string Name { get{return name;} set{name = value; } } public void SetPersonInformation(string Sex,string Age) { this.sex = Sex; this.age = Age; } public void SetExperience(string Home,string School) { this.school = School; this.home = Home; } public void Display() { Console.WriteLine("Age is {0}, Sex is {1}\n",age,sex); Console.WriteLine("Home is {0},School is {1} \n",home,school); } //抽象类关键就是有这样一个Clone方法 public abstract Resume Clone(); }
克隆
public class Resume1 : Resume { public Resume1(string name) : base(name) { } public override Resume Clone() { return (Resume)this.MemberwiseClone(); } }
客户端
class Program { static void Main(string[] args) { Resume1 re = new Resume1("xiaowang"); re.SetExperience("123","456"); re.SetPersonInformation("77","88"); re.Display(); Resume1 re1 = (Resume1)re.Clone(); re1.SetPersonInformation("1","2"); re1.SetExperience("Chinese","NewYork"); re1.Display(); Console.Read(); } }
.
其中this.MemberwiseClone()这个函数,如果字段是值类型,则对该字段执行逐位复制,如果字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象及其复本引用同一对象(这种称为浅复制)。
深复制把引用对象的变量指向复制过来的新对象,而不是原有的被引用的对象。
简单思考一下深复制和浅复制
..
10、模板方法模式
用到继承的话,所有重复的代码都应该上升到父类去,而不是让每个子类都去重复。
模板方法:在完成某一细节层次一致的一个过程或一系列步骤时,其个别步骤在更详细的层次上实现可能不同时,考虑用模板方法模式来处理。
定义了一个操作中的算法骨架,将一些步骤延迟到子类中,模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
抽象模板,定义了实现模板方法,一般是个具体方法,给出了顶级逻辑的框架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类中实现。
定义考试试卷
public class TestPaper { public void QuestionOne() { Console.WriteLine("_在一定时间内、在一定条件下无故障地执行指定功能的能力或可能性。可通过可靠度、失效率,A:元件 B:产品 C:系统\n"); Console.WriteLine("第一道题目的答案为:"+AnswerOne()); } protected virtual string AnswerOne() { return ""; } public void QuestionTwo() { Console.WriteLine("internal作用域。A 在当前的项目(程序集)中访问,不能用于项目间的引用;同一个项目中作用于public相同 B 都可以 C 都不可以"); Console.WriteLine("第二道题目的答案为:" + AnswerTwo()); } protected virtual string AnswerTwo() { return ""; } }
实现父类定义的一个或多个抽象方法,每一个方法跟父类中抽象方法对应。
第一份试卷
public class TestPaperA : TestPaper { //继承父类的方法,父类的修饰符为protected,子类不可以改变其修饰符 protected override string AnswerOne() { return "A"; } protected override string AnswerTwo() { return "B"; } }
第二份试卷
public class TestPaperB : TestPaper { protected override string AnswerOne() { return "C"; } protected override string AnswerTwo() { return "C"; } }
客户端调用
static void Main(string[] args) { //将子类变量的声明改成了父类,利用了多态性,实现了代码的复用。 Console.WriteLine("第一个学生的试卷"); TestPaper one = new TestPaperA(); one.QuestionOne(); one.QuestionTwo(); Console.WriteLine("第二个学生的试卷"); TestPaper two = new TestPaperB(); two.QuestionOne(); two.QuestionTwo(); Console.Read(); }
结果
图片中纯粹是为了举例,第一题答案:ABC,第二题:A
11、迪米特法则
如果两个类不必直接通信,那么这两个类不应当发送直接的相互作用。如果一个类需要直接调用另外一个类的方法时,可以通过第三者转发这个调用。
迪米特法则的思想为强调了类之间的松耦合;类之间的耦合越弱,越有利于复用,一个处在弱耦合的类被修改,不会对有关系的类造成波及。
12、外观模式
为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
13、建造者模式
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
建造者模式用于创建一些复杂的对象,这些对象内部构建间的顺序通常是稳定的,但对象内部的构建通常面临着复杂的变化。
建造者的好处就使得建造者的代码和表示代码分离,由于建造者隐藏了该产品是如何组装的,若需要改变一个产品的内部表示时,只需要再定义一个具体的建造者就可以。
建造者模式是当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时适用的模式。
.
14、观察者模式
观察者模式:定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
类似于有一人在观察,通知其他人。使用委托更方便。
委托:委托是一种引用方法的类型,一旦为委托分配了方法,委托将与该方法具有完全相同的行为。委托方法的使用可以像其他任何方法一样,具有参数和返回值。委托可以看着是对函数的抽象,是函数的类,委托的实例将代表一个具体的函数。
委托对象所搭载的所有方法必须具有相同的原形和形式,也就是用于相同的参数列表和返回值类型。
15、抽象工厂模式
16、
17、
- C#中设计模式的学习
- C# 设计模式学习
- 学习设计模式中
- PHP中设计模式的学习笔记
- Spring中设计模式的学习
- c#23中设计模式
- 学习设计模式----有关C#问题的理解
- 学习笔记(三)-----C#的设计模式
- 基于C#的设计模式学习(简单工厂模式和策略模式)
- 设计模式的学习
- 设计模式的学习
- 设计模式的学习
- 设计模式的学习
- 设计模式的学习
- 设计模式的学习
- 设计模式的学习
- 23中设计模式学习
- 设计模式 c# 学习(-) to be continued..
- Linux下如何不停止服务,清空nohup.out文件
- 智能纠错【优化版】
- 事物和原子性概念实例
- ARP协议分析
- 【JavaScript 】正则表达式
- C#中设计模式的学习
- Google Protocol Buffer的编解码原理
- keepalived+nginx+mysql实现高可用及负载均衡
- Go并发编程之goroutine
- HTML中 <div>标签内背景图片自适应div大小
- 前端工程师自救大法:妈妈我要学 React !
- tomcat启动为什么一闪消失
- Web项目切换为Spring-boot部署
- SSM框架下各层关系说明