状态抽象类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();
}
}