状态抽象类IState
namespace StateNameSpace { public abstract class IState { public StateContext mContext; public IState(StateContext context) { mContext = context; } public abstract void Handle(object data); } }
环境类Context
using System; namespace StateNameSpace { public class StateContext { private IState currentState; private Dictionary<string, IState> stateDic = new Dictionary<string, IState>(); public StateContext(TextAsset textAsset,string defaultState) { //解析配置,初始化状态 string txt = textAsset.text; string[] lines = txt.Split('\n'); Assembly assembly = Assembly.Load("Assembly-CSharp"); object[] par = new object[1]; par[0] = this; foreach (string str in lines) { string[] keyVal = str.Split(','); try { //获取实例,CreateInstance表示使用构造方法,par是参数,其他未知 IState state = (IState)assembly.CreateInstance("StateNameSpace." + keyVal[1].Trim(), false, System.Reflection.BindingFlags.CreateInstance, null, par, null, null); if (state == null) { throw new Exception("你的类名可能写错啦"); } this.RegistState(keyVal[0], state); } catch (Exception e) { Debug.LogError(e.Message); } } //设置默认状态 SetState(defaultState); } public void Handle(object data) { if (currentState != null) { currentState.Handle(data); } } public void SetState(string key) { if (!isExistState(key)) return; IState state; stateDic.TryGetValue(key,out state); currentState = state; } public void RegistState(string key,IState state) { if (isExistState(key)) return; stateDic.Add(key, state); } public void RemoveState(string key) { if (!isExistState(key)) return; } public bool isExistState(string key) { IState state; stateDic.TryGetValue(key, out state); if (state == null) { return false; } return true; } } }
创建环境类,需要提供一个配置文件,告诉key对应的状态类,自动初始化和实例化具体的状态类(利用反射),保存在字典中,根据key值来获取切换状态。(状态模式使用示例)
就是要对象之间要减少耦合,知道依赖的东西越少越好。低耦合,高内聚。尽量的减少对象对依赖的数量和质量,这里说的质量其实是依赖方public出来的东西。
例如,我有一个功能需要3个类来协调完成,所以我依赖上了这3个类,当功能改变时,有可能就会牵动这3个类的改变。如果我增加一个中间类,依赖上3个类,通过这个中间类来提供一个接口来实现功能,调用类也不知道里面实现了什么,此时如果功能改变,我就可以新增加一个中间类来去实现方法,然后替换调原来的,来改变原来的实现。
就是通过抽象和实现的方式来作为某个整体的部分组件,而不是依靠继承来获得某个功能,减少类和类的耦合。
比如说,鸟类都会飞,如果做出一个父类有一个飞的方法,具体鸟类去继承获得这个飞的方法,此时具体鸟类和父类就耦合在一起了,当具体鸟类的飞行方法改变,此时就需要重写父类方法或则修改父类方法,这就不符合里氏替换原则和开闭原则了。
如果通过抽象一个飞行类,让鸟类依赖这个抽象类,我们通过将实现飞行类以注入或则其他方式提供给鸟类,鸟类就有这个功能了。当飞行方法改变,就可以修改注入的飞行实现类就可以了。
接口隔离原则其实和单一原则那样,只有保证了接口隔离原则才能从根源上就保证实现类的单一职责。如果接口的方法很杂乱,一个实现类要去实现它,本来这个类实际只需要一个方法,但是这个接口是很杂乱的,有其他类型的接口方法,这个实现类还要去实现这个不需要的方法,所以,这些接口应该要细化,实现类只挑需要的接口来实现。
就是一个类,它所负责的东西要单一,要同一类型,同一种.....反正这单一的界限个人来讲还是有差异的,从而达到只有一个原因来引起这个类的改动。
例如一个加法类:
class Add : ICal{ public int Cal(int a, int b) { return a + b; } }
里面就只有一个加法运算的方法,唯一能引起这个类改动的就是加法不是这么算了,世界改变,加法用的运算符是:- ,这时候就要修改+号为-了。
就是子类不应该重写可以实例化的父类的方法,然后通过扩展子类的方法来实现新的功能,再替换掉父类,这样就可以保证原有功能的不变,同时也赋予父类(被替换掉的子类)拥有了新的功能。
符合父类可以被子类替换,且程序的逻辑不变,这就是里氏替换原则了。
如果父类是一个抽象接口,不可以实现的,里面没有逻辑性的东西,然后子类就不用担心重写(也不叫重写,叫实现了)父类方法后而改变程序逻辑了,且实现(子)类都可以替换父类,这也是符合里氏替换原则的。
就是高层模块不应该依赖于底层模块。所谓的高层模块就是调用一些类来实现某种功能的,低层模块就是那些被高层模块调用的。高层模块又要使用低层模块实现功能又不能依赖与低层模块,所以要使用接口来替代这个低层模块,从而依赖接口。
高层模块依赖于接口之后,就只需要针对接口的调用了,接口的引用的具体实现就交由配置或则第三方管理,从而方便扩展和修改。
如果高层模块依赖于低层模块,突然需求有变,不适用这个低层模块了,从而修改增加一个新的低层模块,但是高层模块用的是以前的低层模块,那么这些高层模块的代码都需要更改!如果有很多高层模块引用了这个低层模块,那么就是动一处而动全身了。
就是对扩展开放,对修改关闭。当应用需求改变时,不应该就修改原来的代码,而应该通过增加扩展的方式来完成对需求的改变。
对应的代码技术就是接口类型的,通过增加类来实现接口完成不同需求的实现。
例如:一个做一个运算类
//运算方法接口 interface ICal { int Cal(int a, int b); }
//运算加法类 class Add : ICal { public int Cal(int a, int b) { return a + b; } }
//运算类 class Cal { //默认使用加法 private ICal calCode = new Add(); //用来设置使用的运算方式 public void SetCalCode(ICal code) { calCode = code; } public int DoCal(int a, int b) { return calCode.Cal(a, b); } }
这里默认使用的是加法,如果此时需要使用的是乘法,此时不应该对原来代码进行修改,而是进行扩展
//新加乘法运算类 class Mul : ICal { public int Cal(int a, int b) { return a * b; } }
客户端(通过某种方式,配置,注入等)切换运算类
class Program { static void Main(string[] args) { Cal cal = new Cal(); int r1 = cal.DoCal(10, 20); Console.WriteLine(r1);//30 //这里只是示范,应该在第三方地方设置的,然后客户端类就不需要做任何改动了 cal.SetCalCode(new Mul()); int r2 = cal.DoCal(10, 20); Console.WriteLine(r2);//200 Console.ReadKey(); } }