2015-04-03 83 views
1

在此應用程序中,觀察者處理對象網絡中的狀態更改。所有對象都是基於相同BaseObject類的派生類。 BaseObject提供重要的識別和導航功能。C#類型特定觀察者

從BaseObject派生的類由代碼生成器創建。這些類應具有最小的佔用空間,並專注於特定的狀態和行爲。

在應用程序級別,觀察者處理BaseObject派生類中的狀態更改,通常對於多個類而言,並且通常對於大量BaseObject派生實例。

在當前的解決方案中,BaseObject管理觀察者並以BaseObject實例作爲發件人通知觀察者。

using System; 
using System.Collections.Generic; 

namespace Observer { 
    #region underlying framework foundation 
    interface IObserver { 
    void ObjectChanged (BaseObject obj); 
    } 

    abstract class BaseObject { 
    HashSet<IObserver> observers = new HashSet<IObserver>(); 

    public void RegisterObserver (IObserver observer) { 
     observers.Add (observer); 
    } 

    public void FireObjectChanged() { 
     foreach (var obs in observers) 
     obs.ObjectChanged (this); 
    } 
    } 
    #endregion underlying framework foundation 

    #region code generator 
    class DerivedObject1 : BaseObject { 
    } 

    class DerivedObject2 : BaseObject { 
    } 
    #endregion code generator 

    #region application code 
    class Observer : IObserver { 

    public void ObjectChanged (BaseObject obj) { 
     Console.WriteLine (obj.GetType().Name); 

     if (obj is DerivedObject1) { 

     } else if (obj is DerivedObject2) { 

     } 
    } 
    } 
    #endregion application code 

    #region sample 
    class Program { 
    static void Main (string[] args) { 
     Observer observer = new Observer(); 
     List<BaseObject> objects = new List<BaseObject>(); 

     DerivedObject1 obj1 = new DerivedObject1(); 
     objects.Add (obj1); 
     obj1.RegisterObserver (observer); 

     DerivedObject2 obj2 = new DerivedObject2(); 
     objects.Add (obj2); 
     obj2.RegisterObserver (observer); 

     foreach (var bo in objects) 
     bo.FireObjectChanged(); 
    } 
    } 
    #endregion sample 
} 

我不喜歡那種方法是觀察者必須在運行時識別發送者類型。相反,我想在編譯時保證安全。

所以我提出了一種新的方法,它引入了泛型和第二個BaseObject層,它包含類型安全的觀察者。

using System; 
using System.Collections.Generic; 

namespace Observer { 
    #region underlying framework foundation 
    interface IObserver<T> where T : BaseObjectT<T> { 
     void ObjectChanged (T obj); 
    } 

    abstract class BaseObject { 
     public abstract void FireObjectChanged(); 
    } 

    abstract class BaseObjectT<T> : BaseObject where T : BaseObjectT<T> { 
     HashSet<IObserver<T>> observers = new HashSet<IObserver<T>>(); 

     public void RegisterObserver (IObserver<T> observer) { 
      observers.Add (observer); 
     } 

     public override void FireObjectChanged() { 
      foreach (var obs in observers) 
       obs.ObjectChanged ((T)this); 
     } 
    } 
    #endregion underlying framework foundation 

    #region code generator 
    class DerivedObject1 : BaseObjectT<DerivedObject1> { 
    } 

    class DerivedObject2 : BaseObjectT<DerivedObject2> { 
    } 
    #endregion code generator 

    #region application code 
    class Observer : 
     IObserver<DerivedObject1>, 
     IObserver<DerivedObject2> { 

     public void ObjectChanged (DerivedObject1 obj) { 
      Console.WriteLine (obj.GetType().Name); 
     } 

     public void ObjectChanged (DerivedObject2 obj) { 
      Console.WriteLine (obj.GetType().Name); 
     } 
    } 
    #endregion application code 

    #region sample 
    class Program { 
     static void Main (string[] args) { 
      Observer observer = new Observer(); 
      List<BaseObject> objects = new List<BaseObject>(); 

      DerivedObject1 obj1 = new DerivedObject1(); 
      objects.Add (obj1); 
      obj1.RegisterObserver (observer); 

      DerivedObject2 obj2 = new DerivedObject2(); 
      objects.Add (obj2); 
      obj2.RegisterObserver (observer); 

      foreach (var bo in objects) 
       bo.FireObjectChanged(); 
     } 
    } 
    #endregion sample 
} 

雖然這種方法,準確地我想要做什麼在應用層面 - 通過派生類的實例,以觀察者無需類型強制轉換那裏,不同IObserver方法實現的,允許超載 - 在底層我看起來有點難看。

