C#面向抽象编程第二讲

抽象编程怎么说呢,以观察者模式为例:

观察者模式有两个对象,一个是观察者,一个是可观察者(字面翻译很别扭observable),消息发布者(提供者)。

第一层如下,三个对象A、B、C分别有一个接收消息的方法,还有一个存储数据的字段,X就是发布消息的对象,它通过setdata方法设置自己的字段data,然后通知abc,abc如愿以偿地拿到了通知,完美!

internalclass A     {publicint Data;publicvoid Update(int data)         {this.Data = data;         }     }internalclass B     {publicint Count;publicvoid Notify(int data)         {this.Count = data;         }     }internalclass C     {publicint N;publicvoid Set(int data)         {this.N = data;         }     }internalclass X     {privateint data;public A instanceA;public B instanceB;public C instanceC;publicvoid SetData(int data)         {this.data = data;             instanceA.Update(data);             instanceB.Notify(data);             instanceC.Set(data);         }     }using ObserverOne;  A a=new A(); B b=new B(); C c=new C();  Console.WriteLine("订阅前................."); Console.WriteLine($"a.Data = {a.Data}"); Console.WriteLine($"b.Count = {b.Count}"); Console.WriteLine($"c.N = {c.N}");  X x=new X(); x.instanceA= a; x.instanceB= b; x.instanceC= c; x.SetData(10);  Console.WriteLine("X发布data=10, 订阅后................."); Console.WriteLine($"a.Data = {a.Data}"); Console.WriteLine($"b.Count = {b.Count}"); Console.WriteLine($"c.N = {c.N}");

再想一想,这好像不够灵活,订阅者是死的,那改进一下:

internalinterface IUpdatebleObject     {int Data {get; }void Update(int newData);     }internalclass A : IUpdatebleObject     {publicint Data => data;privateint data;publicvoid Update(int newData)         {this.data = newData;         }     }internalclass B : IUpdatebleObject     {publicint Data => data;privateint data;publicvoid Update(int newData)         {this.data = newData;         }     }internalclass C : IUpdatebleObject     {publicint Data => data;privateint data;publicvoid Update(int newData)         {this.data = newData;         }     }internalclass X     {private IUpdatebleObject[] updates=new IUpdatebleObject[3];public IUpdatebleObjectthis[int index]         {set { updates[index] = value; }         }privateint data;publicvoid Update(int newData)         {this.data = newData;foreach (var updatein updates)             {                 update.Update(newData);             }         }     }using ObserverTwo;  X x=new X();  IUpdatebleObject a=new A(); IUpdatebleObject b=new B(); IUpdatebleObject c=new C(); Console.WriteLine("订阅前................."); Console.WriteLine($"a.Data = {a.Data}"); Console.WriteLine($"b.Data = {b.Data}"); Console.WriteLine($"c.Data = {c.Data}"); x[0] = a; x[1] = b; x[2] = c; x.Update(10); Console.WriteLine("X发布data=10, 订阅后................."); Console.WriteLine($"a.Data = {a.Data}"); Console.WriteLine($"b.Data = {b.Data}"); Console.WriteLine($"c.Data = {c.Data}");

虽然写到这个例子已经很了不起了,但是对于有想法的来说还是可以继续改进,要不然怎么常挂嘴边说面对抽象编程呢,那就继续改进了:

///<summary>/// 观察者///</summary>///<typeparam name="T"></typeparam>internalinterface IObserver<T>     {void Update(SubjectBase<T> subject);     }
///<summary>/// 可观察者(发出通知的对象)///</summary>///<typeparam name="T"></typeparam>internalabstractclass SubjectBase<T>     {protected IList<IObserver<T>> observers =new List<IObserver<T>>();protected T state;publicvirtual T State => state;publicstatic SubjectBase<T>operator +(SubjectBase<T> subject,IObserver<T> observer)         {             subject.observers.Add(observer);return subject;         }publicstatic SubjectBase<T>operator -(SubjectBase<T> subject,IObserver<T> observer)         {             subject.observers.Remove(observer);return subject;         }publicvirtualvoid Notify()         {foreach (var observerin observers)             {                 observer.Update(this);             }         }publicvirtualvoid Update(T state)         {this.state = state;             Notify();         }     }
internalclass Observer<T> : IObserver<T>     {public T State;publicvoid Update(SubjectBase<T> subject)         {this.State = subject.State;         }     }
internalclass Subject<T>:SubjectBase<T>     {     }

到这里基本上可以说是把骨架搭起来了,这些可以称之为底层的代码。实现代码如下:

internalclass TestObserver     {publicvoid TestMulticst()         {             SubjectBase<int> subject =new Subject<int>();             Observer<int> observer1 =new Observer<int>();             observer1.State=10;             Observer<int> observer2 =new Observer<int>();             observer2.State=20;             subject+= observer1;             subject+= observer2;             subject.Update(1);             Console.WriteLine($"observer1.State={observer1.State}  observer2.State={observer2.State}");             subject-= observer1;             subject.Update(100);             Console.WriteLine($"update state = 100, observer1.State={observer1.State}  observer2.State={observer2.State}");         }publicvoid TestMultiSubject()         {             SubjectBase<string> subject1 =new Subject<string>();             SubjectBase<string> subject2 =new Subject<string>();             Observer<string> observer1 =new Observer<string>();             observer1.State="运动";             Console.WriteLine($"observer1.State={observer1.State}");             subject1+= observer1;             subject2+= observer1;             subject1.Update("看电影");             Console.WriteLine($"observer1.State={observer1.State}");             subject2.Update("喝茶");             Console.WriteLine($"observer1.State={observer1.State}");              subject1-= observer1;             subject2-= observer1;             observer1.State="休息";             subject1-= observer1;             subject2-= observer1;             Console.WriteLine($"observer1.State={observer1.State}");         }     }
using ObserverThree;//new TestObserver().TestMulticst();new TestObserver().TestMultiSubject();

