1. 程式人生 > >UniRx---簡單封裝一個SubjectManager 解耦主題和監聽者

UniRx---簡單封裝一個SubjectManager 解耦主題和監聽者

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,只要在訂閱前新增這個操作符就能優雅的實現只訂閱一次的功能