我現在的問題,是有沒有更好的,來完成這個更優雅的方式,特別是有沒有辦法避免FireObjectChanged)演員(

obs.ObjectChanged ((T)this);  

,或者BaseObject和BaseObjectT結合成單個基類?

+0

你不應該命名的東西'IObserver '除非,以及['IObserver '](https://msdn.microsoft.com/en-us/library/dd783449(V = VS。 110).aspx) – Falanwe 2015-04-03 10:31:55

回答

0

作爲@Falanwe評論,IObserver<T>是一個系統類,你應該呼籲你的觀察員別的東西。我選擇了ICustomObserver<T>作爲下面的示例代碼。

我想出的唯一選擇是這個。我們創建一個擴展方法來將代表存儲在一個有條件的WeakTable中。這些代表(Action<BaseObject>)可以在每個對象的基礎上從另一個擴展方法內調用ObjectChanged。

這種方法的優點是,您不需要任何花費在您的BaseObject中,並且可以以類型安全的方式爲BaseObject或DerivedObject1調用RegisterObserver。

缺點是,如果您尚不瞭解擴展方法,委託或條件關係表,那麼可能存在學習曲線。

public static class ObjectChangedExtension 
{ 
    internal static ConditionalWeakTable<object, List<Action<BaseObject>>> observers 
     = new ConditionalWeakTable<object, List<Action<BaseObject>>>(); 

    public static void RegisterObserver<T>(this T obj, ICustomObserver<T> observer) 
     where T : BaseObject 
    { 
     Action<BaseObject> objChangedDelegate = v => observer.ObjectChanged((T)v); 

     observers 
      .GetOrCreateValue(obj) 
      .Add(objChangedDelegate); 
    } 

    public static void FireObjectChanged(this BaseObject obj) 
    { 
     observers 
      .GetOrCreateValue(obj) 
      .ForEach(v => v(obj)); 
    } 
} 

#region code generator 
class DerivedObject1 : BaseObject 
{ 
} 

class DerivedObject2 : BaseObject 
{ 
} 
#endregion code generator 

#region application code 
class Observer : ICustomObserver<DerivedObject1>, ICustomObserver<DerivedObject2> 
{ 
    public void ObjectChanged(DerivedObject1 obj) 
    { 
     Console.WriteLine("DerivedObject1 Observer"); 
    } 

    public void ObjectChanged(DerivedObject2 obj) 
    { 
     Console.WriteLine("DerivedObject2 Observer"); 
    } 
} 

class ObserverOfBase : ICustomObserver<BaseObject> 
{ 
    public void ObjectChanged(BaseObject obj) 
    { 
     Console.WriteLine("BaseObject Observer"); 
    } 
} 
#endregion application code 

#region sample 
class Program 
{ 
    internal static void Main(string[] args) 
    { 
     Observer observer = new Observer(); 
     List<BaseObject> objects = new List<BaseObject>(); 

     DerivedObject1 obj1 = new DerivedObject1(); 
     objects.Add(obj1); 
     obj1.RegisterObserver(observer); 

     DerivedObject2 obj2 = new DerivedObject2(); 
     objects.Add(obj2); 
     obj2.RegisterObserver(observer); 

     var baseObjectObserver = new ObserverOfBase(); 
     obj1.RegisterObserver(baseObjectObserver); 
     obj2.RegisterObserver(baseObjectObserver); 

     foreach (var bo in objects) 
      bo.FireObjectChanged(); 
    } 
} 
#endregion sample 

public class BaseObject 
{ 
} 

public interface ICustomObserver<T> 
{ 
    void ObjectChanged(T obj); 
} 
+0

有問題的框架最初是在C#2.0下實現的,並且當時嚴格地基於接口(當時IObserver不是系統類型)。與此同時,有不少代表機制滲透進來,擴展方法在這裏和那裏用來增加功能。 – geophil 2015-04-04 10:06:27

+0

我已經針對您的建議針對10,000個對象和10,000個觸發器執行了性能測試。它實際上比我的多態呼叫快一點。但是我可能會將'ConditionalWeakTable'替換爲傳統'Dictionary'作爲'BaseObject'或'Observer'生命週期在這裏無關緊要,性能測試通過經典字典給出了更好的結果。非常感謝您的幫助。 – geophil 2015-04-04 10:13:46

+0

此外,擴展方法的優點是代碼生成器可以保持不變,並且唯一且不會中斷的更改必須在'BaseObject'中進行,以包含新的類型特定的觀察器。這將有助於順利遷移。 – geophil 2015-04-04 10:21:28