到这里基本上就完成了任务,也就可以结束了。但是,学习需要深度也需要宽度,所以观察者模式在C#可以通过事件来实现一样的效果。下面就看下上面写这么多的代码用事件怎么写呢,这里的实例稍作变化,实现改变名字通知观察者,这里观察者就是控制台了,打印通知:

internalclass UserEventArgs:EventArgs     {privatestring name;publicstring Name => name;public UserEventArgs(string name)         {this.name = name;         }      }
internalclass User     {publicevent EventHandler<UserEventArgs> NameChanged;privatestring name;publicstring Name         {get {return name; }set             {                 name= value;                 NameChanged?.Invoke(this,new UserEventArgs(value));             }         }     }
using ObserverFour;  User user=new User(); user.NameChanged+= OnNameChanged; user.Name="joe";void OnNameChanged(object sender, UserEventArgs args) {     Console.WriteLine($"{args.Name} Changed"); }

再放一个麻烦一点的例子,字典新增的通知(监听)事件:

internalclass DictionaryEventArgs<TKey,TValue> : EventArgs     {private TKey key;private TValue value;public DictionaryEventArgs(TKey key,TValue value)         {this.key = key;this.value = value;         }public TKey Key => key;public TValue Value => value;     }
internalinterface IObserverableDictionary<TKey,TValue>:IDictionary<TKey, TValue>     {         EventHandler<DictionaryEventArgs<TKey,TValue>> NewItemAdded {get;set; }     }
internalclass ObserverableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IObserverableDictionary<TKey, TValue>     {protected EventHandler<DictionaryEventArgs<TKey, TValue>> newItemAdded;public EventHandler<DictionaryEventArgs<TKey, TValue>> NewItemAdded {get => newItemAdded;set=> newItemAdded = value;}publicnewvoid Add(TKey key,TValue value)         {base.Add(key, value);if(NewItemAdded !=null)                 NewItemAdded(this,new DictionaryEventArgs<TKey, TValue>(key, value));           }     }
using ObserverFive;string key ="hello";string value ="world";  IObserverableDictionary<string,string> dictionary =new ObserverableDictionary<string,string>(); dictionary.NewItemAdded+= Validate; dictionary.Add(key, value);void Validate(object sender, DictionaryEventArgs<string,string> args) {     Console.WriteLine($"{args.Key} {args.Value}"); }

事件说完了!再回头看看观察者设计模式。

微软已经很重视观察者模式这个设计,把IObserver、IObservable集成到runtime里面去了,也就是基类库里面。aspnetcore框架也有用到这个,比如日志模块。所以感觉有必要了解一下,放个小例子作为结束:

internalclass Message     {publicstring Notify {get;set; }     }
internalclass Teacher : IObservable<Message>     {privatereadonly List<IObserver<Message>> _observers;public Teacher()         {             _observers=new List<IObserver<Message>>();         }public IDisposable Subscribe(IObserver<Message> observer)         {             _observers.Add(observer);returnnew Unsubscribe(observer, _observers);         }publicvoid SendMessage(string message)         {foreach (var observerin _observers)             {                 observer.OnNext(new Message() { Notify ="message" });             }         }publicvoid OnCompleted()         {foreach (var observerin _observers)             {                 observer.OnCompleted();             }             _observers.Clear();         }     }internalclass Unsubscribe:IDisposable     {privatereadonly IObserver<Message> _observer;privatereadonly List<IObserver<Message>> _observers;public Unsubscribe(IObserver<Message> observer, List<IObserver<Message>> observers)         {this._observers = observers;this._observer = observer;         }publicvoid Dispose()         {if(_observers.Contains(_observer))                 _observers.Remove(_observer);         }     }
internalabstractclass Student : IObserver<Message>     {privatestring name;public Student(string name)         {this.name = name;         }private IDisposable _unsubscribe;publicvirtualvoid OnCompleted()         {             Console.WriteLine("放学了...");         }publicvirtualvoid OnError(Exception error)         {             Console.WriteLine("生病了...");         }publicvirtualvoid OnNext(Message value)         {             Console.WriteLine($"大家好: 我是 {name} -_-");             Console.WriteLine($"老师说:{value.Notify}");         }publicvirtualvoid Subscribe(IObservable<Message> obserable)         {if (obserable !=null)                 _unsubscribe= obserable.Subscribe(this);         }     }
internalclass StudentZhang : Student     {public StudentZhang(string name) :base(name)         {         }     }internalclass StudentLi : Student     {public StudentLi(string name) :base(name)         {         }     }
using ObserverSeven;  Teacher teacher=new Teacher(); teacher.Subscribe(new StudentLi("李逵")); teacher.Subscribe(new StudentZhang("张麻子")); teacher.SendMessage("明天放假"); teacher.OnCompleted();//这里学生是多个,也定义可以多个老师,实现多对多关系

示例代码:

exercise/发布订阅And出版预定_EventBus_Observer/Observer/Observer at master · liuzhixin405/exercise (github.com)