一個(gè)狀態(tài)機(jī)的實(shí)現(xiàn)
話不多說(shuō),先看代碼:
interface IState
{
string Name { get; set; }
//后件處理
IList<IState> Nexts { get; set; }
Func<IState /*this*/, IState /*next*/> Selector { get; set; }
}
class State : IState
{
public string Name { get; set; } = "State";
IList<IState> IState.Nexts { get; set; } = new List<IState>();
public Func<IState, IState> Selector { get; set; }
}
狀態(tài)比較簡(jiǎn)單,一個(gè)Name標(biāo)識(shí),一個(gè)后件狀態(tài)列表,然后一個(gè)狀態(tài)選擇器。
比如狀態(tài)a,可以轉(zhuǎn)移到狀態(tài)b,c,d,那么選擇器就是其中一個(gè)。至于怎么選,就讓用戶來(lái)定義實(shí)際的選擇器了。
delegate bool HandleType<T>(IState current, IState previous,ref T value);
interface IContext<T> : IEnumerator<T>, IEnumerable<T>
{
//data
T Value { get; set; }
//前件處理
IDictionary<Tuple<IState/*this*/, IState/*previous*/>, HandleType<T>> Handles { get; set; }
IState CurrentState { get; set; }
bool transition(IState next);
}
和狀態(tài)類State關(guān)注后件狀態(tài)不同,上下文類Context關(guān)注前件狀態(tài)。當(dāng)跳轉(zhuǎn)到一個(gè)新的狀態(tài),這個(gè)過(guò)程中就要根據(jù)當(dāng)前狀態(tài)來(lái)實(shí)施不同的策略。比如想進(jìn)入狀態(tài)c,根據(jù)當(dāng)前狀態(tài)是a, b,d 有不同的處理程序。這種轉(zhuǎn)移處理程序,是一一對(duì)應(yīng)的,所以用了 Tuple<進(jìn)入的狀態(tài),當(dāng)前狀態(tài)> 來(lái)描述一個(gè)跳轉(zhuǎn)鏈。然后用Dictionary 捆綁相關(guān)的處理程序。
上下文會(huì)攜帶 T Value 數(shù)據(jù),要怎么處理這種數(shù)據(jù)?我是通過(guò)ref 參數(shù)來(lái)傳遞給處理程序。因?yàn)槲也幌隝State 關(guān)心上下文的構(gòu)造,它只需要關(guān)注實(shí)際的數(shù)據(jù) T value;
上下文保存數(shù)據(jù)和當(dāng)前狀態(tài),然后通過(guò)transiton 讓用戶控制狀態(tài)的轉(zhuǎn)移。這里面有一個(gè)重復(fù),因?yàn)镮State有選擇器來(lái)控制狀態(tài)轉(zhuǎn)移了。為什么要這么處理?我是為了構(gòu)造一個(gè)跳轉(zhuǎn)序列。引入IEnumerator和IEnumerable接口,然狀態(tài)可以在選擇器的作用下自動(dòng)跳轉(zhuǎn),然后用foreach 讀取結(jié)果序列(只是不知道有什么用)。
class Context<T> : IContext<T>
{
T data;
T IContext<T>.Value { get=>data ; set=>data = value; }
IDictionary<Tuple<IState, IState>, HandleType<T>> IContext<T>.Handles { get; set; }
= new Dictionary<Tuple<IState, IState>, HandleType<T>>();
public IState CurrentState { get; set;}
T IEnumerator<T>.Current => (this as IContext<T>).Value ;
object IEnumerator.Current => (this as IContext<T>).Value;
bool IContext<T>.transition(IState next)
{
IContext<T> context= this as IContext<T>;
if (context.CurrentState == null || context.CurrentState.Nexts.Contains(next))
{
//前件處理
var key = Tuple.Create(next, context.CurrentState);
if (context.Handles.ContainsKey(key) && context.Handles[key] !=null)
if (!context.Handles[key](next, context.CurrentState,ref this.data))
return false;
context.CurrentState = next;
return true;
}
return false;
}
bool IEnumerator.MoveNext()
{
//后件處理
IContext<T> context = this as IContext<T>;
IState current = context.CurrentState;
if (current == null)
throw new Exception("必須設(shè)置初始狀態(tài)");
if (context.CurrentState.Selector != null)
{
IState next= context.CurrentState.Selector(context.CurrentState);
return context.transition(next);
}
return false;
}
void IEnumerator.Reset()
{
throw new NotImplementedException();
}
#region IDisposable Support
private bool disposedValue = false; // 要檢測(cè)冗余調(diào)用
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
// TODO: 釋放托管狀態(tài)(托管對(duì)象)。
}
// TODO: 釋放未托管的資源(未托管的對(duì)象)并在以下內(nèi)容中替代終結(jié)器。
// TODO: 將大型字段設(shè)置為 null。
disposedValue = true;
}
}
// TODO: 僅當(dāng)以上 Dispose(bool disposing) 擁有用于釋放未托管資源的代碼時(shí)才替代終結(jié)器。
// ~Context() {
// // 請(qǐng)勿更改此代碼。將清理代碼放入以上 Dispose(bool disposing) 中。
// Dispose(false);
// }
// 添加此代碼以正確實(shí)現(xiàn)可處置模式。
void IDisposable.Dispose()
{
// 請(qǐng)勿更改此代碼。將清理代碼放入以上 Dispose(bool disposing) 中。
Dispose(true);
// TODO: 如果在以上內(nèi)容中替代了終結(jié)器,則取消注釋以下行。
// GC.SuppressFinalize(this);
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return this;
}
IEnumerator IEnumerable.GetEnumerator()
{
return this;
}
#endregion
}
重點(diǎn)關(guān)注transition函數(shù)和MoveNext函數(shù)。
bool IContext<T>.transition(IState next)
{
IContext<T> context= this as IContext<T>;
if (context.CurrentState == null || context.CurrentState.Nexts.Contains(next))
{
//前件處理
var key = Tuple.Create(next, context.CurrentState);
if (context.Handles.ContainsKey(key) && context.Handles[key] !=null)
if (!context.Handles[key](next, context.CurrentState,ref this.data))
return false;
context.CurrentState = next;
return true;
}
return false;
}
做的事也很簡(jiǎn)單,就是調(diào)用前件處理程序,處理成功就轉(zhuǎn)移狀態(tài),否則退出。
bool IEnumerator.MoveNext()
{
//后件處理
IContext<T> context = this as IContext<T>;
IState current = context.CurrentState;
if (current == null)
throw new Exception("必須設(shè)置初始狀態(tài)");
if (context.CurrentState.Selector != null)
{
IState next= context.CurrentState.Selector(context.CurrentState);
return context.transition(next);
}
return false;
}
MoveNext通過(guò)選擇器來(lái)選擇下一個(gè)狀態(tài)。
總的來(lái)說(shuō),我這個(gè)狀態(tài)機(jī)的實(shí)現(xiàn)只是一個(gè)框架,沒(méi)有什么功能,但是我感覺(jué)是比較容易編寫(xiě)狀態(tài)轉(zhuǎn)移目錄樹(shù)的。
用戶首先要?jiǎng)?chuàng)建一組狀態(tài),然后建立目錄樹(shù)結(jié)構(gòu)。我的實(shí)現(xiàn)比較粗糙,因?yàn)橛脩粢謩e構(gòu)建目錄樹(shù),前件處理器,還有后件選擇器這三個(gè)部分。編寫(xiě)測(cè)試代碼的時(shí)候,我寫(xiě)了9個(gè)狀態(tài)的網(wǎng)狀結(jié)構(gòu),結(jié)果有點(diǎn)眼花繚亂。要是能統(tǒng)一起來(lái)估計(jì)會(huì)更好一些。
要關(guān)注的是第一個(gè)狀態(tài),和最后的狀態(tài)的構(gòu)造,否則無(wú)法停機(jī),嵌入死循環(huán)。
//測(cè)試代碼
//---------創(chuàng)建部分---------
string mess = "";//3
IState s3 = new State() { Name = "s3" };
//2
IState s2 = new State() { Name = "s2" };
//1
IState s1 = new State() { Name = "s1" };
//---------組合起來(lái)---------
s1.Nexts = new List<IState> { s2, s3 };
s2.Nexts = new List<IState> { s1, s3 };
s3.Nexts = new List<IState> { }; //注意end寫(xiě)法
//---------上下文---------
//transition
IContext<int> cont = new Context<int> { CurrentState=s1};//begin
cont.Value = 0;
//---------狀態(tài)處理器---------
HandleType<int> funcLaji = (IState current, IState previous, ref int v) => { mess += $"{current.Name}:垃圾{previous.Name}\n"; v++; return true; };
//1
cont.Handles.Add(Tuple.Create(s1 , default(IState)), funcLaji);
cont.Handles.Add(Tuple.Create(s1, s2), funcLaji);
//2
cont.Handles.Add(Tuple.Create(s2, s1), funcLaji);
//3
cont.Handles.Add(Tuple.Create(s3, s1), funcLaji);
cont.Handles.Add(Tuple.Create(s3, s2), funcLaji);
//---------狀態(tài)選擇器---------
var rval = new Random();
Func<int,int> round = x => rval.Next(x);
s1.Selector = st => round(2)==0? s2:s3;
s2.Selector = st => round(2)==0? s1:s3;
構(gòu)造完畢后,就可以使用這個(gè)狀態(tài)機(jī)了。
//選擇器跳轉(zhuǎn)
mess += "選擇器跳轉(zhuǎn):\n------------------------\n";
foreach (var stor in cont)
mess+=$"狀態(tài)轉(zhuǎn)變次數(shù):{stor}\n";
//直接控制跳轉(zhuǎn)
mess += "\n直接控制狀態(tài)跳轉(zhuǎn):\n------------------------\n";
cont.transition(s1);
cont.transition(s2);
cont.transition(s3);
以上就是本文的全部?jī)?nèi)容,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,同時(shí)也希望多多支持腳本之家!
相關(guān)文章
那些年,我還在學(xué)習(xí)C# 學(xué)習(xí)筆記續(xù)
那些年學(xué)習(xí)C#,就是對(duì)C#相關(guān)的一些知識(shí)有一個(gè)了解,等到要用時(shí)才不會(huì)找不到方向,比如說(shuō)擴(kuò)展方法,開(kāi)始時(shí)怎么覺(jué)得沒(méi)有用,后來(lái)了解到asp.net MVC,它可以用來(lái)擴(kuò)展Html類,比如做一個(gè)分頁(yè)的方法;所以對(duì)一門(mén)語(yǔ)言了解寬一些是沒(méi)有壞處的2012-03-03
C#后端接收f(shuō)orm-data,創(chuàng)建實(shí)體類教程
這篇文章主要介紹了C#后端接收f(shuō)orm-data,創(chuàng)建實(shí)體類教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06
Unity?UGUI的TouchInputModule觸摸輸入模塊組件介紹使用示例
這篇文章主要為大家介紹了Unity?UGUI的TouchInputModule觸摸輸入模塊組件介紹使用示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08
WinForm實(shí)現(xiàn)狀態(tài)欄跑馬燈效果的方法示例
這篇文章主要介紹了WinForm實(shí)現(xiàn)狀態(tài)欄跑馬燈效果的方法,涉及WinForm控件結(jié)合時(shí)間函數(shù)動(dòng)態(tài)操作元素屬性的相關(guān)技巧,需要的朋友可以參考下2017-07-07
.net實(shí)現(xiàn)裁剪網(wǎng)站上傳圖片的方法
這篇文章主要介紹了.net實(shí)現(xiàn)裁剪網(wǎng)站上傳圖片的方法,比較實(shí)用的功能,需要的朋友可以參考下2014-07-07
C#中的三種定時(shí)計(jì)時(shí)器Timer用法介紹
這篇文章介紹了C#中的三種定時(shí)計(jì)時(shí)器Timer的用法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03

