版本 |
作者 |
参与者 |
完成日期 |
备注 |
YanlzFramework_GoF_V01_1.0 |
严立钻 |
|
2020.02.10 |
|
|
|
|
|
|
++++“大话设计模式(C#实现)”:是对“软件架构设计”的综合探索;【程序=数据结构+算法】:数据结构是程序设计的重要技术;数据结构在某种程度上和设计模式类似,都是前辈的武功套路,不同的是,设计模式是近几十年的卓越程序员的智慧结晶,而数据结构是几百上千年的无数科学家、数学家的智慧沉淀,更具有深厚的背景;数据结构是相互之间存在一种或多种特定关系的数据元素的集合;数据结构是一门研究非数值计算的程序设计问题中的操作对象,以及它们之间的关系和操作等相关问题的学科;
++++“大话设计模式(C#实现)”:定位在一个科普类知识,了解“数据结构和设计模式”以及相关算法研究,以及软件架构设计和框架编程!
++++【大话数据结构(C实现)】:https://blog.csdn.net/VRunSoftYanlz/article/details/104242455
++++【大话数据结构(C#实现)】:https://blog.csdn.net/VRunSoftYanlz/article/details/104243017
++++【大话设计模式(C#实现)】:https://blog.csdn.net/VRunSoftYanlz/article/details/104256981
++++【设计模式简单整理】:https://blog.csdn.net/VRunSoftYanlz/article/details/79839641
++++【设计模式(精华篇)】:https://blog.csdn.net/VRunSoftYanlz/article/details/81322678
++++【大话代码架构(.Net&&C#)】:https://blog.csdn.net/VRunSoftYanlz/article/details/104271806
#第一篇:6大设计原则篇 |
++++如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。(这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意想不到的破坏。)对于单一职责原则,建议是接口一定要做到单一职责,类的设计尽量做到只有一个原因引起变化。
++单一职责原则的好处:++++1、类的复杂性降低:实现什么职责都有清晰明确的定义;++++2、可读性提高:复杂性降低,那当然可读性提高了;++++3、可维护性提高:可读性提高,那当然更容易维护了;++++4、变更引起的风险降低:变更是必不可少的,如果接口的单一职责做得好,一个接口修改只对相应的实现类有影响,对其他的接口无影响,这对系统的扩展性、维护性都有非常大的帮助; |
++++一个软件实体如果使用的是一个父类的话,那么一定适用于其子类,而且它察觉不出父类对象和子类对象的区别。(也就是说:在软件里面,把父类替换成它的子类,程序的行为没有变化。)
++++只有当子类可以替换掉父类,软件单位的功能不受到影响时,父类才能真正被复用,而子类也能够在父类的基础上增加新的行为。
++++采用里氏替换原则的目的就是:增强程序的健壮性,版本升级时可以保持非常好的兼容性。(即使增加子类,原有的子类还可以继续运行。)
++++在项目中,大家只要记住是:“面向接口编程”就基本上抓住了依赖倒置原则的核心。(依赖倒置原则是6大设计原则中最难实现的原则,它是实现开闭原则的重要途径,依赖倒置原则没有实现,就别想实现对扩展开放,对修改关闭。)
++依赖倒置原则包含三层含义:++++A、高层模块不应该依赖低层模块,两者都应该依赖其抽象;++++B、抽象不应该依赖细节;++++C、细节应该依赖抽象;++++补充1:抽象就是指接口或抽象类,两者都是不能直接被实例化的;细节就是实现类,实现接口或继承抽象类而产生的类就是细节,其特点就是可以直接被实例化,也就是可以加上一个关键字new产生一个对象。
|
++依赖倒置原则的表现:++++A、模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的;++++B、接口或抽象类不依赖于实现类;++++C、实现类依赖接口或抽象类;++++补充1:面向接口编程是OOD(Object-Oriented Design,面向对象设计)的精髓之一。
|
++依赖的三种写法:++++第一种:构造函数传递依赖对象:--在类中通过构造函数声明依赖对象,按照依赖注入的说法,这种方式叫做构造函数注入。 --public Driver(ICar _car){ this.car = _car; }++++第二种:Setter方式传递依赖对象:--在抽象中设置Setter方法声明依赖关系,依照依赖注入的说法,这是Setter依赖注入。 --public void SetCar(ICar car){ this.car = car; }++++第三种:接口声明依赖对象:--在接口的方法中声明依赖对象,采用接口声明依赖的方式,该方法也叫做接口注入。 --public void drive(ICar car); |
++++建立单一接口,不要建立臃肿庞大的接口。(接口尽量细化,同时接口中的方法尽量少)
++++接口隔离原则和单一职责原则的审视角度是不同的:单一职责要求的是类和接口职责单一,注重的是职责,这是业务逻辑上的划分;而接口隔离原则要求接口的方法尽量少。
++保证接口的纯洁性:++++A、接口要尽量小。(这是接口隔离原则的核心定义,不出现臃肿的接口(Fat Interface),但是“小”是由限度的,首先就是不能违反单一职责原则。)++++B、接口要高内聚。(高内聚就是提高接口、类、模块的处理能力,减少对外的交互。)(在接口中尽量少公布public方法,接口是对外的承诺,承诺越少对系统的开发越有利,变更的风险也就越少,同时也有利于降低成本。) |
++++迪米特法则其根本思想是:强调了类之间的松耦合。(在类的结构设计上,每一个类都应当尽量降低成员的访问权限。)
++迪米特法则对类的低耦合包含以下4层含义:++++A、只和朋友交流。(类与类之间的关系是建立在类间的,而不是方法间,因此一个方法尽量不引入一个类中不存在的对象。)(两个对象之间的耦合方式:组合、聚合、依赖等)++++B、朋友间也是有距离的。(一个类公开的public属性或方法越多,修改时涉及的面也就越大,变更引起的风险扩散也就越大;)(尽量不要对外公布太多的public方法和非静态的public变量,尽量内敛,多使用private,protected等访问权限。)++++C、是自己的就是自己的。(如果一个方法放在本类中,即不增加类间关系,也对本类不产生负面影响,就放置在本类中。)++++D、谨慎使用Serializable。(从private变更为public,访问权限扩大了,如果服务器上没有做出相应的变更,就会报序列化失败。) |
++++对于扩展是开放的(Open for extension),对于更改是封闭的(Closed for modification)。
++++开放-封闭原则是面向对象设计的核心所在。
++如何使用开闭原则:++++A、抽象约束。(抽象是对一组事物的通用描述,没有具体的实现,也就表示它可以有非常多的可能性,可以跟随需求的变化而变化。因此,通过接口或抽象类可以约束一组可能变化的行为,并且能够实现对扩展开放)--第一:通过接口或抽象类约束扩展,对扩展进行边界限定,不允许出现在接口或抽象类中不存在的public方法; --第二:参数类型、引用对象尽量使用接口或者抽象类,而不是实现类; --第三:抽象层尽量保持稳定,一旦确定即不允许修改; ++++B、元数据(metadata)控制模块行为。(尽量使用元数据来控制程序的行为,减少重复开发。)(元数据:用来描述环境和数据的数据,就是配置参数,参数可以从文件中获得,也可以从数据库中获得。)++++C、封装变化。--第一:将相同的变化封装到一个接口或抽象类中。 --第二:将不同的变化封装到不同的接口或抽象类中,不应该有两个不同的变化出现在同一个接口或抽象类中。 |
#第二篇:23设计模式篇 |
++设计模式的四个境界: ++++境界1(懵懂):没学前是一点不懂,根本想不到用设计模式,设计的代码很糟糕; ++++境界2(认知):学了几个模式后,很开心,于是到处想着要用自己学过的模式,于是时常造成误用模式而不自知; ++++境界3(了解):学完全部模式时,感觉诸多模式极其相似,无法分清模式之间的差异,有困惑,但深知误用之害,应用之时有所犹豫; ++++境界4(领悟):灵活应用模式,甚至不应用具体的某种模式也能设计出非常优秀的代码,以达到无剑胜有剑的境界; |
++2B.01、单例模式 |
++++通常我们可以让一个全局变量使得一个对象被访问,但是不能防止实例化多个对象。(一个最好的方法就是:让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。)
//立钻哥哥:Singleton类class Singleton{ private static Singleton instance; private Singleton(){} //构造方法让其private,防止外界利用new实例化
//此方法是获得本类实例的唯一全局访问点 public static Singleton GetInstance(){ if(instance == null){ instance = new Singleton(); //不存在,new实例化一个 }
return instance; } //立钻哥哥:public static Singleton GetInstance(){} } //立钻哥哥:class Singleton{} |
++2B.02、工厂方法模式 |
//立钻哥哥:计算器的工厂方法模式实现
//A、先构建一个工厂接口:interface IFactory{ Operation CreateOperation(); } //立钻哥哥:interface IFactory{}
//B、然后加减乘除各建一个具体工厂去实现这个接口:
//B.1、加法类工厂 class AddFactory : IFactory{ public Operation CreateOperation(){ return new OperationAdd(); } } //立钻哥哥:class AddFactory:IFactory{}
//B.2、减法类工厂 class SubFactory : IFactory{ public Operation CreateOperation(){ return new OperationSub(); } } //立钻哥哥:class SubFactory:IFactory{}
//B.3、乘法类工厂 class MulFactory : IFactory{ public Operation CreateOperation(){ return new OperationMul(); } } //立钻哥哥:class MulFactory:IFactory{}
//B.4、除法类工厂 class DivFactory : IFactory{ public Operation CreateOperation(){ return new OperationDiv(); } } //立钻哥哥:class DivFactory:IFactory{}
//C、创建一个Operation运算类public class Operation{ private double _numberA = 0; private double _numberB = 0;
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; return result; } } //立钻哥哥:public class Operation{}
//C、加减乘除类(继承public class Operation{})
//C.1、加法类,继承运算类 class OperationAdd : Operation{ public override double GetResult(){ double result = 0; resutl = NumberA + NumberB; return result; } } //立钻哥哥:class OperationAdd:Operation{}
//C.2、减法类,继承运算类 class OperationSub : Operation{ public override double GetResult(){ double result = 0; result = NumberA - NumberB; return result; } } //立钻哥哥:class OperationSub:Operation{}
//C.3、乘法类,继承运算类 class OperationMul : Operation{ public override double GetResult(){ double result = 0; result = NumberA * NumberB; return result; } } //立钻哥哥:class OperationMul:Operation{}
//C.4、除法类,继承运算类 class OperationDiv : Operation{ public override double GetResult(){ double result = 0; if(NumberB == 0){ throw new Exception(“立钻哥哥:除数不能为0。”); }
result = NumberA / NumberB; return result; } } //立钻哥哥:class OperationDiv:Operation{}
//D、客户端的代码IFactory operFactory = new AddFactory(); Operation oper = operFactory.CreateOperation(); oper.NumberA = 11; oper.NumberB = 100; double result = oper.GetResult();
|
++2B.03、抽象工厂模式 |
++++抽象工厂模式是工厂方法模式的升级版本,在有多个业务品种、业务分类时,通过抽象工厂模式产生需要的对象时一种非常好的解决方式。
++++AbstractProductA和AbstractProductB是两个抽象产品。
++++ProductA1、ProductA2和ProductB1、ProductB2就是两个抽象产品的具体分类的实现。
++++IFactory是一个抽象工厂接口,它里面应该包含所有的产品创建的抽象方法。
++++ConcreteFactory1和ConcreteFactory2就是具体的工厂。
++2B.04、建造者模式 |
++++建造者模式(Builder Pattern)也叫做:生成器模式。
++++建造者模式的好处:使得建造代码与表示代码分离,由于建造者隐藏了该产品是如何组装的,所以若需要改变一个产品的内部表示,只需要再定义一个具体的建造者就可以了。
++++【Builder】:是为创建一个Product对象的各个部件指定的抽象接口;
++++【ConcreteBuilder】:是具体建造者,实现Builder接口,构造和装配各个部件。
++++【Product】:就是产品角色。
//立钻哥哥:建造者模型基本代码
//A、Product类:产品类,由多个部件组成class Product{ List<string> parts = new List<string>();
//添加产品部件 public void Add(string part){ parts.Add(part); }
public void Show(){ Console.WriteLine(“立钻哥哥:产品 创建 ----”); foreach(string part in parts){ Console.WriteLine(part); } } } //立钻哥哥:class Product{}
//B、Builder类:抽象建造者类,确定产品由两个部件PartA和PartB组成,并声明一个得到产品建造后结果的方法GetResult:abstract class Builder{ public abstract void BuildPartA(); public abstract void BuildPartB(); public abstract Product GetResult(); } //立钻哥哥:abstract class Builder{}
//B.1、ConcreteBuilder1类:具体建造者类 class ConcreteBuilder1 : Builder{ private Product product = new Product();
public override void BuilderPartA(){ product.Add(“立钻哥哥:部件A”); }
public override void BuilderPartB(){ product.Add(“立钻哥哥:部件B”); }
public override Product GetResult(){ return product; } } //立钻哥哥:class ConcreteBuilder1:Builder{}
//B.2、ConcreteBuilder2类:具体建造者类 class ConcreteBuilder2 : Builder{ private Product product = new Product();
public override void BuildPartA(){ product.Add(“立钻哥哥:部件X”); }
public override void BuildPartB(){ product.Add(“立钻哥哥:部件Y”); }
public override Product GetResult(){ return product; } } //立钻哥哥:class ConcreteBuilder2:Builder{}
//C、Director类:指挥者类class Director{ public void Construct(Builder builder){ builder.BuildPartA(); builder.BuildPartB(); } } //立钻哥哥:class Director{}
//D、客户端代码,客户不需知道具体的建造过程static void Main(string[] args){ Director director = new Director(); Builder b1 = new ConcreteBuilder1(); Builder b2 = new ConcreteBuilder2();
//指挥者用ConcreteBuilder1的方法来建造产品 director.Construct(b1); Product p1 = b1.GetResult(); p1.Show();
//指挥者用ConcreteBuilder2的安防来建造产品 director.Construct(b2); Product p2 = b2.GetResult(); p2.Show();
Debug.Log(“立钻哥哥:建造者模式演示完毕~~”); } |
++2B.05、原型模式 |
++++原型模式的核心是一个clone方法,通过该方法进行对象的拷贝。
++++原型模式就是由一个正本可以创建多个副本的概念:一个对象的产生可以不由零起步,直接从一个已经具备一定雏形的对象克隆,然后再修改为生产需要的对象。
++++Prototype:声明一个克隆自身的接口;
++++ConcretePrototype:实现一个克隆自身的操作;
++++Client:让一个原型克隆自身从而创建一个新的对象;
//立钻哥哥:Prototype原型模式
//A、原型类abstract class Prototype{ private string id; public string Id{ get{ return id; } }
public Prototype(string id){ this.id = id; }
//立钻哥哥:抽象类关键就是有这样一个Clone方法 public abstract Prototype Clone();
} //立钻哥哥:abstract class Prototype{}
//B、具体原型类class ConcretePrototype1 : Prototype{ public ConcretePrototype1(string id) : base(id){ }
public override Prototype Clone(){ return (Prototype)this.MemeberwiseClone(); }
} //立钻哥哥:class ConcretePrototype1:Prototype{}
//C、客户端代码static void Main(string[] args){ ConcretePrototype1 p1 = new ConcretePrototype1(“立钻哥哥”);
//克隆类ConcretePrototype1的对象p1就能得到新的实例c1 ConcretePrototype1 c1 = (ConcretePrototype1)p1.Clone(); }
//补充说明:对于.NET而言,原型抽象类Prototype是用不着的,因为克隆实在是太常用了,所以.NET在System命名空间中提供了ICloneable接口,其中就是唯一的一个方法Clone(),这样只需要实现这个接口就可以完成原型模式了。 |
++2B.06、适配器模式 |
++++适配器模式是一个补偿模式,或者说是一个“补救”模式,通常用来解决接口不相容的问题,在百分之百的完美设计中是不可能使用到的。
++适配器模式的优点++++优点1:适配器模式可以让两个没有任何关系的类在一起运行,只要适配器这个角色能够搞定他们就成。++++优点2:增加了类的透明性。(我们访问的Target目标角色,但是具体的实现都委托给了源角色,而这些对高层次模块是透明的,也是它不需要关心的。)++++优点3:提高了类的复用度。(源角色在原有的系统中还是可以正常使用,而在目标角色中也可以充当新的演员。)++++优点4:灵活性非常好。(如果不想要适配器了,删除掉就可以了,其他的代码都不用修改,基本上就类似一个灵活的构件,想用就用,不想要就卸载。) |
++++【Target】:目标角色。(该角色定义把其他类转换为何种接口,也就是我们的期望接口)
++++【Client】:与符合Target接口的对象协同;
++++【Adaptee】:源角色。(定义一个已经存在的接口,这个接口需要适配)
++++【Adapter】:适配器角色。(对Adaptee的接口与Target接口进行适配)
//立钻哥哥:Adapter适配器模式代码实现
//A、Target类(这是客户所期望的接口。目标可以是具体的或抽象的类,也可以是接口)class Target{ public virtual void Request(){ Debug.Log(“立钻哥哥:----普通请求----”); } } //立钻哥哥:class Target{}
//B、Adaptee类(需要适配的类)class Adaptee{ public void SpecificRequest(){ Debug.Log(“立钻哥哥:----特殊请求----”); } } //立钻哥哥:class Adaptee{}
//C、Adapter类(通过在内部包装一个Adaptee对象,把源接口转换成目标接口)class Adapter : Target{ //建立一个私有的Adaptee对象 private Adaptee adaptee = new Adaptee();
public override void Request(){ //这样就可把表面上调用Request()方法变成实际调用SpecificRequest() adaptee.SpecificRequest(); } } //立钻哥哥:class Adapter:Target{}
//D、客户端代码static void Main(string[] args){ Target target = new Adapter(); target.Request(); //对客户端来说,调用的就是Target的Request()
Debug.Log(“立钻哥哥:----适配器模式演示完毕~~”); } |
++2B.07、装饰模式 |
++++装饰模式是对继承的有力补充。(继承是静态地给类增加功能,而装饰模式则是动态地增加功能。)
++++【Component】:是定义一个对象接口,可以给这些对象动态地添加职责。(在装饰模式中,必然有一个最基本、最核心、最原始的接口或抽象类充当Component抽象构件。)
++++【ConcreteComponent】:是定义了一个具体的对象,也可以给这个对象添加一些职责。(具体构件,是最核心、最原始、最基本的接口或抽象类的实现,我们要装饰的就是它。)
++++【Decorator】:装饰抽象类,继承了Component,从外类来扩展Component类的功能,但对于Component来说,是无需知道Decorator的存在的。(装饰角色,一般是一个抽象类,实现接口或者抽象方法,它里面可不一定有抽象的方法,在它的属性里必然有一个private变量指向Component抽象构件。)(维持一个指向Component对象的指针,并定义一个与Component接口一致的接口。)
++++【ConcreteDecorator】:是具体的装饰对象,起到给Component添加职责的功能。(向组件添加职责。)
//立钻哥哥:Decorator装饰模式的代码实现
//A、Component类abstract class Component{ public abstract void Operation(); } //立钻哥哥:abstract class Component{}
//B、ConcreteComponent类class ConcreteComponent : Component{ public override void Operation(){ Debug.Log(“立钻哥哥:----具体对象的操作----”); } } //立钻哥哥:class ConcreteComponent:Component{}
//C、Decorator类abstract class Decorator : Component{ protected Component component;
//设置Component public void SetComponent(Component component){ this.component = component; }
//重写Operation(),实际执行的是Component的Operation() public override void Operation(){ if(component != null){ component.Operation(); } } } //立钻哥哥:abstract class Decorator:Component{}
//D.1、ConcreteDecoratorA类class ConcreteDecoratorA : Decorator{ //本类的独有功能,以区别于ConcreteDecoratorB private string addedState;
public override void Operation(){ //首先运行原Component的Operation(),再执行本类的功能,如addedState,相当于对原Component进行了装饰。 base.Operation(); addedState = “立钻哥哥”;
Debug.Log(“立钻哥哥:具体装饰对象A的操作”); }
} //立钻哥哥:class ConcreteDecoratorA:Decorator{}
//D.2、ConcreteDecoratorB类class ConcreteDecoratorB : Decorator{ public override void Operation(){ //首先运行原Component的Operation(),再执行本类的功能,如AddedBehaviour(),相当于对原Component进行了装饰 base.Operation(); AddedBehaviour();
Debug.Log(“立钻哥哥:----具体装饰对象B的操作”); }
//本类独有的方法,以区别于ConcreteDecoratorA private void AddedBehaviour();
} //立钻哥哥:class ConcreteDecoratorB:Decorator{}
//E、客户端代码static void Main(string[] args){ ConcreteComponent c = new ConcreteComponent(); ConcreteDecoratorA d1 = new ConcreteDecoratorA(); ConcreteDecoratorB d2 = new ConcreteDecoratorB();
//装饰的方法是:首先用ConcreteComponent实例化对象c,然后用ConcreteDecoratorA的实例化对象d1来包装c,再用ConcreteDecoratorB的对象d2包装d1,最终执行d2的Operation(); d1.SetComponent(c); d2.SetComponent(d1); d2.Operation();
Debug.Log(“立钻哥哥:----装饰模式演示完毕~~”); } //立钻哥哥:static void Main(string[] args){} |
++2B.08、桥接模式 |
++++桥接模式的意图是对变化的封装,尽量把可能变化的因素封装到最细、最小的逻辑单元中,避免风险扩散。(在进行系统设计时,发现类的继承有N层时,可以考虑使用桥接模式。)
++++Bridge桥接模式:在系统开始时就被使用,它使得抽象接口和实现部分可以独立进行改变;
++++【Abstraction】:抽象化角色。(它的主要职责是定义出该角色的行为,同时保存一个对实现化角色的引用,该角色一般是抽象类。)(维护一个指向Implementor类型对象的指针)
++++【Implementor】:实现化角色。(它是接口或者抽象类,定义角色必须的行为和属性。)(一般来讲,Implementor接口仅提供基本操作,而Abstraction则定义了基于这些基本操作的较高层次的操作。)
++++【RefinedAbstraction】:修正抽象化角色。(它引用实现化角色对抽象化角色进行修正。)(扩充由Abstraction定义的接口。)
++++【ConcreteImplementor】:具体实现化角色。(它实现接口或抽象类定义的方法和属性。)
//立钻哥哥:Bridge桥接模式的代码实现
//A、Implementor类abstract class Implementor{ public abstract void Operation(); } //立钻哥哥:abstract class Implementor{}
//B.1、ConcreteImplementorA派生类class ConcreteImplementorA : Implementor{ public override void Operation(){ Debug.Log(“立钻哥哥:----具体实现A的方法执行----”); } } //立钻哥哥:class ConcreteImplementorA:Implementor{}
//B.2、ConcreteImplementorB派生类class ConcreteImplementorB : Implementor{ public override void Operation(){ Debug.Log(“立钻哥哥:----具体实现B的方法执行----”); } } //立钻哥哥:class ConcreteImplementorB:Implementor{}
//C、Abstraction类class Abstraction{ protected Implementor implementor;
public void SetImplementor(Implementor implementor){ this.implementor = implementor; }
public virtual void Operation(){ implementor.Operation(); }
} //立钻哥哥:class Abstraction{}
//D、RefinedAbstraction类class RefinedAbstraction : Abstraction{ public override void Operation(){ implementor.Operation(); } } //立钻哥哥:class RefinedAbstraction:Abstraction{}
//E、客户端实现static void Main(stirng[] args){ Abstraction ab = new RefinedAbstraction();
ab.SetImplementor(new ConcreteImplementorA()); ab.Operation();
ab.SetImplementor(new ConcreteImplementorB()); ab.Operation();
Debug.Log(“立钻哥哥:----桥接模式演示完毕~~”); } |
++2B.09、组合模式 |
++++组合模式定义了包含基本对象和组合对象的类层次结构。
++++【Component】:抽象构件角色。(定义参与组合对象的共有方法和属性,可以定义一些默认的行为或属性。)(声明一个接口用于访问和管理Component的子组件)
++++【Leaf】:叶子构件。(叶子对象,其下再也没有其他的分支,也就是遍历的最小单位。)
++++【Composite】:树枝构件。(树枝对象,它的作用是组合树枝节点和叶子节点形成一个树形结构。)
++++【Client】:通过Component接口操纵组合部件的对象;
//立钻哥哥:Composite组合模式代码实现
//A、Component为组合中的对象声明接口,在适当情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理Component的子部件abstract class Component{ protected string name;
public Component(string name){ this.name = name; }
//通常都用Add和Remove方法来提供增加或移除树叶或树枝的功能 public abstract void Add(Component c); public abstract void Remove(Component c); public abstract void Display(int depth); } //立钻哥哥:abstract class Component{}
//B、Leaf组合中表示叶节点对象,叶节点没有子节点class Leaf : Component{ public Leaf(string name) : base(name){ }
//由于叶子没有再增加分枝和树叶,所以Add和Remove方法实现它没有意义,但这样做可以消除叶节点和枝节点对象在抽象层次的区别,它们具有完全一致的接口
public override void Add(Component c){ Debug.Log(“立钻哥哥:----Cannot add to a leaf.---”); }
public override void Remove(Component c){ Debug.Log(“立钻哥哥:----Cannot remove from a leaf.---”); }
//叶节点的具体方法,此处是显示其名称和级别 public override void Display(int depth){ Debug.Log(new string(‘-’, depth) + name); }
} //立钻哥哥:class Leaf:Component{}
//C、Composite定义有枝节点行为,用来存储子部件,在Component接口中实现与子部件有关的操作,比如增加Add和删除Removeclass Composite : Component{ //一个子对象集合用来存储其下属的枝节点和叶节点 private List<Component> children = new List<Component>();
public Composite(string name) : base(name){ }
public override void Add(Component c){ children.Add(c); }
public override void Remove(Component c){ children.Remove(c); }
public override void Display(int depth){ Debug.Log(new string(‘-’, depth) + name);
foreach(Component component in children){ component.Display(depth + 2); } }
} //立钻哥哥:class Composite:Component{}
//D、客户端代码,能通过Component接口组合部件的对象static void Main(string[] args){ //生成树根root,根上长出两叶LeafA和LeafB Composite root = new Composite(“root”); root.Add(new Leaf(“Leaf A”)); root.Add(new Leaf(“Leaf B”));
//根上长出分枝CompositeX,分枝上也有两叶LeafXA和LeafXB Composite comp = new Composite(“Composite X”); comp.Add(new Leaf(“Leaf XA”)); comp.Add(new Leaf(“Leaf XB”)); root.Add(comp);
//在CompositeX上再长出分枝CompositeXY,分枝上也有两叶LeafXYA和LeafXYB Composite comp2 = new Composite(“Composite XY”); comp2.Add(new Leaf(“Leaf XYA”)); comp2.Add(new Leaf(“Leaf XYB”)); comp.Add(comp2);
//根部又长出两叶LeafC和LeafD,可惜LeafD每长牢,被风吹走了 root.Add(new Leaf(“Leaf C”));
Leaf leaf = new Leaf(“Leaf D”); root.Add(leaf); root.Remove(leaf);
//显示大树的样子 root.Display(1);
Debug.Log(“立钻哥哥:----组合模式演示完毕~~”); }
//E、结果显示
|
++2B.10、享元模式 |
++++享元模式可以避免大量非常相类似的开销;在程序设计中,有时需要生成大量细粒度的类实例来表示数据;如果能发现这些实例除了几个参数外基本上都是相同的,有时就能够受大幅度地减少需要实例化的类的数量;如果能把这些参数移到类实例的外面,在方法调用时将它们传递进来,就可以通过共享大幅度地减少单个实例的数目;
++++如果一个应用程序使用了大量的对象,而大量的这些对象造成了很大的存储开销时就应该考虑使用;还有就是对象的大多数状态可以外部状态,如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象,此时可以考虑使用享元模式;
++++Flyweight模式通常和Composite模式结合起来,用共享叶节点的有向无环图实现一个逻辑上的层次结构。最好用Flyweight实现State和Strategy对象。
++享元模式的应用:++++享元模式的优点和缺点:享元模式是一个非常简单的模式,它可以大大减少应用程序创建的对象,降低程序内存的占用,增强程序的性能,但它同时也提高了系统复杂性,需要分离出外部状态和内部状态,而且外部状态具有固化特性,不应该随内部状态改变而改变,否则导致系统的逻辑混乱。++++享元模式的使用场景:--场景1:系统中存在大量的相似对象。--场景2:细粒度的对象都具备较接近的外部状态,而且内部状态与环境无关,也就是说对象没有特定身份。--场景3:需要缓冲池的场景。
|
++Flyweight模式的适用性:++++Flyweight模式的有效性很大程度上取决于如何使用它以及在何处使用它。++++适用性1:一个应用程序使用了大量的对象。++++适用性2:完全由于使用大量的对象,造成很大的存储开销。++++适用性3:对象的大多数状态都可变为外部状态。++++适用性4:如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。++++适用性5:应用程序不依赖于对象标识。(由于Flyweight对象可以被共享,对于概念上明显有别的对象,标识测试将返回真值。)
|
++++【Flyweight】:抽象享元角色。(简单地说就是一个产品的抽象类,同时定义出对象的外部状态和内部状态的接口或实现。)(描述一个接口,通过接口flyweight可以接受并作用于外部状态。)
++++【ConcreteFlyweight】:具体享元角色。(具体的一个产品类,实现抽象角色定义的业务。该角色中需要注意的是内部状态处理应该与环境无关,不应该出现一个操作改变了内部状态,同时修改了外部状态,这是绝对不允许的。)(实现Flyweight接口,并为内部状态(如果有的话)增加存储空间。ConcreteFlyweight对象必须是可共享的。它所存储的状态必须是内部的,即,它必须独立于ConcreteFlyweight对象的场景。)
++++【UnsharedConcreteFlyweight】:不可共享的享元角色。(不存在外部状态或者安全要求(如线程安全)不能够使用共享技术的对象,该对象一般不会出现在享元工厂中。)
++++【FlyweightFactory】:享元工厂。(职责非常简单,就是构造一个池容器,同时提供从池中获得对象的方法。)
++++【Client】:(维持一个对flyweight的引用。)(计算或存储一个(多个)flyweight的外部状态。)
//立钻哥哥:Flyweight享元模式的代码实现++++Flyweight类,它是所有具有享元类的超类或接口,通过这个接口,Flyweight可以接受并作用于外部状态。abstract class Flyweight{ public abstract void Operation(int extrinsicstate); }
++++ConcreteFlyweight是继承Flyweight超类或实现Flyweight接口,并为内部状态增加存储空间。class ConcreteFlyweight : Flyweight{ public override void Operation(int extrinsicstate){ Console.WriteLine(“立钻哥哥:具体Flyweight: ” + extrinsicstate); } }
++++UnsharedConcreteFlyweight是指那些不需要共享的Flyweight子类。因为Flyweight接口共享为可能,但并不强制共享。class UnsharedConcreteFlyweight : Flyweight{ public override void Operation(int extrinsicstate){ Console.WriteLine(“立钻哥哥:不共享的具体Flyweight:” + extrinsicstate); } }
++++FlyweightFactory,是一个享元工厂,用来创建并管理Flyweight对象。它主要是用来确保合理地共享Flyweight,当用户请求一个Flyweight时,FlyweightFactory对象提供一个已创建的实例或者创建一个(如果不存在的话)。class FlyweightFactory{ private Hashtable flyweights = new Hashtable();
public FlyweightFactory(){ flyweights.Add(“X”, new ConcreteFlyweight()); flyweights.Add(“Y”, new ConcreteFlyweight()); flyweights.Add(“Z”, new ConcreteFlyweight()); }
public Flyweight GetFlyweight(string key){ return ((Flyweight)flyweights[key]); } }
++++客户端代码:static void Main(string[] args){ int extrinsicstate = 22;
FlyweightFactory f = new FlyweightFactory();
Flyweight fx = f.GetFlyweight(“X”); fx.Operation(--extrinsicstate);
Flyweight fy = f.GetFlyweight(“Y”); fy.Operation(--extrinsicstate);
Flyweight fz = f.GetFlyweight(“Z”); fz.Operation(--extrinsicstate);
UnsharedConcreteFlyweight uf = new UnsharedConcreteFlyweight();
uf.Operation(--extrinsicstate);
Console.Read(); }
++++结果表示:具体Flyweight: 21 具体Flyweight: 20 具体Flyweight: 19 不共享的具体Flyweight: 18 |
++2B.11、代理模式 |
++++代理模式(Proxy Pattern)是一个使用率非常高的模式,定义:Provide a surrogate or placeholder for another object to control access to it.(为其他对象提供一种代理以控制对这个对象的访问。)
++++代理模式也叫委托模式,它是一项基本设计技巧。(许多其他的模式,如状态模式、策略模式、访问者模式本质上是在更特殊的场合采用了委托模式,而且在日常的应用中,代理模式可以提供非常好的访问控制。)代理模式应用得非常广泛,大到一个系统框架、企业平台,小到代码片段、事务处理。代理模式的意图:为其他对象提供一种代理以控制对这个对象的访问。
++代理模式的优点++++优点1:职责清晰。(真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件事务,附带的结果就是编程简洁清晰。)++++优点2:高扩展性。(具体主题角色是随时都会发生变化的,只要它实现了接口,那代理类完全就可以在不做任何修改的情况下使用。)++++优点3:智能化。
|
++代理模式的拓展:++++立钻哥哥:一般来说代理分为远程代理、虚拟代理、安全代理、智能指引等。(远程代理:也就是为一个对象在不同的地址空间提供局部代理。这样可以隐藏一个对象存在于不同地址空间的事实。)(虚拟代理:是根据需要创建开销很大的对象。通过它来存放实例化需要很长时间的真实对象。)(安全代理:用来控制真实对象访问时的权限。)(智能指引:是指当调用真实的对象时,代理处理另外一些事。)++++拓展1:普通代理。(普通代理要求客户端只能访问代理角色,而不能访问真实角色,这是比较简单的。)++++拓展2:强制代理。(强制代理就是要从真实角色查找到代理角色,不允许直接访问真实角色。)++++拓展3:代理是有个性的。(一个类可以实现多个接口,完成不同任务的整合。也就是说代理类不仅仅可以实现主题接口,也可以实现其他接口完成不同的任务,而且代理的目的是在目前对象方法的基础上作增强,这种增强的本质通常就是对目标对象的方法济宁拦截和过滤。)++++拓展4:虚拟代理。(虚拟代理(Virtual Proxy),只要把代理模式的通用代码修改成虚拟代理。在需要的时候才初始化主题对象,可以避免被代理对象较多而引起的初始化缓慢的问题。其缺点是需要在每个方法中判断主题对象是否被创建,这就是虚拟代理。)++++拓展5:动态代理。(动态代理是在实现阶段不用关心代理谁,而在运行阶段才指定代理哪一个对象。相对来说,自己写代理类的方式就是静态代理。)(要实现动态代理的首要条件是:被代理类必须实现一个接口。)
|
++代理模式的适用性++++立钻哥哥:在需要用比较通用和复杂的对象指针代替简单的指针的时候,使用Proxy模式。++++适用性1:远程代理(Remote Proxy):为一个对象在不同的地址空间提供局部代表。++++适用性2:虚代理(Virtual Proxy):根据需要创建开销很大的对象。++++适用性3:保护代理(Protection Proxy):控制对原始对象的访问。(保护代理用于对象应该有不同的访问权限的时候。)++++适用性4:智能指引(Smart Reference):取代了简单的指针,它在访问对象时执行一些附加操作。(对指向实际对象的引用计数,这样当该对象没有引用时,可以自动释放它(也称为Smart Pointers)。)(当第一次引用一个持久对象时,将它装入内存。)(在访问一个实际对象前,检查是否已经锁定了它,以确保其他对象不能改变它。)
|
++++【Subject】:抽象主题角色。(抽象主题类可以是抽象类也可以是接口,是一个最普通的业务类型定义,无特殊要求。)(定义RealSubject和Proxy的共用接口,这样就在任何使用RealSubject的地方都可以使用Proxy。)
++++【RealSubject】:具体主题角色。(也叫做被委托角色、被代理角色。是业务逻辑的具体执行者。)(定义Proxy所代表的实体。)
++++【Proxy】:代理主题角色。(也叫做委托类、代理类。它负责对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实主题角色实现,并且在真实主题角色处理完毕前后做预处理和善后处理工作。)(保存一个引用使得代理可以访问实体。若RealSubject和Subject的接口相同,Proxy会引用Subject。)(提供一个与Subject的接口相同的接口,这样代理就可以用来替代实体。)(控制对实体的存取,并可能负责创建和删除它。)
//立钻哥哥:Proxy代理模式的代码实现++++Subject类,定义了RealSubject和Proxy的共用接口,这样就在任何使用RealSubject的地方都可以使用Proxy。abstract class Subject{ public abstract void Request(); }
++++RealSubject类,定义Proxy所代表的真实实体。class RealSubject : Subject{ public override void Request(){ Console.WriteLine(“立钻哥哥Print:真实的请求”); } }
++++Proxy类,保存一个引用使得代理可以访问实体,并提供一个与Subject的接口相同的接口,这样代理就可以用来替代实体。class Proxy : Subject{ RealSubject realSubject;
public override void Request(){ if(realSubject == null){ realSubject = new RealSubject(); }
realSubject.Request(); } }
++++客户端代码:static void Main(string[] args){ Proxy proxy = new Proxy(); proxy.Request();
Console.Read(); } |
++2B.12、外观模式 |
++++外观模式(Facade Pattern)也叫做门面模式,是一种比较常用的封装模式,定义:Provide a unified interface to a set of interfaces in a subsystem. Facade defines a highter-level interface that makes the subsystem easier to use.(要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。)
++++外观模式的使用分三个阶段:首先,在设计初期阶段,应该要有意识的将不同的两个层分离,层与层之间建立外观Facade;其次,在开发阶段,子系统往往因为不断的重构演化而变得越来越复杂,增加外观Facade可以提供一个简单的接口,减少它们之间的依赖;第三,在维护一个遗留的大型系统时,可能这个系统已经非常难以维护和扩展了,为新系统开发一个外观Facade类,来提供设计粗糙或高度复杂的遗留代码的比较清晰简单的接口,让新系统与Facade对象交互,Facade与遗留代码交互所有复杂的工作。
++++外观模式的意图:为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。(将一个系统划分为若干个子系统有利于降低系统的复杂性。一个常见的设计目标是使子系统间的通信和相互依赖关系达到最小。达到该目标的途径之一就是引入一个外观(facade)对象,它为子系统中较一般的设施提供了一个单一而简单的界面。)
++外观模式的优缺点++++优点1:减少系统的相互依赖。(如果我们不使用外观模式,外界访问直接深入到子系统内部,相互之间是一种强耦合关系,这样的强依赖是系统设计所不能接受的,门面模式的出现就很好地解决了该问题,所有的依赖都是对外观对象的依赖,与子系统无关。)++++优点2:提供了灵活性。(依赖减少了,灵活性自然提高了。不管子系统内部如何变化,只要不影响到门面对象,任你自由活动。)++++优点3:提高安全性。(想让访问子系统的哪些业务就开通哪些逻辑,不在门面上开通的方法,休想访问到。)++++缺点1:外观模式最大的缺点就是不符合开闭原则。(后期维护,继承?覆写?都不顶用,唯一能做的一件事就是修改外观角色的代码,这个风险相当大。)
|
++外观模式的使用场景++++场景1:为一个复杂的模块或子系统提供一个供外界访问的接口。++++场景2:子系统相对独立:外界对子系统的访问只要黑箱操作即可。++++场景3:预防低水平人员带来的风险扩散。(“画地为牢”,只能在指定的子系统中开发,然后再提供外观接口进行访问操作。)
|
++外观模式的适用性++++适用性1:当要为一个复杂子系统提供一个简单接口时。(子系统往往因为不断演化而变得越来越复杂。大多数模式使用时都会产生更多更小的类。这使得子系统更具可重用性,也更容易对子系统进行定制,但这也给那些不需要定制子系统的用户带来一些使用上的困难。)(Facade可以提供一个简单的缺省视图,这一视图对大多数用户来说已经足够,而那些需要更多的可定制性的用户可以越过facade层。)++++适用性2:客户程序与抽象类的实现部分之间存在着很大的依赖性。(引入facade将这个子系统与客户以及其他的子系统分离,可以提高子系统的独立性和可移植性。)++++适用性3:当需要构建一个层次结构的子系统时,使用facade模式定义子系统中每层的入口点。(如果子系统之间是相互依赖的,可以让它们仅通过facade进行通讯,从而简化了它们之间的依赖关系。) |
++++【Facade】:门面角色。(客户端可以调用这个角色的方法。此角色知晓子系统的所有功能和责任。一般情况下,会将所有从客户端发来的请求委派到相应的子系统去,也就是说该角色没有实际的业务逻辑,只是一个委托类。)(知道哪些子系统类负责处理请求。)(将客户的请求代理给适当的子系统对象。)
++++【Subsystem】:子系统角色。(可以同时有一个或者多个子系统。每一个子系统都不是一个单独的类,而是一个类的集合。子系统并不知道门面的存在。对于子系统而言,门面仅仅是另外一个客户端而已。)(实现子系统的功能。)(处理由Facade对象指派的任务。)(没有facade的任何信息,即,没有指向facade的指针。)
//立钻哥哥:Facade外观模式的代码实现
++++四个子系统的类
class SubSystemOne{ public void MethodOne(){ Console.WriteLine(“立钻哥哥Print: 子系统方法一”); } }
class SubSystemTwo{ public void MethodTwo{ Console.WriteLine(“立钻哥哥Print: 子系统方法二”); } }
class SubSystemThree{ public void MethodThree(){ Console.WriteLine(“立钻哥哥Print: 子系统方法三”); } }
class SubSystemFour{ public void MethodFour(){ Console.WriteLine(“立钻哥哥Print: 子系统方法四”); } }
++++外观类class Facade{ SubSystemOne one; SubSystemTwo two; SubSystemThree three; SubSystemFour four;
public Facade(){ one = new SubSystemOne(); two = new SubSystemTwo(); three = new SubSystemThree(); four = new SubSystemFour(); }
public void MethodA(){ Console.WriteLine(“立钻哥哥Print: 方法组A()----”); one.MethodOne(); two.MethodTwo(); four.MethodFour(); }
public void MethodB(){ Console.WriteLine(“立钻哥哥Print: 方法组B()----”); two.MethodTwo(); three.MethodThree(); } }
++++客户端调用static void Main(string[] args){ Facade facade = new Facade();
facade.MethodA(); facade.MethodB();
Console.Read(); }
++++立钻哥哥:外观模式完美体现了依赖倒置原则和迪米特法则的思想,所以是非常常用的模式之一。 |
++2B.13、观察者模式 |
++++观察者模式(Observer Pattern)又叫发布-订阅(Publish/Subscribe)模式,它是一个在项目中经常使用的模式,定义:Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.(定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。)
++++观察者模式的意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。(将一个系统分割成一系列相互协作的类有一个常见的副作用:需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,因为这样降低了它们的可重用性。)
++观察者模式特点++++立钻哥哥:观察者模式的关键对象是主题Subject和观察者Observer,一个Subject可以有任意数目的依赖它的Observer,一旦Subject的状态发生了改变,所有的Observer都可以得到通知。Subject发出通知时并不需要知道谁是它的观察者,也就是说,具体观察者是谁,它根本不需要知道。而任何一个具体观察者不知道耶不需要知道其他观察者的存在。(将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便。)++++当一个对象的改变需要同时改变其他对象,而且它不知道具体有多少对象有待改变时,应该考虑使用观察者模式。(一个抽象模型有两个方法,其中一方面依赖于另一方面,这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用。) ++++观察者模式所做的工作其实就是在解除耦合。让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响另一边的变化。
|
++事件委托说明++++立钻哥哥:委托就是一种引用方法的类型。一旦为委托分配了方法,委托将与该方法具有完全相同的行为。(委托方法的使用可以像其他任何方法一样,具有参数和返回值。)(委托可以看做是对函数的抽象,是函数的“类”,委托的实例将代表一个具体的函数。)++++举例:delegate void EventHandler();可以理解为声明了一个特殊的“类”。(而public event EventHandler Update; 可以理解为声明了一个“类”的变量。=>声明了一个事件委托变量叫Update) ++++一个委托可以搭载多个方法,所有方法被依次唤起。(可以使得委托对象所搭载的方法并不需要属于同一个类。) ++++委托对象所搭载的所有方法必须具有相同的原形和形式,也就是拥有相同的参数列表和返回值类型。
|
++观察者模式的优缺点++++立钻哥哥:Observer模式允许我们独立的改变目标和观察者。(我们可以单独复用目标对象而无需同时复用其观察者,反之亦然。)(观察者模式也使我们可以在不改动目标和其他的观察者的前提下增加观察者。)++++优点A.1:目标和观察者间的抽象耦合。(一个目标所知道的仅仅是它有一系列观察者,每个都符合抽象的Observer类的简单接口。目标不知道任何一个观察者属于哪一个具体的类。这样目标和观察者之间的耦合是抽象的和最小的。)(因为目标和观察者不是紧密耦合的,它们可以属于一个系统中的不同抽象层次。一个处于较低层次的目标对象可与一个处于较高层次的观察者通信并通知它,这样就保持了系统层次的完整。如果目标和观察者混在一起,那么得到的对象要么横贯两个层次(违反了层次性),要么必须放在这两层的某一层中(这可能会损害层次抽象。)) ++++优点A.2:支持广播通信。(不像通常的请求,目标发送的通知不需指定它的接受者。通知被自动广播给所有已向该目标对象登记的有关对象。目标对象并不关心到底有多少对象对自己感兴趣;它唯一的责任就是通知它的各观察者。这给了我们在任何时刻增加和删除观察者的自由。处理还是忽略一个通知取决于观察者。) ++++缺点A.1:意外的更新。(因为一个观察者并不知道其它观察者的存在,它可能对改变目标的最终代价一无所知。在目标上一个看似无害的操作可能会引起一系列对观察者以及依赖于这些观察者的那些对象的更新。此外,如果依赖准则的定义或维护不当,常常会引起错误的更新,这种错误通常很难捕捉。)(简单的更新协议不提供具体细节说明目标中什么被改变了,这就使得问题更加严重。如果没有其他协议帮助观察者发现什么发生了改变,它们可能会被迫尽力减少改变。) ++++优点B.1:观察者和被观察者之间是抽象耦合。(如此设计,则不管是增加观察者还是被观察者都非常容易扩展。) ++++优点B.2:建立一套触发机制。(根据单一职责原则,每个类的职责是单一的,把各个单一的职责串联成真实世界的复杂的逻辑关系,这就是一个触发机制,形成了一个触发链。观察者模式可以完美地实现这里的链条形式。) ++++缺点B.1:观察者模式需要考虑开发效率和运行效率问题,一个被观察者,多个观察者,开发和调试就会比较复杂,消息的通知默认是顺序执行,一个观察者卡壳,会影响整体的执行效率。(在这种情况下,一般考虑采用异步的方式。)(多级触发时的效率更是让人担忧,大家在设计时注意考虑。)
|
++观察者模式的使用场景++++场景1:关联行为场景。(需要注意的是,关联行为是可拆分的,而不是“组合”关系。)++++场景2:事件多级触发场景。++++场景3:跨系统的消息交换场景,如消息队列的处理机制。
|
++观察者模式适用性++++适用性1:当一个抽象模型有两个方面,其中一个方面依赖于另一个方面。(将这两者封装在独立的对象中以使它们可以各自独立地改变和复用。)++++适用性2:当对一个对象的改变需要同时改变其它对象,而不知道具体由多少对象有待改变。++++适用性3:当一个对象必须通知其它对象,而它又不能假定其它对象是谁。(换言之,我们不希望这些对象时紧密耦合的。)
|
++++【Subject】:被观察者。(定义被观察者必须实现的职责,它必须能够动态地增加、取消观察者。它一般是抽象类或者是实现类,仅仅完成作为被观察者必须实现的职责:管理观察者并通知观察者。)(目标知道它的观察者。可以有任意多个观察者观察同一个目标。)(提供注册和删除观察者对象的接口。)
++++【Observer】:观察者。(观察者接收到消息后,即进行update(更新方法)操作,对接收到的信息进行处理。)(为那些在目标发生改变时需获得通知的对象定义一个更新接口。)
++++【ConcreteSubject】:具体的被观察者。(定义被观察者自己的业务逻辑,同时定义对哪些事件进行通知。)(将有关状态存入各ConcreteObserver对象。)(当它的状态发生改变时,向它的各个观察者发出通知。)
++++【ConcreteObserver】:具体的观察者。(每个观察者在接收到消息后的处理反映是不同的,各个观察者有自己的处理逻辑。)(维护一个指向ConcreteSubject对象的引用。)(存储有关状态,这些状态应与目标的状态保持一致。)(实现Observer的更新接口以使自身状态与目标的状态保持一致。)
++++注意发出改变请求的Observer对象并不立即更新,而是将其推迟到它从目标得到一个通知之后。Notify不总是由目标对象调用。它也可被一个观察者或其它对象调用。
//立钻哥哥:Observer观察者模式的代码实现
++++Subject类,可翻译为主题或抽象通知者,一般用一个抽象类或者一个接口实现。它把所有对观察者对象的引用保存在一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。abstract class Subject{ private List<Observer> observers = new List<Observer>();
//增加观察者 public void Attach(Observer observer){ observers.Add(observer); }
//移除观察者 public void Detach(Observer observer){ observers.Remove(observer); }
//通知 public void Notify(){ foreach(Observer o in observers){ o.Update(); } } }
++++Observer类,抽象观察者,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。这个接口叫做更新接口。抽象观察者一般用一个抽象类或者一个接口实现。更新接口通常包含一个Update()方法,这个方法叫做更新方法。abstract class Observer{ public abstract void Update(); }
++++ConcreteSubject类,叫做具体主题或具体通知者,将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个具体子类实现。class ConcreteSubject : Subject{ private string subjectState;
//具体被观察者状态 public string SubjectState{ get{ return subjectState; } set{ subjectState = value; } } }
++++ConcreteObserver类,具体观察者,实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。具体观察者角色可以保持一个指向具体主题对象的引用。具体观察者角色通常用一个具体子类实现。class ConcreteObserver : Observer{ private string name; private string observerState; private ConcreteSubject subject;
public ConcreteObserver(ConcreteSubject subject, string name){ this.subject = subject; this.name = name; }
public override void Update(){ observerState = subject.SubjectState; Console.WriteLine(“立钻哥哥Print: 观察者{0}的新状态是{1}”, name, observerState); }
public ConcreteSubject Subject{ get{ return subject; } set{ subject = value; } } }
++++客户端代码static void Main(string[] args){ ConcreteSubject s = new ConcreteSubject();
s.Attach(new ConcreteObserver(s, “X”)); s.Attach(new ConcreteObserver(s, “Y”)); s.Attach(new ConcreteObserver(s, “Z”));
s.SubjectState = “ABC”; s.Notify();
Console.Read(); }
++++结果显示:观察者X的新状态是ABC 观察者Y的新状态是ABC 观察者Z的新状态是ABC
|
++2B.14、模板方法模式 |
++++模板方法模式(Template Method Pattern)是如此简单,定义:Define the skeleton of algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm’s structure.(定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。)
++++模板方法是一种代码复用的基本技术。它们在类库中尤为重要,它们提取了类库中的公共行为。(模板方法导致一种反向的控制结构,这种结构有时被称为“好莱坞法则”,即,“别找我们,我们找你”。这指的是一个父类调用一个子类的操作,而不是相反。);模板方法在一些开源框架中应用非常多,它提供了一个抽象类,然后开源框架谢了一堆子类。(如果你需要扩展功能,可以继承这个抽象类,然后覆写protected方法,再然后就是调用一个类似execute方法,就完成你的扩展开发,非常容易扩展的一种方式。);注意:为了防止恶意的操作,一般模板方法都加上final关键字,不允许被覆写。抽象模板中的基本方法尽量设计为protected类型,符合迪米特法则,不需要暴露的属性或方法尽量不要设置为protected类型。(实现类若非必要,尽量不要扩大父类中的访问权限。)
++模板方法模式特点:++++特点1:模板方法模式是通过把不变行为搬移到超类,去除子类中的重复代码来体现它的优势。++++特点2:模板方法模式就是提供了一个很好的代码复用平台。++++特点3:当不变的和可变的行为在方法的子类实现中混合在一起的时候,不变的行为就会在子类中重复出现。我们通过模板方法模式把这些行为搬移到单一的地方,这样就帮助子类摆脱重复的不变行为的纠缠。
|
++模板方法模式的优缺点++++优点1:封装不变部分,扩展可变部分。(把认为是不变部分的算法封装到父类实现,而可变部分的则可以通过继承来继承扩展。)++++优点2:提取公共部分代码,便于维护。++++优点3:行为由父类控制,子类实现。(基本方法是由子类实现的,因此子类可以通过扩展的方式增加相应的功能,符合开闭原则。)++++缺点1:按照我们的设计习惯,抽象类负责声明最抽象、最一般的事物属性和方法,实现类完成具体的事物属性和方法。但是模板方法模式却颠倒了,抽象类定义了部分抽象方法,由子类实现,子类执行的结果影响了父类的结果,也就是子类对父类产生了影响,这在复杂的项目中,会带来代码阅读的难度,而且也会让新手产生不适感。++++模板方法模式的使用场景:--场景1:多个子类有公有的方法,并且逻辑基本相同时。 --场景2:重复、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现。 --场景3:重构时,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类中,然后通过钩子函数约束其行为。(有了钩子方法,模板方法模式才算完美,由子类的一个方法返回值决定公共部分的执行结果,是不是很有吸引力呀!)
|
++++【AbstractClass】:抽象类。(定义抽象的原语操作(primitive operation),具体的子类将重定义它们以实现一个算法的各步骤。)(实现一个模板方法,定义一个算法的骨架。该模板方法不仅调用原语操作,也调用定义在AbstractClass或其他对象中的操作。)
++++【ConcreteClass】:具体类。(实现原语操作以完成算法中与特定子类相关的步骤。)
++++模板方法调用下列类型的操作:
--类型1:具体的操作(ConcreteClass或对客户类的操作)。
--类型2:具体的AbstractClass的操作(即,通常对子类有用的操作)。
--类型3:原语操作(即,抽象操作)。
--类型4:Factory Method。
--类型5:钩子操作(hook operations),它提供了缺省的行为,子类可以在必要时进行扩展。(钩子操作在缺省操作通常是一个空操作。)
++++很重要的一点是模板方法应该指明哪些操作是钩子操作(可以被重定义)以及哪些是抽象操作(必须被重定义)。要有效地重用一个抽象类,子类编写者必须明确了解哪些操作是设计为有待重定义的。(子类可以通过重定义父类的操作来扩展该操作的行为,其间可显式地调用父类操作。)(我们可以将一个操作转换为一个模板方法,以使得父类可以对子类的扩展方式进行控制。也就是,在父类的模板方法中调用钩子操作。)
//立钻哥哥:TemplateMethod模板方法模式的代码实现
++++AbstractClass是抽象类,其实也就是一抽象模板,定义并实现了一个模板方法。这个模板方法一般是一个具体方法,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现。顶级逻辑也有可能调用一些具体方法。abstract class AbstractClass{ //一些抽象行为,放到子类去实现 public abstract void PrimitiveOperation1(); public abstract void PrimitiveOperation2();
//模板方法,给出了逻辑的骨架,而逻辑的组成是一些相应的抽象操作,它们都推迟到子类实现 public void TemplateMethod(){ PrimitiveOperation1(); PrimitiveOperation2();
Console.WriteLine(“”); } }
++++ConcreteClass,实现父类所定义的一个或多个抽象方法。每一个AbstractClass都可以有任意多个ConcreteClass与之对应,而每一个ConcreteClass都可以给出这些抽象方法(也就是顶级逻辑的组成步骤)的不同实现,从而使得顶级逻辑的实现各不相同。class ConcreteClassA : AbstractClass{ public override void PrimitiveOperation1(){ Console.WriteLine(“立钻哥哥Print: 具体类A方法1实现”); }
public override void PrimitiveOperation2(){ Console.WriteLine(“立钻哥哥Print: 具体类A方法2实现”); } }
class ConcreteClassB : AbstractClass{ public override void PrimitiveOperation1(){ Console.WriteLine(“立钻哥哥Print: 具体类B方法1实现”); }
public override void PrimitiveOperation2(){ Console.WriteLine(“立钻哥哥Print: 具体类B方法2实现”); } }
++++客户端调用:static void Main(string[] args){ AbstractClass c;
c = new ConcreteClassA(); c.TemplateMethod();
c = new ConcreteClassB(); c.TemplateMethod();
Console.Read(); } |
++2B.15、命令模式 |
++++命令模式是一个高内聚的模式,定义:Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or long requests, and support undoable operations.(将一个请求封装成一个对象,从而让我们使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。)
++++命令模式比较简单,但是在项目中非常频繁地使用,因为它的封装性非常好,把请求方(Invoker)和执行方(Receiver)分开了,扩展性也有很好的保障,通用代码比较简单。命令模式的意图:将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。(有时必须向某对象提交请求,但并不知道关于被请求的操作或请求的接受者的任何信息。)
++命名模式的作用++++作用1:它能较容易地设计一个命令队列;++++作用2:在需要的情况下,可以较容易地将命令记入日志;++++作用3:允许接收请求的一方决定是否要否决请求。++++作用4:可以容易地实现对请求的撤销和重做;++++作用5:由于加进新的具体命令类不影响其他的类,因此增加新的具体命令类很容易。++++作用6:命令模式把请求一个操作的对象与知道怎么执行一个操作的对象分隔开。++++提示:敏捷开发原则告诉我们,不要为代码添加基于猜测的、实际不需要的功能。如果不清楚一个系统是否需要命令模式,一般就不要着急去实现它,事实上,在需要的时候通过重构实现这个模式并不困难,只有在真正需要如撤销/恢复操作等功能时,把原来的代码重构为命令模式才有意义。
|
++命令模式的优缺点++++优点1:类间解耦。(调用者角色与接收者之间没有任何依赖关系,调用者实现功能时只须调用Command抽象类的execute方法就可以,不需要了解到底是哪个接收者执行。)++++优点2:可扩展性。(Command的子类可以非常容易地扩展,而调用者Invoker和高层次的模块Client不产生严重的代码耦合。)++++优点3:命令模式结合其他模式会更优秀。(命令模式可以结合责任链模式,实现命令族解析任务;结合模板方法模式,则可以减少Command子类的膨胀问题。)++++缺点1:如果有N个命令,问题就出来了,Command的子类就有N个,这个类膨胀得非常大,在项目中慎重考虑使用。++++场景1:只要认为是命令的地方就可以采用命令模式。(在GUI开发中,一个按钮的点击是一个命令,可以采用命令模式。)(触发-反馈机制的处理。) |
++++【Receiver】:接收者角色。(该角色就是干活的角色,命令传递到这里是应该被执行的。)(知道如何实施与执行一个请求相关的操作。任何类都可能作为一个接收者。)
++++【Command】:命令角色。(需要执行的所有命令都在这里声明。)(声明执行操作的接口。)
++++【ConcreteCommand】。(将一个接收者对象绑定于一个动作。)(调用接收者相应的操作,以实现Execute。)
++++【Client】。(创建一个具体命令对象并设定它的接收者。)
++++【Invoker】:调用者角色。(接收到命令,并执行命令。)(要求该命令执行这个请求。)
++++实践:每一个模式到实际应用的时候都有一些变形,命令模式的Receiver在实际应用中一般都会被封装掉,那是因为在项目中:约定的优先级最高,每一个命令是对一个或多个Receiver的封装,我们可以在项目中通过有意义的类名或命令处理命令角色和接受者角色耦合关系(这就是约定),减少高层模块(Client类)对低层模块(Receiver角色类)的依赖关系,提高系统整体的稳定性。(在实际的项目开发时采用封闭Receiver的方式,减少Client对Receiver的依赖。)
//立钻哥哥:Command命令模式的代码实现
++++Command类,用来声明执行操作的接口。abstract class Command{ protected Receiver receiver;
public Command(Receiver receiver){ this.receiver = receiver; }
abstract public void Execute(); }
++++ConcreteCommand类,将一个接受者对象绑定于一个动作,调用接受者相应的操作,以实现Execute。class ConcreteCommand : Command{ public ConcreteCommand(Receiver receiver) : base(receiver){ }
public override void Execute(){ receiver.Action(); } }
++++Invoker类,要求改命令执行这个请求。class Invoker{ private Command command;
public void SetCommand(Command command){ this.command = command; }
public void ExecuteCommand(){ command.Execute(); } }
++++Receiver类,知道如何实施与执行一个与请求相关的操作,任何类都可能作为一个接受者。class Receiver{ public void Action(){ Console.WriteLine(“立钻哥哥Print: 执行请求!”); } }
++++客户端代码,创建一个具体命令对象并设定它的接受者。static void Main(string[] args){ Receiver r = new Receiver(); Command c = new ConcreteCommand(r); Invoker i = new Invoker();
i.SetCommand(); i.ExecuteCommand();
Console.Read(); } |
++2B.16、状态模式 |
++++状态模式的定义:Allow an object to alter its behaviour when its internal state changes. The object will appear to change its class.(当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类。)
++++状态模式的核心是封装,状态的变化引起了行为的变更,从外部看起来就好像这个对象对应的类发生了改变一样;状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。(把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。);状态模式的好处是将与特定状态的行为局部化,并且将不同状态的行为分割开来。(将特定的状态相关的行为都放入一个对象中,由于所有与状态相关的代码都存在于某个ConcreteState中,所以通过定义新的子类可以很容易地增加新的状态和转换。)(消除庞大的条件分支语句。)(状态模式通过把各种状态转移逻辑分布到State的子类之间,来减少相互间的依赖。)(当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,就可以考虑使用状态模式了。)
++++状态模式的意图:允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。(状态模式也叫:状态对象(Objects for States))
++状态模式的优缺点++++优点1:结构清晰。(避免了过多的switch...case或者if...else语句的使用,避免了程序的复杂性,提高系统的可维护性。)++++优点2:遵循设计原则。(很好地体现了开闭原则和单一职责原则,每个状态都是一个子类,要增加状态就要增加子类,要修改状态,只修改一个子类就可以了。)++++优点3:封装性非常好。(这是状态模式的基本要求,状态变换放置到类的内部来实现,外部的调用不用知道类内部如何实现状态和行为的变换。)++++缺点1:状态模式只有一个缺点:子类会很多,类膨胀。(如果一个事物有很多个状态,完全使用状态模式就会有太多的子类,不好管理。)++++使用场景1:行为随状态而改变的场景。(这是状态模式的根本出发点,例如权限设计,人员的状态不同即使执行相同的行为结果也会不同,在这种情况下需要考虑使用状态模式。)++++使用场景2:条件、分支判断语句的替代者。(在程序中大量使用switch语句或者if判断语句会导致程序结构不清晰,逻辑混乱,使用状态模式可以很好地避免这一问题,它通过扩展子类实现了条件的判断处理。) |
++++【State】:抽象状态角色。(接口或抽象类,负责对象状态定义,并且封装环境角色以实现状态切换。)(定义一个封装以封装与Context的一个特定状态相关的行为。)
++++【ConcreteState】:具体状态角色。(每一个具体状态必须完成两个职责:本状态的行为管理以及趋向状态处理,就是本状态下要做的事情,以及本状态如何过渡到其他状态。)(每一子类实现一个与Context的一个状态相关的行为。)
++++【Context】:环境角色。(定义客户端需要的接口,并且负责具体状态的切换。)(定义客户感兴趣的接口。)(维护一个ConcreteState子类的实例,这个实例定义当前的状态。)
++++适用性1:一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为。
++++适用性2:一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。这个状态通常用一个或多个枚举常量表示。(通常,有多个操作包含这一相同条件结构。)(State模式将每一个条件分支放入一个独立的类中。这使得我们可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化。)
//立钻哥哥:State状态模式的代码实现
++++State类,抽象状态类,定义一个接口以封装与Context的一个特定状态相关的行为。abstract class State{ public abstract void Handle(Context context); }
++++ConcreteState类,具体状态,每一个子类实现一个与Context的一个状态相关的行为。class ConcreteStateA : State{ //设置ConcreteStateA的下一状态是ConcreteStateB public override void Handle(Context context){ context.State = new ConcreteStateB(); } }
class ConcreteStateB : State{ public override void Handle(Context context){ context.State = new ConcreteStateA(); } }
++++Context类,维护一个ConcreteState子类的实例,这个实例定义当前的状态。class Context{ private State state;
//定义Context的初始状态 public Context(State state){ this.state = state; }
//可读写的状态属性,用于读取当前状态和设置新状态 public State State{ get{ return state; } set{ state = value; Console.WriteLine(“立钻哥哥Print: 当前状态:” + state.GetType().Name); } }
public void Request(){ //对请求做处理,并设置下一状态 state.Handle(this); } }
++++客户端代码:static void Main(string[] args){ //设置Context的初始状态为ConcreteStateA Context c = new Context(new ConcreteStateA());
//不断的请求,同时更改状态 c.Request(); c.Request(); c.Request(); c.Request();
Console.Read(); } |
++2B.17、职责链模式 |
++++职责链模式定义:Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.(使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。)
++++职责链的好处:当客户提交一个请求时,请求是沿链传值直至有一个ConcreteHandler对象负责处理它。(接收者和发送者都没有对象的明确信息,且链中的对象自己也并不知道链的结构。结果是职责链可简化对象的相互连接,它们仅需保持一个指向其后继者的引用,而不需保持它所有的候选接受者的引用。)(随时地增加或修改处理一个请求的结构。增强了对象指派职责的灵活性。)(缺点:一个请求极有可能到了链的末端都得不到处理,或者因为么有正确配置而得不到处理。)
++++职责链模式的意图:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
++职责链模式的优缺点++++优点1:职责链模式非常显著的优点是将请求和处理分开。(请求者可以不用知道是谁处理的,处理者可以不用知道请求的全貌。)++++缺点1:性能问题,每个请求都是从链头遍历到链尾,特别是在链比较长的时候,性能是一个非常大的问题。++++缺点2:调试不方便,特别是链条比较长,环节比较多的时候,由于采用了类似递归的方式,调试的时候逻辑可能比较复杂。++++注意事项1:链中节点数量需要控制,避免出现超长链的情况,一般的做法是在Handler中设置一个最大节点数量,在setNext方法中判断是否已经是超过其阀值,超过则不允许该链建立,避免无意识地破坏系统性能。 |
++++【Handler】。(定义一个处理请求的接口。)(实现后继链。)
++++【ConcreteHandler】。(处理它所负责的请求。)(可访问它的后继者。)(如果可处理该请求,就处理之;否则将该请求转发给它的后继者。)
++++【Client】。(向链上的具体处理者(ConcreteHandler)对象提交请求。)
++++适用性:有多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定。(你想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。)(可处理一个请求的对象集合应被动态指定。)
//立钻哥哥:“Chain of Responsibility”责任链模式的代码实现
++++Handler类,定义一个处理请示的接口。abstract class Handler{ protected Handler successor;
//设置继任者 public void SetSuccessor(Handler successor){ this.successor = successor; }
//处理请求的抽象方法 public abstract void HandleRequest(int request); }
++++ConcreteHandler类,具体处理者类,处理它所负责的请求,可访问它的后继者,如何可处理该请求,就处理之,否则就将该请求转发给它的后继者。
++++ConcreteHandler1,当请求数在0到10之间则有权处理,否则转到下一位。class ConcreteHandler1 : Handler{ public override void HandleRequest(int request){ //0到10,处理此请求 if(request >= 0 && request < 10){ Console.WriteLine(“立钻哥哥Print: {0}处理请求{1}”, this.GetType().Name, request); }else if(successor != null){ //转移到下一位 successor.HandleRequest(request); } } }
++++ConcreteHandler2,当请求输在10到20之间则有权处理,否则转到下一位。class ConcreteHandler2 : Handler{ public override void HandleRequest(int request){ //10到20,处理此请求 if(request >= 10 && request < 20){ Console.WriteLine(“立钻哥哥Print: {0}处理请求{1}”, this.GetType().Name, request); }else if(successor != null){ //转到下一位 successor.HandlerRequest(request); } } }
++++ConcreteHandler3,当请求数在20到30之间则有权处理,否则转到下一位。class ConcreteHandler3 : Handler{ public override void HandleRequest(int request){ //20到30,处理此请求 if(request >= 20 && request < 30){ Console.WriteLine(“立钻哥哥Print: {0}处理请求{1}”, this.GetType().Name, request); }else if(successor != null){ //转移到下一位 successor.HandleRequest(request); } } }
++++客户端代码,向链上的具体处理者对象提交请求。static void Main(string[] args){ Handler h1 = new ConcreteHandler1(); Handler h2 = new ConcreteHandler2(); Handler h3 = new ConcreteHandler3();
//设置职责链上家和下家 h1.SetSuccessor(h2); h2.SetSuccessor(h3);
int[] requests = { 2, 5, 14, 22, 18, 3, 27, 20 }; foreach(int request in requests){ //循环给最小处理者提交请求,不同的数额,由不同权限处理者处理 h1.HandleRequest(request); }
Console.Read(); } |
++2B.18、解释器模式 |
++++解释器模式(Interpreter Pattern)是一种按照规定语法进行解析的方案,定义:Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language.(给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。)
++++解释器模式在实际的系统开发中使用得非常少,因为它会引起效率、性能以及维护等问题,一般在大中型的框架型项目能够找到它的身影,如一些数据分析工具、报表设计工具、科学计算工具等。解释器模式的不足:解释器模式为文法中的每一条规则至少定义了一个类,因此包含许多规则的文法可能难以管理和维护。(建议当文法非常复杂时,使用其他的技术如语法分析程序或编译器生成器来处理。)
++++解释器模式的意图:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。(如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。)
++解释器模式的优缺点++++优点1:解释器是一个简单语法分析工具,它最显著的优点就是扩展性,修改语法规则只要修改相应的非终结符表达式就可以了,若扩展语法,则只要增加非终结符类就可以了。++++缺点1:解释器模式会引起类膨胀。(每个语法都要产生一个非终结符表达式,语法规则比较复杂时,就可能产生大量的类文件,为维护带来了非常多的麻烦。)++++缺点2:解释器模式采用递归调用方法。(每个非终结符表达式只关心与自己有关的表达式,每个表达式需要知道最终的结果,必须一层一层地剥茧,无论是面向过程的语言还是面向对象的语言,递归都是在必要条件下使用的,它导致调试非常复杂。如果要排查一个语法错误,要一个一个断点地调试下去,直到最小的语法单元。)++++缺点3:效率问题。(解释器模式由于使用了大量的循环和递归,效率是一个不容忽略的问题,特别是用于解析复杂、冗长的语法时,效率是难以忍受的。)++++使用场景1:重复发生的问题可以使用解释器模式。++++使用场景2:一个简单语法需要解释的场景。(解释器模式一般用来解析比较标准的字符集。)++++注意事项1:尽量不要在重要的模块中使用解释器模式,否则维护会是一个很大的问题。 |
++++【AbstractExpression】:抽象解释器。(具体的解释任务由各个实现类实现,具体的解释器分别由TerminalExpression和NonterminalExpression完成。)(声明一个抽象的解释操作,这个接口为抽象语法树中所有的节点所共享。)
++++【TerminalExpression】:终结符表达式。(实现与文法中的元素相关联的解释操作,通常一个解释器模式只有一个终结符表达式,但有多个实例,对应不同的终结符。)(实现与文法中的终结符相关联的解释操作。)(一个句子中的每个终结符需要该类的一个实例。)
++++【NonterminalExpression】:非终结符表达式。(文法中的每条规则对应于一个非终结表达式。非终结符表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式。)(对文法中的每一条规则R::=R1R2...Rn都需要一个NonterminalExpression类。)(为从R1到Rn的每个字符都维护一个AbstractExpression类型的实例变量。)(为文法中的非终结符实现解释(Interpret)操作。解释(Interpret)一般要递归地调用表示R1到R2的那些对象的解释操作。)
++++【Context】:环境角色。(上下文。)(包含解释器之外的一些全局信息。)
++++【Client(客户)】。(构建(或被给定)表示该文法定义的语言中一个特定的句子的抽象语法树。该抽象语法树由NonterminalExpression和TerminalExpression的实例装配而成。)(调用解释操作。)
++++适用性:当有一个语言需要解释执行,并且我们可将该语言中的句子表示为一个抽象语法树时,可使用解释器模式。(该文法简单。对于复杂的文法,文法的类层次变得庞大而无法管理。此时语法分析程序生成器这样的工具是更好的选择。它们无需构建抽象语法树即可解释表达式,这样可以节省空间而且还可能节省时间。)(效率不是一个关键问题。最高效的解释器通常不是通过直接解释语法分析树实现的,而是首先将它们转换成另一种形式。例如,正则表达式通常被转换成状态机。但即使在这种情况下,转换器仍可用解释器模式实现,该模式仍是有用的。)
//立钻哥哥:interpreter解释器模式的代码实现
++++AbstractExpression(抽象表达式),声明一个抽象的解释操作,这个接口为抽象语法树中所有的节点所共享。abstract class AbstractExpression{ public abstract void Interpret(Context context); }
++++TerminalExpression(终结符表达式),实现与文法中的终结符相关联的解释操作。实现抽象表达式中所要求的接口,主要是一个interpret()方法。文法中每一个终结符都有一个具体终结表达式与之相对应。class TerminalExpression : AbstractExpression{ public override void Interpret(Context context){ Console.WriteLine(“立钻哥哥Print: 终端解释器”); } }
++++NonterminalExpression(非终结符表达式),为文法中的非终结符实现解释操作。对文法中每一条规则R1、R2......Rn都需要一个具体的非终结符表达式类。通过实现抽象表达式的interpret()方法实现解释操作。解释操作以递归方式调用上面所提到的代表R1、R2、......、Rn中各个符号的实例变量。class NonterminalExpression : AbstractExpression{ public override void Interpret(Context context){ Console.WriteLine(“立钻哥哥Print: 非终端解释器”); } }
++++Context,包含解释器之外的一些全局信息。class Context{ private string input; public string Input{ get{ return input; } set{ input = value; } }
private string output; public string Output{ get{ return output; } set{ output = value; } } }
++++客户端代码,构建表示该文法定义的语言中一个特定的句子的抽象语法树。调用解释操作。static void Main(string[] args){ Context context = new Context(); List<AbstractExpression> list = new List<AbstractExpression>(); list.Add(new TerminalExpression()); list.Add(new NonterminalExpression()); list.Add(new TerminalExpression()); list.Add(new TerminalExpression());
foreach(AbstractExpreesion exp in list){ exp.Interpret(context); }
Console.Read(); }
++++结果显示:终端解释器 非终端解释器 终端解释器 终端解释器 |
++2B.19、中介者模式 |
++++中介者模式的定义:Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently.(用一个中介对象封装一系列的对象交互,中介者使各对象不需要显式地相互作用,从而使其耦合松散,而且可以独立地改变它们之间的交互。)
++++中介者模式也叫做调停者模式,一个对象要和N多个对象交流,就像对象间的战争,很混乱。(这是,需要加入一个中心,所有的类都和中心交流,中心说怎么处理就怎么处理。);中介者模式是一个非常好的封装模式,也是一个很容易被滥用的模式,一个对象依赖几个对象是再正常不过的事情,但是纯理论家就会要求使用中介者模式来封装这种依赖关系,这是非常危险的!(使用中介者模式就必然会带来中介者的膨胀问题,这在一个项目中是很不恰当的。)
++++中介者模式的意图:用一个中介对象来封装一系列的对象交互。(中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。)
++中介者模式优缺点++++优点A.1、减少了子类生成。(Mediator将原本分布于多个对象间的行为集中在一起。改变这些行为只需生成Meditator的子类即可。这样各个Colleague类可被重用。)++++优点A.2、它将各Colleague解耦。(Mediator有利于各Colleague间的松耦合,可以独立的改变和复用各Colleague类和Mediator类。)++++优点A.3、它简化了对象协议。(用Mediator和各Colleague间的一对多的交互来代替多对多的交互。一对多的关系更易于理解、维护和扩展。)++++优点A.4、它对对象如何协作进行了抽象。(将中介作为一个独立的概念并将其封装在一个对象中,使我们将注意力从对象各自本身的行为转移到它们之间的交互上来。这有助于弄清楚一个系统中的对象是如何交互的。)++++缺点A.1、它使控制集中化。(中介者模式将交互的复杂性变为中介者的复杂性。因为中介者封装了协议,它可能变得比任一个Colleague都复杂。这可能使得中介者自身成为一个难于维护的庞然大物。)++++中介者模式很容易在系统中应用,也很容易在系统中误用。(当系统出现了“多对多”交互复杂的对象群时,不要急于使用中介者模式,而要先反思你的系统在设计上是不是合理。)++++优点B.1:Mediator的出现减少了各个Colleague的耦合,使得可以独立地改变和复用各个Colleague类和Mediator,由于把对象如何协作进行了抽象,将中介作为一个独立的概念并将其封装在一个对象中,这样关注的对象就从对象各自本身的行为转移到它们之间的交互上来,也就是站在一个更宏观的角度去看待系统。++++缺点B.1:由于ConcreteMediator控制了集中化,于是就把交互复杂性变味了中介者的复杂性,这就使得中介者会变得比任何一个ConcreteColleague都复杂。
|
++中介者模式的实际应用++++立钻哥哥:中介者模式也叫做调停者模式,一个对象要和N多个对象交流,就像对象间的战争,很混乱。这是,需要加入一个中心,所有的类都和中心交流,中心说怎么处理就怎么处理。++++应用1:机场调度中心。++++应用2:MVC框架。(C(Controller)就是一个中介者,叫做前端控制器(Front Controller),它的作用就是把M(Model,业务逻辑)和V(View,视图)隔离开,协调M和V协同工作,把M运行的结果和V代表的视图融合成一个前端可展示的页面,减少M和V的依赖关系。)(MVC框架已经成为一个非常流行、成熟的开发框架,这也是中介者模式的优点的一个体现。)++++应用3:媒体网关。++++应用4:中介服务。 |
++++【Mediator】:抽象中介者角色。(抽象中介者角色定义统一的接口,用于各同事角色之间的通信。)(中介者定义一个接口用于与各同事(Colleague)对象通信。)
++++【Concrete Mediator】:具体中介者角色。(具体中介者角色通过协调各同事角色实现协作行为,因此它必须依赖于各个同事角色。)(具体中介者通过协调各同事对象实现协作行为。)(了解并维护它的各个同事。)
++++【Colleague】:同事角色。(每一个同事角色都知道中介者角色,而且与其他的同事角色通信的时候,一定要通过中介者角色协作。)(每个同事类的行为分为两种:一种是同事本身的行为,比如改变对象本身的状态,处理自己的行为等,这种方法叫做自发行为(Self-Method),与其他的同事类或中介者没有任何的依赖;第二种是必须依赖中介者才能完成的行为,叫做依赖方法(Dep-Method)。)(每一个同事类都知道它的中介者对象。)(每一个同事对象在需与其他的同时通信的时候,与它的中介者通信。)
++++适用性:一组对象以定义良好但是复杂的方式进行通信,产生的相互依赖关系结构混乱且难以理解。(一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象。)(想定制一个分布在多个类中的行为,而又不想生成太多的子类。)
//立钻哥哥:Mediator中介者模式的代码实现
++++Colleague叫做抽象同事类,而ConcreteColleague是具体同事类,每个具体同事只知道自己的行为,而不了解其他同事类的情况,但它们却都认识中介者对象,Mediator是抽象中介者,定义了同事对象到中介者对象的接口,ConcreteMediator是具体中介者对象,实现抽象类的方法,它需要知道所有具体同事类,并从具体同事接收消息,向具体同事对象发出命令。
++++Mediator类(抽象中介者类)abstract class Mediator{ //定义一个抽象的发送消息方法,得到同事对象和发送消息 public abstract void Send(string message, Colleague colleague); }
++++Colleague类(抽象同事类)abstract class Colleague{ protected Mediator mediator;
// 构造方法,得到中介者对象 public Colleague(Mediator mediator){ this.mediator = mediator; } }
++++ConcreteMediator类(具体中介者类)class ConcreteMediator : Mediator{ private ConcreteColleague1 colleague1; private ConcreteColleague2 colleague2;
//需要了解所有的具体同事对象
public ConcreteColleague1 Colleague1{ set{ colleague1 = value; } }
public ConcreteColleague2 Colleague2{ set{ colleague2 = value; } }
//重写发送消息的方法,根据对象做出选择判断,通知对象 public override void Send(string message, Colleague colleague){ if(colleague == colleague1){ colleague2.Notify(message); }else{ colleague1.Notify(message); } } }
++++ConcreteColleague1和ConcreteColleague2等各种同事对象。class ConcreteColleague1 : Colleague{ public ConcreteColleague1(Mediator mediator) : base(mediator){ }
//发送消息时通常是中介者发送出去的 public void Send(string message){ mediator.Send(message, this); }
public void Notify(string message){ Console.WriteLine(“立钻哥哥Print: 同事1得到消息:” + message); } }
class ConcreteColleague2 : Colleague{ public ConcreteColleague2(Mediator mediator) : base(mediator){ }
public void Send(string message){ mediator.Send(message, this); }
public void Notify(string message){ Console.WriteLine(“立钻哥哥Print: 同事2得到信息:” + message); } }
++++客户端调用:static void Main(string[] args){ ConcreteMediator m = new ConcreteMediator();
//让两个具体同事类认识中介者对象 ConcreteColleague1 c1 = new ConcreteColleague1(m); ConcreteColleague2 c2 = new ConcreteColleague2(m);
//让中介者认识各个各个具体同事类对象 m.Colleague1 = c1; m.Colleague2 = c2;
c1.Send(“立钻哥哥:吃过饭了吗?”); c2.Send(“立钻哥哥:没有呢,你打算请客?”);
Console.Read(); } |
++2B.20、访问者模式 |
++++访问者模式(Visitor Pattern)是一个相对简单的模式,定义:Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.(封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。)
++++访问者模式是一种集中规则模式,特别适用于大规模重构的项目,在这一阶段需求已经非常清晰,原系统的功能点也已经明确,通过访问者模式可以很容易把一些功能进行梳理,达到最终目的:功能集中化;访问者模式适用于数据结构相对稳定的系统。(它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由地演化。)
++++访问者模式的目的是要把处理从数据结构分离出来。(有比较稳定的数据结构,又有易于变化的算法的话,使用访问者模式就是比较合适的,因为访问者模式使得算法操作的增加变得容易。);访问者模式的能力和复杂性是把双刃剑,只有当我们真正需要它的时候,才考虑使用它。(不要为了展示自己的面向对象的能力或是沉迷于模式当中,往往会误用这个模式,所以一定要好好理解它的适用性。)
++++访问者模式的意图:表示一个作用于某对象结构中的各元素的操作。它使我们可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
++访问者模式的优缺点++++优点A.1、访问者模式使得易于增加新的操作。(访问者使得增加依赖于复杂对象结构的构件的操作变得容易了。仅需增加一个新的访问者即可在一个对象结构上定义一个新的操作。相反,如果每个功能都分散在多个类之上的话,定义新的操作时必须修改每一类。)++++优点A.2、访问者集中相关的操作而分离无关的操作。(相关的行为不是分布在定义该对象结构的各个类上,而是集中在一个访问者中。无关行为却被分别放在它们各自的访问者子类中。这就既简化了这些元素的类,也简化了再这些访问者中定义的算法。所有与它的算法相关的数据结构都可以被隐藏在访问者中。)++++缺点A.1、增加新的ConcreteElement类很困难.(Visitor模式使得难以增加新的Element的子类。每添加一个新的ConcreteElement都要在Vistor中添加一个新的抽象操作,并在每一个ConcretVisitor类中实现相应的操作。有时可以在Visitor中提供一个缺省的实现,这一实现可以被大多数的ConcreteVisitor继承,但这与其说是一个规律还不如说是一种例外。)(在应用访问者模式时考虑关键的问题是系统的哪个部分会经常变化,是作用于对象结构上的算法呢还是构成该结构的各个对象的类。如果老是有新的ConcreteElement类加入进来的话,Visitor类层次将变得难以维护。在这种情况下,直接在构成该结构的类中定义这些操作可能更容易一些。如果Element类层次时稳定的,而我们不断地增加操作或修改算法,访问者模式可以帮助我们管理这些改动。)++++说明1:通过类层次进行访问。(一个迭代器(Iterator模式)可以通过调用节点对象的特定操作来遍历整个对象结构,同时访问这些对象。但是迭代器不能对具有不同元素类型的对象结构进行操作。)++++说明2:累计状态。(当访问者访问对象结构中的每一个元素时,它可能会累积状态。如果没有访问者,这一状态将作为额外的参数传递给进行遍历的操作,或者定义为全局变量。)++++说明3:破坏封装。(访问者方法假定ConcreteElement接口的功能足够强,足以让访问者进行它们的工作。结果是,该模式常常迫使我们提供访问元素内部状态的公共操作,这可能会破坏它的封装性。)++++优点B.1:符合单一职责原则。(具体元素角色也就是Employee抽象类的两个子类负责数据的加载,而Visitor类则负责报表的展现,两个不同的职责非常明确地分离开来,各自演绎变化。)++++优点B.2:优秀的扩展性。(由于职责分开,继续增加对数据的操作是非常快捷的。)++++优点B.3:灵活性非常高。++++缺点B.1:具体元素对访问者公布细节。(访问者要访问一个类就必然要求这个类公布一些方法和数据,也就是说访问者关注了其他类的内部细节,这是迪米特法则所不建议的。)++++缺点B.2:具体元素变更比较困难。(具体元素角色的增加、删除、修改都是比较困难的。)++++缺点B.3:违背了依赖倒置原则。(访问者依赖的是具体元素,而不是抽象元素,这破坏了依赖倒置原则,特别是在面向对象的编程中,抛弃了对接口的依赖,而直接依赖实现类,扩展比较难。)++++使用场景1:一个对象结构包含很多类对象,它们有不同的接口,而我们想对这些对象实施一些依赖于其具体类的操作。(也就是说用迭代器模式已经不能胜任的情景。)++++使用场景2:需要对一个对象结构中的对象进行很多不同并且不相关的操作,而我们想避免让这些操作“污染”这些对象的类。++++使用场景3:一定要考虑使用访问者模式的场景:业务规则要求遍历多个不同的对象。(这本身也是访问者模式出发点,迭代器模式只能访问同类或同接口的数据,而访问者模式是对迭代器模式的扩充,可以遍历不同的对象,然后执行不同的操作,也就是针对访问的对象不同,执行不同的操作。) |
++++【Visitor】:抽象访问者。(抽象类或者接口,声明访问者可以访问哪些元素,具体到程序中就是visit方法的参数定义哪些对象是可以被访问的。)(为该对象结构中ConcreteElement的每一个类声明一个Visit操作。该操作的名字和特征标识了发送Visit请求给该访问者的那个类。这使得访问者可以确定正被访问元素的具体的类。这样访问者就可以通过该元素的特定接口直接访问它。)
++++【ConcreteVisitor】:具体访问者。(它影响访问者访问到一个类后该怎么干,要做什么事情。)(实现每个由Visitor声明的操作。每个操作实现本算法的一部分,而该算法片段乃是对应于结构中的对象的类。ConcreteVisitor为该算法提供了上下文并存储它的局部状态。这一状态常常在遍历该结构的过程中累计结果。)
++++【Element】:抽象元素。(接口或者抽象类,声明接受哪一类访问者访问,程序上是通过accept方法中的参数来定义的。)(抽象元素有两类方法:一是本身的业务逻辑,也就是元素作为一个业务处理单元必须完成的职责;另外一个是允许哪一个访问者来访问。)(定义一个Accept操作,它以一个访问者为参数。)
++++【ConcreteElement】:具体元素。(实现accept方法,通常是visitor.visit(this),基本上都形成了一种模式了。)(实现Accept操作,该操作以一个访问者为参数。)
++++【ObjectStruture】:结构对象。(元素产生者,一般容纳在多个不同类、不同接口的容器,如List、Set、Map等。在项目中,一般很少抽象出这个角色。)(能枚举它的元素。)(可以提供一个高层的接口以允许该访问者访问它的元素。)(可以是一个复合(Composite模式)或者一个集合,如一个列表或一个无序集合。)
++++适用性:一个对象结构包含很多类对象,它们有不同的接口,而我们想对这些对象实施一些依赖于其具体类的操作。(需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而我们想避免让这些操作“污染”这些对象的类。Visitor使得我们可以将相关的操作集中起来定义在一个类中。当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作。)
//立钻哥哥:Visitor访问者模式的代码实现
++++Visitor类,为该对象结构中ConcreteElement的每一个类声明一个Visit操作。abstract class Visitor{ public abstract void VisitConcreteElementA(ConcreteElementA concreteElementA); public abstract void VisitConcreteElementB(ConcreteElementB concreteElementB); }
++++ConcreteVisitor1和ConcreteVisitor2类,具体访问者,实现每个由Visitor声明的操作。每个操作实现算法的一部分,而该算法片段乃是对应于结构中对象的类。class ConcreteVisitor1 : Visitor{ public override void VisitConcreteElementA(ConcreteElementA concreteElementA){ Console.WriteLine(“立钻哥哥Print: {0}被{1}访问”, concreteElementA.GetType().Name, this.GetType().Name); }
public override void VisitConcreteElementB(ConcreteElementB concreteElementB){ Console.WriteLine(“立钻哥哥Print: {0}被{1}访问”, concreteElementB.GetType().Name, this.GetType().Name); } }
class ConcreteVisitor2 : Visitor{ //代码与上类类似,省略.... }
++++Element类,定义一个Accept操作,它以一个访问者为参数:abstract class Element{ public abstract void Accept(Visitor visitor); }
++++ConcreteElementA和ConcreteElementB类,具体元素,实现Accept操作。class ConcreteElementA : Element{ //充分利用双分派技术,实现处理与数据结构的分离。 public override void Accept(Visitor visitor){ visitor.VisitConcreteElementA(this); }
//其他的相关方法 public void OperationA(){ } }
class ConcreteElementB : Element{ public override void Accept(Visitor visitor){ visitor.VisitConcreteElementB(this); }
public void OperationB(){ } }
++++ObjectStructure类,能枚举它的元素,可以提供一个高层的接口以允许访问者访问它的元素。class ObjectStructure{ private List<Element> elements = new List<Element>();
public void Attach(Element element){ elements.Add(element); }
public void Detach(Element element){ elements.Remove(element); }
public void Accept(Visitor visitor){ foreach(Element e in elements){ e.Accept(visitor); } } }
++++客户端代码: static void Main(string[] args){ ObjectStructure o = new ObjectStructure(); o.Attach(new ConcreteElementA()); o.Attach(new ConcreteElementB());
ConcreteVisitor1 v1 = new ConcreteVisitor1(); ConcreteVisitor2 v2 = new ConcreteVisitor2();
o.Accept(v1); o.Accept(v2);
Console.Read(); } |
++2B.21、策略模式 |
++++策略模式(Strategy Pattern)是一种比较简单的模式,也叫做政策模式(Policy Pattern),定义:Define a family of algorithms, encapsulate each one, and make them interchangeable.(定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。)
++++策略模式就是用来封装算法的,但在实践中,我们发现可以用它来封装几乎任何类型的规则,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性;策略模式使用的就是面向对象的继承和多态机制,非常容易理解和掌握。
++++策略模式的意图:定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
++++【Context】:封装角色。(它也叫做上下文角色,起承上启下封装作用,屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化。)(用一个ConcreteStrategy对象类配置。)(维护一个对Strategy对象的引用。)(可定义一个接口来让Stategy访问它的数据。)
++++【Strategy】:抽象策略角色。(策略、算法家族的抽象,通常为接口,定义每个策略或算法必须具有的方法和属性。)(定义所有支持的算法的公共接口。Context使用这个接口来调用某ConcreteStrategy定义的算法。)
++++【ConcreteStrategy】:具体策略角色。(实现抽象策略中的操作,该类含有具体的算法。)(以Strategy接口实现某具体算法。)
++++策略模式是一个非常简单的模式。(它在项目中使用得非常多,但它单独使用的地方就比较少了,因为它有致命缺陷:所有的策略都需要暴露出去,这样才方便客户端决定使用哪一个策略。)(策略模式只是实现了锦囊的管理,没有严格定义“适当的场景”拆开“适当的锦囊”,在实际项目中,我们一般通过工厂方法模式来实现策略类的声明。)
++++适用性:许多相关的类仅仅是行为有异。“策略”提供了一种用多个行为中的一个行为来配置一个类的方法。(需要使用一个算法的不同变体。)(算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。)(一个类定义了多种行为,兵器这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。)
//立钻哥哥:Strategy策略模式的代码实现
++++Strategy类,定义所有支持的算法的公共接口//抽象算法类 abstract class Strategy{ //算法方法 public abstract void AlgorithmInterface(); }
++++ConcreteStrategy,封装了具体的算法或行为,继承于Strategy//具体算法A class ConcreteStrategyA : Strategy{ //算法A实现方法 public override void AlgorithmInterface(){ Console.WriteLine(“立钻哥哥Print: 算法A实现”); } }
//具体算法B class ConcreteStrategyB : Strategy{ //算法B实现方法 public override void AlgorithmInterface(){ Console.WriteLine(“立钻哥哥Print: 算法B实现”); } }
//具体算法C class ConcreteStrategyC : Strategy{ //算法C实现方法 public override void AlgorithmInterface(){ Console.WriteLine(“算法C实现”); } }
++++Context,用一个ConcreteStrategy来配置,维护一个对Strategy对象的引用。//上下文 class Context{ Strategy strategy;
//初始化时,传入具体的策略对象 public Context(Strategy strategy){ this.strategy = strategy; }
//上下文接口
//根据具体的策略对象,调用其算法的方法 public void ContextInterface(){ strategy.AlgorithmInterface(); } }
++++客户端代码:static void Main(string[] args){ Context context;
//由于实例化不同的策略,所以最终在调用context.ContextInterface();时,所获得的结果就不尽相同
context = new Context(new ConcreteStrategyA()); context.ContextInterface();
context = new Context(new ConcreteStrategyB()); context.ContextInterface();
context = new Context(new ConcreteStrategyC()); context.ContextInterface();
Console.Read(); } |
++2B.22、备忘录模式 |
++++备忘录模式(Memento Pattern)提供了一种弥补真实世界缺陷的方法,让“后悔药”在程序的世界中真实可行,定义:Without violating encapsulation, capture and externalize an object’s internal state so that the object can be restored to this state later.(在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。)(备忘录模式就是一个对象的备份模式,提供了一种程序数据的备份方法。)
++++如果在某个系统中使用命令模式时,需要实现命令的撤销功能,那么命令模式可以使用备忘录模式来存储可撤销操作的状态。(使用备忘录可以把复杂的对象内部信息对其他的对象屏蔽起来。);当角色的状态改变的时候,有可能这个状态无效,这时候就可以使用暂时存储起来的备忘录将状态复原。
++++备忘录模式的意图:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
++备忘录模式的优缺点++++立钻哥哥:由于备忘录模式有太多的变形和处理方式,每种方式都有它自己的优点和缺点,标准的备忘录模式很难在项目中遇到,基本上都有一些变换处理方式。因此,我们在使用备忘录模式时主要了解如何应用以及需要注意哪些事项就可以了。++++使用场景1:需要保存和恢复数据的相关状态场景。++++使用场景2:提供一个可回滚(rollback)的操作。++++使用场景3:需要监控的副本场景中。++++使用场景4:数据库连接的事务管理就是用的备忘录模式。++++注意事项1:备忘录的生命期。(备忘录创建出来就要在“最近”的代码中使用,要主动管理它的生命周期,建立就要使用,不使用就要立刻删除其引用,等待垃圾回收器对它的回收处理。)++++注意事项2:备忘录的性能。(不要在频繁建立备份的场景中使用备忘录模式(比如一个for循环中),原因有二:一是控制不了备忘录建立的对象数量;二是大对象的建立是要消耗资源的,系统的性能需要考虑。) |
++++【Originator】:发起人(原发器)。(负责创建一个备忘录Memento,用以记录当前时刻它的内部状态,并可使用备忘录恢复内部状态。Originator可根据需要决定Memento存储Originator的哪些内部状态。)(记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和恢复备忘录数据。)(原发器创建一个备忘录,用以记录当前时刻它的内部状态。)(使用备忘录恢复内部状态。)
++++【Memento】:备忘录。(负责存储Originator对象的内部状态,并可防止Originator以外的其他对象访问备忘录Memento。)(备忘录有两个接口,Caretaker只能看到备忘录的窄接口,它只能将备忘录传递给其他对象。Originator能够看到一个宽接口,允许它访问返回到先前状态所需的所有数据。)(负责存储Originator发起人对象的内部状态,在需要的时候提供发起人需要的内部状态。)(备忘录存储原发器对象的内部状态。原发器根据需要决定备忘录存储原发器的哪些内部状态。)(防止原发器以外的其他对象访问备忘录。备忘录实际上有两个接口,管理者(caretaker)只能看到备忘录的窄接口:它只能将备忘录传递给其他对象。相反,原发器能够看到一个宽接口,允许它访问返回到先前状态所需的所有数据。理想的情况是只允许生成本备忘录的那个原发器访问本备忘录的内部状态。)
++++【Caretaker】:管理者。(负责保存好备忘录Memento,不能对备忘录的内容进行操作或检查。)(对备忘录进行管理、保存和提供备忘录。)(负责保存好备忘录。)(不能对备忘录的内容进行操作或检查。)
++++适用性:必须保存一个对象在某一个时刻的(部分)状态,这样以后需要时它才能恢复到先前的状态。(如果一个用接口来让其他对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。)
//立钻哥哥:Memento备忘录模式的代码实现
++++发起人(Originator)类:class Originator{ //需要保存的属性,可能有多个 private string state; public string State{ get{ return state; } set{ state = value; } }
//创建备忘录,将当前需要保存的信息导入并实例化出一个Memento对象 public Memento CreateMemento(){ return (new Memento(state)); }
//恢复备忘录,将Memento导入并将相关数据恢复 public void SetMemento(Memento memento){ state = memento.State; }
//显示数据 public void Show(){ Console.WriteLine(“立钻哥哥Print: State=” + state); } }
++++备忘录(Memento)类:class Memento{ private string state;
//构造方法,将相关数据导入 public Memento(string state){ this.state = state; }
//需要保存的数据属性,可以是多个 public string State{ get{ return state; } } }
++++管理者(Caretaker)类:class Caretaker{ private Memento memento;
//得到或设置备忘录 public Memento Memento{ get{ return memento; } set{ memento = value; } } }
++++客户端程序:static void Main(string[] args){ Originator o = new Originator();
//Originator初始状态,状态属性为“on” o.State = “On”; o.Show();
//保存状态时,由于有了很好的封装,可以隐藏Originator的实现细节 Caretaker c = new Caretaker(); c.Memento = o.CreateMemento();
//Originator改变了状态属性为“Off” o.State = “Off”; o.Show();
//恢复原初始状态 o.SetMemento(c.Memento); o.Show();
Console.Read(); } |
++2B.23、迭代器模式 |
++++迭代器模式(Iterator Pattern)是一个没落的模式,基本上没人会单独写一个迭代器,除非是产品性质的开发,定义:Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.(它提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节。)
++++迭代器模式的关键思想是将对列表的访问和遍历从列表对象中分离出来并放入一个迭代器(iterator)对象中。(迭代器类定义了一个访问该列表元素的接口。)(迭代器对象负责跟踪当前的元素,即,它知道哪些元素已经遍历过了。);当我们需要访问一个聚集对象,而且不管这些对象是什么都需要遍历的时候,我们就应该考虑用迭代器模式。(我们需要对聚集有多种方式遍历时,可以考虑用迭代器模式。)(为遍历不同的聚集结构提供如开始、下一个、是否结束、当前哪一项等统一的接口。)
++++迭代器模式的意图:提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴露对象的内部表示。(一个聚合对象,如列表(list),应该提供一种方法来让别人可以访问它的元素,而又不需暴露它的内部结构。此外,针对不同的需要,可能要以不同的方式遍历这个列表。但是即使可以预见到所需的那些遍历操作,我们可能也不希望列表的接口中充斥着各种不同遍历的操作。有时还可能需要在同一个表列上同时进行多个遍历。)
++迭代器模式的作用++++作用1:它支持以不同的方式遍历一个聚合。(复杂的聚合可用多种方式进行遍历。)++++作用2:迭代器简化了聚合的接口。(有了迭代器的遍历接口,聚合本身就不再需要类似的遍历接口了。这样就简化了聚合的接口。)++++作用3:在同一个聚合上可以有多个遍历。(每个迭代器保持它自己的遍历状态。因此我们可以同时进行多个遍历。) |
++++【Iterator】:抽象迭代器。(抽象迭代器负责定义访问和遍历元素的接口,而且基本上是有固定的3个方法:firest()获得第一个元素,next()访问下一个元素,isDone()是否已经访问到底部。)(迭代器定义访问和遍历元素的接口。)
++++【ConcreteIterator】:具体迭代器。(具体迭代器角色要实现迭代器接口,完成容器元素的遍历。)(具体迭代器实现迭代器接口。)(对该聚合遍历时跟踪当前位置。)
++++【Aggregate】:抽象容器(聚合)。(容器角色负责提供创建具体迭代器角色的接口,必须提供一个类似createIterator()这样的方法。)(聚合定义创建相应迭代器对象的接口。)
++++【ConcreteAggregate】:具体容器。(具体容器实现容器接口定义的方法,创造出容纳迭代器的对象。)(具体聚合实现创建相应迭代器的接口,该操作返回ConcreteIterator的一个适当的实例。)
++++适用性:访问一个聚合对象的内容而无需暴露它的内部表示。(支持对聚合对象的多种遍历。)(为遍历不同的聚合结构提供一个统一的接口,即支持多态迭代。)
//立钻哥哥:Iterator迭代器模式的代码实现
++++Iterator迭代器抽象类abstract class Iterator{ //用于定义得到开始对象、得到下一个对象、判断是否到结尾、当前对象等抽象方法,统一接口 public abstract object First(); public abstract object Next(); public abstract bool IsDone(); public abstract object CurrentItem(); }
++++Aggregate聚集抽象类abstract class Aggregate{ //创建迭代器 public abstract Iterator CreateIterator(); }
++++ConcreteIterator具体迭代器类,继承Iteratorclass ConcreteIterator : Iterator{ //定义了一个具体聚集对象 private ConcreteAggregate aggregate; private int current = 0;
//初始化时将具体的聚集对象传入 public ConcreteIterator(ConcreteAggregate aggregate){ this.aggregate = aggregate; }
//得到聚集的第一个对象 public override object First(){ return aggregate[0]; }
//得到聚集的下一个对象 public override object Next(){ object ret = null; current++;
if(current < aggregate.Count){ ret = aggregate[current]; } return ret; }
//判断当前是否遍历到结尾,到结尾返回true public override bool IsDone(){ return current >= aggregate.Count ? true : false; }
//返回当前的聚集对象 public override object CurrentItem(){ return aggregate[current]; } }
++++ConcreteAggregate具体聚集类,继承Aggregateclass ConcreteAggregate : Aggregate{ //声明一个List泛型变量,用于存放聚合对象,用ArrayList同样可以实现 private List<object> items = new List<object>();
public override Iterator CreateIterator(){ return new ConcreteIterator(this); }
public int Count{ //返回聚集总个数 get{ return items.Count; } }
//声明一个索引器 public object this[int index]{ get{ return items[index]; } set{ items.Insert(index, value); } } }
++++客户端代码:static void Main(string[] args){ //聚集对象(公交车) ConcreteAggregate a = new ConcreteAggregate();
//对象数组(乘客) a[0] = “立钻哥哥”; a[1] = “美女1”; a[2] = “美女2”; a[3] = “美女3”; a[4] = “帅哥4”; a[5] = “帅哥5”;
//声明迭代器对象(售票员) Iterator i = new ConcreteIterator(a);
//从第一个乘客开始 object item = i.First(); while(!i.IsDone()){ Console.WriteLine(“立钻哥哥Print: {0}请买车票!”, i.CurrentItem()); i.Next(); }
Console.Read(); }
++++运行结果:立钻哥哥 请买车票 美女1 请买车票 美女2 请买车票 美女3 请买车票 帅哥1 请买车票 帅哥2 请买车票
++++立钻哥哥:当我们需要对聚集多种方式遍历时,可以考虑用迭代器模式。 |
#第三篇:框架编程篇 |
++++【大话数据结构(C实现)】:https://blog.csdn.net/VRunSoftYanlz/article/details/104242455
++++【大话数据结构(C#实现)】:https://blog.csdn.net/VRunSoftYanlz/article/details/104243017
++++【大话设计模式(C#实现)】:https://blog.csdn.net/VRunSoftYanlz/article/details/104256981
++++【大话代码架构(C#实现)】:https://blog.csdn.net/VRunSoftYanlz/article/details/104271806
++++【大话数据结构(C实现)】:https://blog.csdn.net/VRunSoftYanlz/article/details/104242455
++++【大话数据结构(C#实现)】:https://blog.csdn.net/VRunSoftYanlz/article/details/104243017
++++【大话设计模式(C#实现)】:https://blog.csdn.net/VRunSoftYanlz/article/details/104256981
++++【设计模式简单整理】:https://blog.csdn.net/VRunSoftYanlz/article/details/79839641
++++【设计模式(精华篇)】:https://blog.csdn.net/VRunSoftYanlz/article/details/81322678
++++【游戏框架】:https://blog.csdn.net/VRunSoftYanlz/article/details/80630325
++++【C/C++函数与算法】:https://blog.csdn.net/VRunSoftYanlz/article/details/104026090
++++【设计模式简单整理】:https://blog.csdn.net/VRunSoftYanlz/article/details/79839641
++++【设计模式(精华篇)】:https://blog.csdn.net/VRunSoftYanlz/article/details/81322678
++++【C++从入门到放弃】:https://blog.csdn.net/VRunSoftYanlz/article/details/103849127
++++【Linux从入门到放弃】:https://blog.csdn.net/VRunSoftYanlz/article/details/104176967
++++【Python从入门到放弃】:https://blog.csdn.net/VRunSoftYanlz/article/details/101393069
++++【XLua教程】:https://blog.csdn.net/VRunSoftYanlz/article/details/81141502
++++【Unity Shader教程】:https://blog.csdn.net/VRunSoftYanlz/article/details/80372628
++++【SteamVR2.2.0快速入门】:https://blog.csdn.net/VRunSoftYanlz/article/details/88833579
++++【SteamVR实战之PMCore】:https://blog.csdn.net/VRunSoftYanlz/article/details/89463658
++++【Unity企业内训】:https://blog.csdn.net/VRunSoftYanlz/article/details/82634668
++++【Cocos2dx面试题】:https://blog.csdn.net/VRunSoftYanlz/article/details/78630967
++++【Unity面试题】:https://blog.csdn.net/VRunSoftYanlz/article/details/78630687
++++【C++C铸就生存利器】分类:https://blog.csdn.net/vrunsoftyanlz/category_9325802.html
++++【人工智能AI2026】分类:https://blog.csdn.net/vrunsoftyanlz/category_9212024.html
++++【立钻哥哥CSDN空间】:https://blog.csdn.net/VRunSoftYanlz/
立钻哥哥推荐的拓展学习链接(Link_Url) |
++++立钻哥哥Unity 学习空间: http://blog.csdn.net/VRunSoftYanlz/
++++虚拟现实VR资讯: https://blog.csdn.net/VRunSoftYanlz/article/details/89165846
++++HTC_VIVE开发基础:https://blog.csdn.net/VRunSoftYanlz/article/details/81989970
++++Oculus杂谈:https://blog.csdn.net/VRunSoftYanlz/article/details/82469850
++++Oculus安装使用:https://blog.csdn.net/VRunSoftYanlz/article/details/82718982
++++Unity+SteamVR=>VR:https://blog.csdn.net/VRunSoftYanlz/article/details/88809370
++++Unity减少VR晕眩症:https://blog.csdn.net/VRunSoftYanlz/article/details/89115518
++++SteamVR简介:https://blog.csdn.net/VRunSoftYanlz/article/details/86484254
++++SteamVR脚本功能分析:https://blog.csdn.net/VRunSoftYanlz/article/details/86531480
++++SteamVR2.0开发指南:https://blog.csdn.net/VRunSoftYanlz/article/details/86618187
++++SteamVR2.2.0开发指南:https://blog.csdn.net/VRunSoftYanlz/article/details/88784527
++++SteamVR2.2.0快速入门:https://blog.csdn.net/VRunSoftYanlz/article/details/88833579
++++SteamVR2.2.0交互系统:https://blog.csdn.net/VRunSoftYanlz/article/details/89199778
++++SteamVR2.2.0传送机制:https://blog.csdn.net/VRunSoftYanlz/article/details/89390866
++++SteamVR2.2.0教程(一):https://blog.csdn.net/VRunSoftYanlz/article/details/89324067
++++SteamVR2.2.0教程(二):https://blog.csdn.net/VRunSoftYanlz/article/details/89894097
++++SteamVR_Skeleton_Poser:https://blog.csdn.net/VRunSoftYanlz/article/details/89931725
++++SteamVR实战之PMCore:https://blog.csdn.net/VRunSoftYanlz/article/details/89463658
++++SteamVR/Extras:https://blog.csdn.net/VRunSoftYanlz/article/details/86584108
++++SteamVR/Input:https://blog.csdn.net/VRunSoftYanlz/article/details/86601950
++++OpenXR简介:https://blog.csdn.net/VRunSoftYanlz/article/details/85726365
++++VRTK杂谈:https://blog.csdn.net/VRunSoftYanlz/article/details/82562993
++++VRTK快速入门(杂谈):https://blog.csdn.net/VRunSoftYanlz/article/details/82955267
++++VRTK官方示例(目录):https://blog.csdn.net/VRunSoftYanlz/article/details/82955410
++++VRTK代码结构(目录):https://blog.csdn.net/VRunSoftYanlz/article/details/82780085
++++VRTK(SceneResources):https://blog.csdn.net/VRunSoftYanlz/article/details/82795400
++++VRTK_ControllerEvents:https://blog.csdn.net/VRunSoftYanlz/article/details/83099512
++++VRTK_InteractTouch:https://blog.csdn.net/VRunSoftYanlz/article/details/83120220
++++虚拟现实行业应用:https://blog.csdn.net/VRunSoftYanlz/article/details/88360157
++++Steam平台上的VR:https://blog.csdn.net/VRunSoftYanlz/article/details/88960085
++++Steam平台热销VR:https://blog.csdn.net/VRunSoftYanlz/article/details/89007741
++++VR实验:以太网帧的构成:https://blog.csdn.net/VRunSoftYanlz/article/details/82598140
++++实验四:存储器扩展实验:https://blog.csdn.net/VRunSoftYanlz/article/details/87834434
++++FrameVR示例V0913:https://blog.csdn.net/VRunSoftYanlz/article/details/82808498
++++FrameVR示例V1003:https://blog.csdn.net/VRunSoftYanlz/article/details/83066516
++++SwitchMachineV1022:https://blog.csdn.net/VRunSoftYanlz/article/details/83280886
++++PlaySceneManagerV1022:https://blog.csdn.net/VRunSoftYanlz/article/details/83280886
++++Unity5.x用户手册:https://blog.csdn.net/VRunSoftYanlz/article/details/81712741
++++Unity面试题ABC:https://blog.csdn.net/vrunsoftyanlz/article/details/78630687
++++Unity面试题D:https://blog.csdn.net/VRunSoftYanlz/article/details/78630838
++++Unity面试题E:https://blog.csdn.net/vrunsoftyanlz/article/details/78630913
++++Unity面试题F:https://blog.csdn.net/VRunSoftYanlz/article/details/78630945
++++Cocos2dx面试题:https://blog.csdn.net/VRunSoftYanlz/article/details/78630967
++++禅道[zentao]:https://blog.csdn.net/VRunSoftYanlz/article/details/83964057
++++Lua快速入门篇(Xlua拓展):https://blog.csdn.net/VRunSoftYanlz/article/details/81173818
++++Lua快速入门篇(XLua教程):https://blog.csdn.net/VRunSoftYanlz/article/details/81141502
++++Lua快速入门篇(基础概述):https://blog.csdn.net/VRunSoftYanlz/article/details/81041359
++++框架知识点:https://blog.csdn.net/VRunSoftYanlz/article/details/80862879
++++游戏框架(UI框架夯实篇):https://blog.csdn.net/vrunsoftyanlz/article/details/80781140
++++游戏框架(初探篇):https://blog.csdn.net/VRunSoftYanlz/article/details/80630325
++++.Net框架设计:https://blog.csdn.net/VRunSoftYanlz/article/details/87401225
++++从零开始学架构:https://blog.csdn.net/VRunSoftYanlz/article/details/88095895
++++设计模式简单整理:https://blog.csdn.net/vrunsoftyanlz/article/details/79839641
++++专题:设计模式(精华篇):https://blog.csdn.net/VRunSoftYanlz/article/details/81322678
++++U3D小项目参考:https://blog.csdn.net/vrunsoftyanlz/article/details/80141811
++++Unity小游戏算法分析:https://blog.csdn.net/VRunSoftYanlz/article/details/87908365
++++Unity案例(Vehicle):https://blog.csdn.net/VRunSoftYanlz/article/details/82355876
++++UML类图:https://blog.csdn.net/vrunsoftyanlz/article/details/80289461
++++PowerDesigner简介:https://blog.csdn.net/VRunSoftYanlz/article/details/86500084
++++Unity知识点0001:https://blog.csdn.net/vrunsoftyanlz/article/details/80302012
++++Unity知识点0008:https://blog.csdn.net/VRunSoftYanlz/article/details/81153606
++++U3D_Shader编程(第一篇:快速入门篇):https://blog.csdn.net/vrunsoftyanlz/article/details/80372071
++++U3D_Shader编程(第二篇:基础夯实篇):https://blog.csdn.net/vrunsoftyanlz/article/details/80372628
++++Unity引擎基础:https://blog.csdn.net/vrunsoftyanlz/article/details/78881685
++++Unity面向组件开发:https://blog.csdn.net/vrunsoftyanlz/article/details/78881752
++++Unity物理系统:https://blog.csdn.net/vrunsoftyanlz/article/details/78881879
++++Unity2D平台开发:https://blog.csdn.net/vrunsoftyanlz/article/details/78882034
++++UGUI基础:https://blog.csdn.net/vrunsoftyanlz/article/details/78884693
++++UGUI进阶:https://blog.csdn.net/vrunsoftyanlz/article/details/78884882
++++UGUI综合:https://blog.csdn.net/vrunsoftyanlz/article/details/78885013
++++Unity动画系统基础:https://blog.csdn.net/vrunsoftyanlz/article/details/78886068
++++Unity动画系统进阶:https://blog.csdn.net/vrunsoftyanlz/article/details/78886198
++++Navigation导航系统:https://blog.csdn.net/vrunsoftyanlz/article/details/78886281
++++Unity特效渲染:https://blog.csdn.net/vrunsoftyanlz/article/details/78886403
++++Unity数据存储:https://blog.csdn.net/vrunsoftyanlz/article/details/79251273
++++Unity中Sqlite数据库:https://blog.csdn.net/vrunsoftyanlz/article/details/79254162
++++WWW类和协程:https://blog.csdn.net/vrunsoftyanlz/article/details/79254559
++++Unity网络:https://blog.csdn.net/vrunsoftyanlz/article/details/79254902
++++Unity资源加密:https://blog.csdn.net/VRunSoftYanlz/article/details/87644514
++++PhotonServer简介:https://blog.csdn.net/VRunSoftYanlz/article/details/86652770
++++编写Photon游戏服务器:https://blog.csdn.net/VRunSoftYanlz/article/details/86682935
++++C#事件:https://blog.csdn.net/vrunsoftyanlz/article/details/78631267
++++C#委托:https://blog.csdn.net/vrunsoftyanlz/article/details/78631183
++++C#集合:https://blog.csdn.net/vrunsoftyanlz/article/details/78631175
++++C#泛型:https://blog.csdn.net/vrunsoftyanlz/article/details/78631141
++++C#接口:https://blog.csdn.net/vrunsoftyanlz/article/details/78631122
++++C#静态类:https://blog.csdn.net/vrunsoftyanlz/article/details/78630979
++++C#中System.String类:https://blog.csdn.net/vrunsoftyanlz/article/details/78630945
++++C#数据类型:https://blog.csdn.net/vrunsoftyanlz/article/details/78630913
++++Unity3D默认的快捷键:https://blog.csdn.net/vrunsoftyanlz/article/details/78630838
++++游戏相关缩写:https://blog.csdn.net/vrunsoftyanlz/article/details/78630687
++++UnityAPI.Rigidbody刚体:https://blog.csdn.net/VRunSoftYanlz/article/details/81784053
++++UnityAPI.Material材质:https://blog.csdn.net/VRunSoftYanlz/article/details/81814303
++++UnityAPI.Android安卓:https://blog.csdn.net/VRunSoftYanlz/article/details/81843193
++++UnityAPI.AndroidJNI安卓JNI:https://blog.csdn.net/VRunSoftYanlz/article/details/81879345
++++UnityAPI.Transform变换:https://blog.csdn.net/VRunSoftYanlz/article/details/81916293
++++UnityAPI.WheelCollider轮碰撞器:https://blog.csdn.net/VRunSoftYanlz/article/details/82356217
++++UnityAPI.Resources资源:https://blog.csdn.net/VRunSoftYanlz/article/details/83155518
++++JSON数据结构:https://blog.csdn.net/VRunSoftYanlz/article/details/82026644
++++CocosStudio快速入门:https://blog.csdn.net/VRunSoftYanlz/article/details/82356839
++++Unity企业内训(目录):https://blog.csdn.net/VRunSoftYanlz/article/details/82634668
++++Unity企业内训(第1讲):https://blog.csdn.net/VRunSoftYanlz/article/details/82634733
++++Unity企业内训(第2讲):https://blog.csdn.net/VRunSoftYanlz/article/details/82861180
++++Unity企业内训(第3讲):https://blog.csdn.net/VRunSoftYanlz/article/details/82927699
++++Unity企业内训(第4讲):https://blog.csdn.net/VRunSoftYanlz/article/details/83479776
++++Unity企业内训(第5讲):https://blog.csdn.net/VRunSoftYanlz/article/details/83963811
++++Unity企业内训(第6讲):https://blog.csdn.net/VRunSoftYanlz/article/details/84207696
++++钻哥带您了解产品原型:https://blog.csdn.net/VRunSoftYanlz/article/details/87303828
++++插件
++++计算机组成原理(教材篇):https://blog.csdn.net/VRunSoftYanlz/article/details/82719129
++++5G接入:云计算和雾计算:https://blog.csdn.net/VRunSoftYanlz/article/details/88372718
++++云计算通俗讲义:https://blog.csdn.net/VRunSoftYanlz/article/details/88652803
++++立钻哥哥Unity 学习空间: http://blog.csdn.net/VRunSoftYanlz/
--_--VRunSoft:lovezuanzuan--_--