UniRx---簡單封裝一個SubjectManager 解耦主題和監聽者
阿新 • • 發佈:2019-01-12
Subject<T> 是UniRx 推薦的 事件實現方式 ( UniRx是AssetStore上的一款免費外掛,但其實他的出身很地道,脫胎於微軟的Rx框架,主要思想是像Linq那樣對訂閱的事件進行操作,並且他還能對操作進行 時間排程,執行緒排程,而UniRx就是Rx的Unity版) 。在開發中,這種資料發射工作(在使用UniRx時,可以把事件觸發時呼叫觀察者回調並傳遞引數的行為 形象的理解成發射,這樣有助於理解萬物皆Stream的這種程式設計思想),一般會讓訂閱方和發射方解耦,彼此不用瞭解對方的存在,SubjectManager的意義就在於此。 此係統中,用繼承於SubjectArgs基類的一個一個子類來表示不同的事件,同時類本身作為事件傳遞的引數型別,你可以在裡面隨便定義任何你需要在這種事件裡傳遞的資料
下面是實現
//主題引數基類 public abstract class SubjectArgs { public object sender = null; public abstract int SubjectId { get; } } //主題管理器 public class Subject_Manager { //主題字典,以主題傳遞的引數型別提供的hashCode為key private Dictionary<int, Subject<SubjectArgs>> mSubjectDic = new Dictionary<int, Subject<SubjectArgs>>(); //通過id拿到主題 public IObservable<T> GetSubject<T>()where T : SubjectArgs { int subjectId = typeof(T).GetHashCode(); Subject<SubjectArgs> subject = null; if (!mSubjectDic.TryGetValue(subjectId, out subject)) { subject = new Subject<SubjectArgs>(); mSubjectDic.Add(subjectId, subject); } return subject.Select(_ => _ as T); } //資料,發射! public void Fire<T>(T e)where T:SubjectArgs { Subject<SubjectArgs> subject = null; if (!mSubjectDic.TryGetValue(e.SubjectId, out subject)) return; subject.OnNext(e); } }
加一層靜態封裝,用起來稍微舒服點
public static class SubjectManager { private static Subject_Manager mInstance = new Subject_Manager(); public static IObservable<T> GetSubject<T>() where T : SubjectArgs { return mInstance.GetSubject<T>(); } public static void Fire<T>(T e) where T : SubjectArgs { mInstance.Fire<T>(e); } }
然後進行測試
public class HappySubjectArgs : SubjectArgs //Todo用物件池快取這些炮彈...
{
public static readonly int ID = typeof(HappySubjectArgs).GetHashCode();
public override int SubjectId { get { return ID; } }
public int HappyDegree = 0;
}
public class SMTest : MonoBehaviour {
//登出器集合,所有訂閱的登出器都往這裡塞
CompositeDisposable mSubjectUnRegister=new CompositeDisposable();
void Start () {
//只接收開心程度大於10的發射...
SubjectManager.GetSubject<HappySubjectArgs>().Where(e => e.HappyDegree > 10)
.Subscribe(e => print("開心程度:"+e.HappyDegree+"...看起來很開心")).AddTo(mSubjectUnRegister);
//開心程度會隨著點選增長...
Observable.EveryUpdate().Where(_ => Input.GetMouseButtonDown(0))
.Select((_,count)=>count)
.Subscribe(count => SubjectManager.Fire(new HappySubjectArgs() { HappyDegree = count}));
//沒有多餘欄位的類...看起來真的很舒爽...
}
void OnDestroy()
{
//此物件銷燬時登出器登出 就算重複登出也沒關係 這就是UniRx用起來舒服的點點滴滴...
mSubjectUnRegister.Dispose();
}
}
這樣你可以在任何地方發射 在任何地方訂閱
同時可以考慮增加一種輕量級的訊息發射方式,使用string作為訊息的key,object作為傳遞的引數(value是subject<object>)
這樣不用每種訊息都要去實現一個類,但是缺點是如果引數是struct的話,就要裝箱和拆箱
總之,UniRx版的事件subject 的優點主要在於 可以對發射來的資料進行各種定製操作, 無縫對接UniRx所有的操作符,不多,也就七八十個把(滑稽臉),隨便舉個例子好把,比如First,只要在訂閱前新增這個操作符就能優雅的實現只訂閱一次的功能