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

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

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

internal class A
{
public int Data;
public void Update(int data)
{
this.Data = data;
}
} internal class B
{
public int Count;
public void Notify(int data)
{
this.Count = data;
}
} internal class C
{
public int N;
public void Set(int data)
{
this.N = data;
}
} internal class X
{
private int data;
public A instanceA;
public B instanceB;
public C instanceC;
public void 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}");

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

internal interface IUpdatebleObject
{
int Data { get; }
void Update(int newData);
} internal class A : IUpdatebleObject
{
public int Data => data;
private int data; public void Update(int newData)
{
this.data = newData;
}
} internal class B : IUpdatebleObject
{
public int Data => data;
private int data;
public void Update(int newData)
{
this.data = newData;
}
} internal class C : IUpdatebleObject
{
public int Data => data;
private int data;
public void Update(int newData)
{
this.data = newData;
}
} internal class X
{
private IUpdatebleObject[] updates=new IUpdatebleObject[3]; public IUpdatebleObject this[int index]
{
set { updates[index] = value; }
}
private int data;
public void Update(int newData)
{
this.data = newData;
foreach (var update in 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>
internal interface IObserver<T>
{
void Update(SubjectBase<T> subject);
}
/// <summary>
/// 可观察者(发出通知的对象)
/// </summary>
/// <typeparam name="T"></typeparam>
internal abstract class SubjectBase<T>
{
protected IList<IObserver<T>> observers = new List<IObserver<T>>();
protected T state;
public virtual T State => state; public static SubjectBase<T> operator +(SubjectBase<T> subject,IObserver<T> observer)
{
subject.observers.Add(observer);
return subject;
}
public static SubjectBase<T> operator -(SubjectBase<T> subject,IObserver<T> observer)
{
subject.observers.Remove(observer);
return subject;
} public virtual void Notify()
{
foreach (var observer in observers)
{
observer.Update(this);
}
} public virtual void Update(T state)
{
this.state = state;
Notify();
}
}
 internal class Observer<T> : IObserver<T>
{
public T State; public void Update(SubjectBase<T> subject)
{
this.State = subject.State;
}
}
 internal class Subject<T>:SubjectBase<T>
{
}

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

internal class TestObserver
{
public void 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}");
} public void 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#可以通过事件来实现一样的效果。下面就看下上面写这么多的代码用事件怎么写呢,这里的实例稍作变化,实现改变名字通知观察者,这里观察者就是控制台了,打印通知:

  internal class UserEventArgs:EventArgs
{
private string name;
public string Name => name; public UserEventArgs(string name)
{
this.name = name;
} }
 internal class User
{
public event EventHandler<UserEventArgs> NameChanged;
private string name;
public string 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 ");
}

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

 internal class 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;
}
 internal interface IObserverableDictionary<TKey,TValue>:IDictionary<TKey, TValue>
{
EventHandler<DictionaryEventArgs<TKey,TValue>> NewItemAdded { get; set; }
}
 internal class 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;}
public new void 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框架也有用到这个,比如日志模块。所以感觉有必要了解一下,放个小例子作为结束:

  internal class Message
{
public string Notify { get; set; }
}
internal class Teacher : IObservable<Message>
{
private readonly List<IObserver<Message>> _observers;
public Teacher()
{
_observers = new List<IObserver<Message>>();
}
public IDisposable Subscribe(IObserver<Message> observer)
{
_observers.Add(observer);
return new Unsubscribe(observer, _observers);
} public void SendMessage(string message)
{
foreach (var observer in _observers)
{
observer.OnNext(new Message() { Notify = "message" });
}
}
public void OnCompleted()
{
foreach (var observer in _observers)
{
observer.OnCompleted();
}
_observers.Clear();
}
} internal class Unsubscribe:IDisposable
{
private readonly IObserver<Message> _observer;
private readonly List<IObserver<Message>> _observers;
public Unsubscribe(IObserver<Message> observer, List<IObserver<Message>> observers)
{
this._observers = observers;
this._observer = observer;
} public void Dispose()
{
if(_observers.Contains(_observer))
_observers.Remove(_observer);
}
}
 internal abstract class Student : IObserver<Message>
{
private
string name;
public Student(string name)
{
this.name = name;
}
private IDisposable _unsubscribe;
public virtual void OnCompleted()
{
Console.WriteLine("放学了...");
} public virtual void OnError(Exception error)
{
Console.WriteLine("生病了...");
} public virtual void OnNext(Message value)
{
Console.WriteLine($"大家好: 我是 {name} -_- ");
Console.WriteLine($"老师说:{value.Notify}");
} public virtual void Subscribe(IObservable<Message> obserable)
{
if (obserable != null)
_unsubscribe = obserable.Subscribe(this);
}
}
 internal class StudentZhang : Student
{
public StudentZhang(string name) : base(name)
{
}
} internal class 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)

