抽象编程怎么说呢,以观察者模式为例:
观察者模式有两个对象,一个是观察者,一个是可观察者(字面翻译很别扭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();//这里学生是多个,也定义可以多个老师,实现多对多关系
示例代码:
下一个:拓扑排序小结_在线工具