C#面向抽象编程第二讲的更多相关文章

  1. 大型 web 前端架构设计-面向抽象编程入门

    https://mp.weixin.qq.com/s/GG6AtBz6KgNwplpaNXfggQ 大型 web 前端架构设计-面向抽象编程入门 曾探 腾讯技术工程 2021-01-04   依赖反转 ...

  2. C#面对抽象编程第一讲

    闲话不多说,面向对象编程是高级语言的一个特点,但是把它概括成面向抽象更容易直击灵魂,经过了菜鸟大家都要面对的是不要写这么菜的代码了. 上例子,这应该是大家都很熟悉耳熟能详的代码, so easy. 1 ...

  3. 2. Shell编程第二讲

    (1) 条件测试: test   [ 命令 test 或 [ 可以测试一个条件是否成立,如果测试结果为真,则该命令的Exit Status为0,如果测试结果为假,则命令的Exit Status为1(注 ...

  4. java 面向抽象编程的思想

    SIM.java public abstract class SIM { public abstract void setNumber(String n); public abstract Strin ...

  5. java面向抽象编程样例

    import java.util.*; abstract class Geometry{    public abstract double getArea();    }  class Pillar ...

  6. 你必须知道的.net读书笔记之第二回深入浅出关键字---对抽象编程:接口和抽象类

    请记住,面向对象思想的一个最重要的原则就是:面向接口编程. 借助接口和抽象类,23个设计模式中的很多思想被巧妙的实现了,我认为其精髓简单说来就是:面向抽象编程. 抽象类应主要用于关系密切的对象,而接口 ...

  7. java--面向抽象编程

    所谓面向抽象编程是指当设计某种重要的类时,不让该类面向具体的类,而是面向抽象类,及所设计类中的重要数据是抽象类声明的对象,而不是具体类声明的对象.就是利用abstract来设计实现用户需求. 比如:我 ...

  8. 快速高效掌握企业级项目中的Spring面向切面编程应用,外带讲面试技巧

    Spring面向切面编程(AOP)是企业级应用的基石,可以这样说,如果大家要升级到高级程序员,这部分的知识必不可少. 这里我们将结合一些具体的案例来讲述这部分的知识,并且还将给出AOP部分的一些常见面 ...

  9. 重新学习之spring第二个程序,配置AOP面向切面编程

    第一步:在配置好的ioc容器的基础上,导入面向切面编程所需要的jar包 (本案例用的是spring3.2.4,由于spring3.2.4的官网jar包中不再有依赖包,所以依赖包都是从网上找的) 第二步 ...

随机推荐

  1. ElasticSearch7.3 学习之定制动态映射(dynamic mapping)

    1.dynamic mapping ElasticSearch中有一个非常重要的特性--动态映射,即索引文档前不需要创建索引.类型等信息,在索引的同时会自动完成索引.类型.映射的创建. 当ES在文档中 ...

  2. 《前端运维》二、Nginx--4代理、负载均衡与其他

    一.代理服务 比较容易理解吧,简单来说.客户端访问服务器并不是直接访问的,而是通过中间代理服务器,代理服务器再去访问服务器.就像一个中转站一样,无论什么,只要从客户端到服务器,你就要通过我. 一)正向 ...

  3. CRI容器运行时

    CRI容器运行时 我们知道 Kubernetes 提供了一个 CRI 的容器运行时接口,那么这个 CRI 到底是什么呢?这个其实也和 Docker 的发展密切相关的. 在 Kubernetes 早期的 ...

  4. centos配置ssh服务并简单测试

    最近在做计算机集群方面的东西,简单弄了一下ssh服务. 首先把前提情况介绍一下: 1.我是用的虚拟机先模拟的,也不是没有真机,就是跑来跑去麻烦. 2.装了三个相同配置的centos虚拟机,详细参数就不 ...

  5. 为什么ado,biz层得先写个接口,然后再实现?

    为什么ado,biz层得先写个接口,然后再实现?在我写的那个案例中不定义接口也可以 在实际开发中,一个项目肯定不是一个人完成的,这时需要项目经理的角色统一定义接口,负责不同功能模块的开发人员只需实现相 ...

  6. spring-boot-learning-事务处理

     事务处理的重要性: 面对高井发场景, 掌握数据库事务机制是至关重要的,它能够帮助我们在一定程度上保证数据的一致性,并且有效提高系统性能,避免系统产生岩机,这对于互联网企业应用的成败是至关重要的. 以 ...

  7. linux文本编辑器vim详解

    vim 1.打开文件 vim [option] - file... 打开文件 +# 打开文件后,让光标处于第#行的行首 +/字符串 打开文件后,光标处于第一个被匹配到字符串的行首 -b file 二进 ...

  8. 学习MFS(二)

    MooseFS,是一个具备冗余容错功能的分布式网络文件系统,它将数据分别存放在多个物理server或单独disk或partition上,确保一份数据有多个备份副本,对于访问MFS的client或use ...

  9. Spark学习摘记 —— RDD转化操作API归纳

    本文参考 在阅读了<Spark快速大数据分析>动物书后,大概了解到了spark常用的api,不过书中并没有给予所有api具体的示例,而且现在spark的最新版本已经上升到了2.4.5,动物 ...

  10. 用js中的let等操作,要手动开启ECMAScript6(如果不设置,let等ES6语法会报错)

    问题:idea默认没有开启ECMAScript6,需要进行设置:(如果不设置,let等ES6语法会报错)步骤: File | Settings | Languages & Frameworks ...