您会如何用初学者语言描述观察者模式?
目前,我的理解水平低于网络上所有关于观察者模式的编码示例。 我将其简单地理解为几乎是一个订阅,当委托注册的更改发生时,它会更新所有其他事件。 然而,我对好处和用途的真正理解非常不稳定。 我已经做了一些谷歌搜索,但大多数都超出了我的理解水平。
我正在尝试通过当前的作业来实现此模式,并且要真正使我的项目有意义,需要更好地理解该模式本身,也许还需要一个示例来了解其用途。 我不想强迫这种模式只是为了提交,我需要理解目的并相应地开发我的方法,以便它实际上达到良好的目的。 我的文字并没有真正讨论它,只是在一句话中提到它。 MSDN 对我来说很难理解,因为我是这方面的初学者,而且它似乎是一个高级主题。
您会如何向初学者描述这个观察者模式及其在 C# 中的用途? 例如,请保持代码非常简单,这样我比复杂的代码片段更能理解其目的。 我正在尝试通过一些简单的文本框字符串操作来有效地使用它,并使用委托来完成我的作业,因此指针会有所帮助!
Currently, my level of understanding is below all the coding examples on the web about the Observer Pattern. I understand it simply as being almost a subscription that updates all other events when a change is made that the delegate registers. However, I'm very unstable in my true comprehension of the benefits and uses. I've done some googling, but most are above my level of understanding.
I'm trying to implement this pattern with my current homework assignment, and to truly make sense on my project need a better understanding of the pattern itself and perhaps an example to see what its use. I don't want to force this pattern into something just to submit, I need to understand the purpose and develop my methods accordingly so that it actually serves a good purpose. My text doesn't really go into it, just mentions it in one sentence. MSDN was hard for me to understand, as I'm a beginner on this, and it seems more of an advanced topic.
How would you describe this Observer pattern and its uses in C# to a beginner?
For an example, please keep code very simple so I can understand the purpose more than complex code snippets. I'm trying to use it effectively with some simple textbox string manipulations and using delegates for my assignment, so a pointer would help!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(16)
我能想到的最好的例子是邮件列表(作为示例)。
作为观察者,您订阅邮件列表并观察该列表。 当您不再对该列表感兴趣时,您可以取消订阅。
这个概念就是观察者模式。 涉及两个或多个类。 一个或多个类订阅发布者类(有不同的名称),然后第一个类(以及每个订阅类)将在发布者需要时收到通知。
我就是这样向我的妻子解释的,她经常听我对编程和设计理论的咆哮。 这对她来说很有意义。 我意识到这对您来说可能太简单了,但这是一个好的开始...
问候,
坦率
The best example I can come up with is that of a mailing list (as an example).
You, the observer, subscribe to a mailing list and you observe the list. When you are no longer interested in the list, you unsubscribe.
This concept is the observer pattern. Two or more classes are involved. One or more class, subscribes to a publisher class (there are different names) and then the first class (and every subscribing class) will get notified when ever the publisher desires.
This is how I explained it to my wife, who often listens to my rantings about programming and design theory. It made sense to her. I realize this might be too simple for you but is a good start...
Regards,
Frank
查看“首先:设计模式”,了解一些真正令人惊叹的内容额头容易遵循主要模式的描述。
对于观察者来说,重要的是要理解它描述了一对多关系,并使用订阅模型来通知其他类何时发生更改。 RSS、Atom 和 Twitter 都是按照这些思路工作的。
Check out "Head First: Design Patterns" for some really, smack-your-forehead easy to follow descriptions of the major patterns.
For Observer it is important to understand that it describes a one-to-many relationship and uses a subscription model for telling other classes when there has been a change. RSS, Atom, and Twitter work along these lines.
观察者想知道什么时候发生变化,因此它订阅了主题。 主体不认识观察者。 这是重要的部分。 主题只是定义观察者需要提供的接口(或委托),并允许注册。
简而言之:观察者模式允许从一个主题调用您的观察者,该主题不关心观察者是谁以及它是否存在。
The Observer wants to know when anything changes, so it subscribes to the Subject. The Subject does not know the Observer. This is the important part. The Subject just defines the Interface (or delegate) the Observer needs to provide, and allows the registration.
In short: The Observer pattern allows your observer to be called from a subject, that does not care who the observer is and if it even exists.
有两个对象NOTIFIER和OBSERVER。 NOTIFIER 对 OBSERVER 一无所知,而 OBSERVER 知道 NOTIFER 实现了一个事件。
观察者使用事件来通知其他对象发生了某些事情。 简单来说,事件就是方法列表。 因为 OBSERVER 希望在发生某事时得到通知,所以 OBSERVER 在 NOTIFER 事件中添加了一个方法,如果发生某事则应调用该方法。
因此,如果发生这种情况,NOTIFIER 会发布此事件,NOTIFIER 只是遍历方法列表并调用它们。 当调用 OBSERVER 添加的方法时,OBSERVER 知道事情发生了,并且可以执行这种情况下所需的操作。
下面是一个带有
ValueChanged()
事件的示例通知程序类。这是一个观察者类的示例。
一个小型测试应用程序。
输出如下。
为了理解委托类型,我将它们与类类型进行比较。
我们定义了一个带有两个方法的简单类
Example
。 现在我们可以使用这个类类型了。虽然这有效,但以下不起作用,因为类型不匹配。 您会收到编译器错误。
我们可以使用变量
example
。现在与委托类型相同。 首先我们定义一个委托类型——这只是一个类型定义,就像之前的类定义一样。
现在我们可以使用委托类型,但是我们不能将普通数据存储在委托类型变量中,而是存储在方法中。
我们现在已经存储了对象
example
的方法DoSomething()
。 以下代码不起作用,因为我们将MyDelegate
定义为接受一个字符串参数并返回 void 的委托。DoSomethingElse
返回 void 但采用整数参数,因此您会收到编译器错误。最后你可以使用变量
方法
。 您无法执行数据操作,因为变量不存储数据,而是存储方法。 但您可以调用存储在变量中的方法。这会调用我们存储在变量中的方法 -
example.DoSomething()
。There are two objects NOTIFIER and OBSERVER. NOTIFIER knows nothing about OBSERVER, while OBSERVER knows that NOTIFER implements a event.
OBSERVER uses the event to inform other objects that something happened. Simply spoken an event is a list of methods. Because OBSERVER wants to be notified if something happend, OBSERVER adds a method, that should be called if something happens, to the event of NOTIFER.
So if the thing happens, that NOTIFIER publishes with this event, NOTIFIER just walks over the list of methods and calls them. When the method added by OBSERVER is called, OBSERVER knows that the thing happend and can do what ever is required in this case.
Here is a example notifier class with a
ValueChanged()
event.Here a example observer class.
A small test application.
The output will be the following.
To understand delegate types I am going to compare them with class types.
We defined a simple class
Example
with two methods. Now we can use this class type.While this works the following does not work because the types do not match. You get a compiler error.
And we can use the variable
example
.Now the same with a delegate type. First we define a delegate type - this is just a type definition like the class definition before.
Now we can use the delegate type, but we cannot store normal data in a delegate type variable, but a method.
We have now stored the method
DoSomething()
of the objectexample
. The following does not work because we definedMyDelegate
as a delegate taking one string parameter and returning void.DoSomethingElse
returns void but takes an integer parameter so you get a compiler error.And finally you can use the variable
method
. You cannot perform data manipulation because the variable stores no data but a method. But you can call the method stored in the variable.This calls the method we stored in the variable -
example.DoSomething()
.观察者模式就像它听起来的那样——
它是某些对象观察对象、观察其变化的一种方式。
在 C# 中,这变得有些简单,因为事件基本上是实现观察者模式的特定于语言的方法。 如果您曾经使用过事件,那么您就使用过观察者模式。
在其他语言中,这不是内置的,因此已经进行了许多尝试来形式化处理此问题的方法。
The observer pattern is just like it sounds -
It's a means for some objects to watch an object, observing it for changes.
In C#, this becomes somewhat simple, since events are basically a language-specific means of implementing the observer pattern. If you've ever used events, you've used the observer pattern.
In other languages, this isn't built in, so there have been many attempts to formalize approaches to handling this.
观察者就像一条直接的通讯线路。 当您生病时,不要让所有亲戚打电话询问您的情况,而是写一张卡片,每个感兴趣的人都会收到它(或副本)。 当你病情好转时,你会寄出一张卡片。 当你绊倒脚趾时,你就发出了一张卡片。 当你得到 A 时,你会发出一张卡片。
任何关心的人都会进入您的群发邮件列表,并可以按照他们认为合适的方式进行回复。
这种依赖关系对于 UI 来说非常有用。 如果我有一个缓慢的进程(例如),即使取得进展,它也可能会触发。 进度条元素可以观察并更新其覆盖范围。 OK 按钮可以观察到这一点并在 100% 时变为活动状态。 光标可以观察到动画,直到进度达到 100%。 这些观察者都不需要互相了解。 此外,这些元素都不需要严格了解驱动它们的因素。
Observer is like a direct line of communication. Rather than have all your relatives call you to find out how you are, when you get sick write a card and everyone who is interested gets it (or a copy). When you get better, you send out a card. When you stub your toe, you send out a card. When you get an A, you send out a card.
Anyone who cares gets on your mass mailing list and can respond however they see fit.
This dependency is great for UI. If I have a process that is slow (for example), it can fire an even when progress is made. A progress bar element could observe that and update its coverage. An OK button could observe that and become active at 100%. A cursor could observe that an animate until the progress is 100%. None of these observers needs to know about each other. Furthermore, none of these elements strictly needs to know what is driving them either.
这种模式即使不是最基本的模式,也可能是最基本的模式之一。
涉及两个“人”; 发布者和订阅者/观察者。
观察者只需要求发布者在有“新闻”时通知他。这里的新闻可以是任何重要的东西。 它可以是空气的温度,可以是网站上的新帖子,也可以是一天中的时间。
This pattern is probably one of the most basic, if not the most basic pattern there is.
There are two "people" involved; the publisher and the subscriber/observer.
An observer simply asks the publisher to notify him when there is "news". News can be anything of importance here. It can be the temperature of the air, it can be a new post on a website, it can be the time of day.
您遇到的麻烦可能是定义正确的接口。 该接口定义了订阅者和发布者之间的交互。
C# WinForms 应用程序
首先创建一个像这样的
Setup Program.cs我们在这里做了两件事。 第一个是订阅者将实现的接口。 然后是发布者保存所有订阅者的列表。
然后制作带有两个按钮的表单一,一个标记为“表单 2”,另一个标记为“表单 3”。然后添加一个文本框,然后添加另一个标记为“添加”的按钮。
我特意设置了 Form2 按钮和 FOrm3 按钮,以便为每种类型的表单制作多个副本。 例如,您可以一次有十二个。
您会注意到,创建每个表单后,我将其放入观察者列表中。 我能够做到这一点是因为 Form2 和 Form3 都实现了 IObserver。 显示表单后,我在观察者列表上调用刷新,以便使用最新数据更新新表单。 请注意,我可以将其转换为 IObserver 变量并仅更新该形式。 我试图尽可能简短。
然后,对于“添加”按钮“Button3”,我从文本框中提取文本,将其存储在我的 DataList 中,然后刷新所有观察者。
然后制作Form2。 添加一个列表框和以下代码。
使用系统;
然后添加 Form3(一个组合框)并添加以下代码。
您会注意到,每个表单实现 IObserver 接口的刷新方法略有不同。 一个用于列表框,另一个用于组合框。 接口的使用是这里的关键要素。
在现实世界的应用中,这个例子会更加复杂。 例如,不要在刷新界面中传递字符串列表。 它不会有任何参数。 相反,发布者(本例中为 Form1)将实现发布者接口,并在观察者初始化时向观察者注册自身。 每个观察者都能够在其初始化例程中接受发布者。 然后,当刷新时,它将通过通过接口公开的方法将字符串列表从发布者中拉出。
对于具有多种数据类型的更复杂的应用程序,这允许您自定义实现 IObserver 的表单从发布者中提取哪些数据。
当然,如果您只希望观察者能够显示字符串列表或特定数据。 然后将其作为参数的一部分传递。 接口明确了每一层想要做什么。 这样 5 年后,您就可以查看代码并了解代码“哦,它在做什么”。
Probably the thing you are having trouble with is defining the proper interfaces. The interface defines the interaction between the Subscriber and the Publisher.
First make a C# WinForms application
Setup Program.cs like this
We are making two things here. The first the interface which the subscribers will implement. Then a list for the publisher to hold all the subscribers.
Then make form one with two buttons, one labeled Form 2 and the other Labeled Form 3. Then add a textbox, then another button labeled Add
I deliberately set up the Form2 button and the FOrm3 button to make multiple copies of each type of Form. For example, you can have twelve up at once.
You will notice that after creating each form I put it into the Observers list. I am able to do this because both Form2 and Form3 implement IObserver. After I show the Form I call refresh on the Observer list so the new form is updated with the latest data. Note I could have cast it to a variable of IObserver and updated just that form. I am trying to be as brief as possible.
Then for the Add button 'Button3' I pull the text from the textbox store it in my DataList and then refresh all the observers.
Then make Form2. Add a list box and the following code.
using System;
Then Add Form3, a combobox and add the following code.
You will notice that each form implements the refresh method of the IObserver interface slightly different. One is for a listbox the other for a combo box. The use of interfaces is the key element here.
IN a real world application this example would be more complex. For example instead of passing the string list in the Refresh Interface. It would not have any parameters. Instead the Publisher (Form1 in this example) would implement a publisher interface and register itself with the Observers as they are being initialized. Each observer would be able to accept a publisher in it's initialization routine. Then when it is refreshed it would pull the string list out of the Publisher via a method exposed through the interface.
For more complex applications with multiple types of data this allows you customize what data the form implementing IObserver is pulling out of the publisher.
Of course if you ONLY want the Observer to be able to display a string list or specific data. Then pass it as part of the parameters. The Interface makes explicit what want each layer to do. This way 5 years from now you can look at the code and code "Oh that what it is doing."
(来源:headfirstlabs.com)
正如所说,查看“Head First:设计模式”,他们也有一些有关本书的论坛 和 设计冥想。
观察者模式遵循好莱坞原则“不要打电话给我们,我们会打电话给你”
模式的好网站
http://www.dofactory.com/Patterns/PatternObserver.aspx
(source: headfirstlabs.com)
As said Check out "Head First: Design Patterns" they also have some forums regarding the book and a design meditation.
Observer Pattern follow the Hollywood principle "Don't call us we call you"
Good site for patterns
http://www.dofactory.com/Patterns/PatternObserver.aspx
用最简单的术语来说,有两个组件:观察者和被观察者。
在外部,被观察者需要一种添加(注册)和删除观察者的方法。
在内部,被观察者需要一个已注册观察者的列表。
观察者需要一个公共方法,例如 Notify() 或 Notify(params)。
每当被观察者发生特定事件时,它将循环遍历列表并对每个注册的观察者调用 Notify()。
在最简单的情况下,这是一个简单的通知,内容是“嘿,观察者,我的数据已更改,请过来刷新一下”
在更复杂的版本中,参数可以让观察者知道发生了什么变化。
在模型-视图-控制器中,被观察者通常是一个实体对象——保存数据的东西。 控制者就是观察者。 它监视模型中的更改,并告诉视图如果对更改感兴趣则自行更新。
Java 事件侦听器是此模式的现实实现。
In it's very simplest terms, there are two components: Observer and Observed.
Externally, the Observed needs a way to add (register) and remove an Observer.
Internally, the Observed needs a list of registered Observers.
The observer needs a public method such as Notify() or Notify(params).
Any time a particular thing happens to the Observed, it will loop through the list and call Notify() on every registered observer.
In the simplest case, it's a simple notification that says "Hey, Observer, My Data Changed, come and refresh yourself"
In more complex versions, parameters can be past letting the observer know what has changed.
In a Model-View-Controller, the Observed is usually an entity object -- Something that holds data. The Controller is the Observer. It watches for changes in the Model, and tells the View to update itself if it is interested in the change.
Java event listeners are a real world implementation of this pattern.
想象一下,您有一个想要观察其行为(或状态)的对象。 例如,当字段 A 达到值 10 时,您希望获得有关该事件的通知,而无需实际耦合您想要观察的这个复杂对象的实现细节。
你定义一个接口,称之为 Observable 并让你的目标实现这个接口,它应该至少有两个方法来注册和注销一个观察者,而观察者又是当字段 A 达到 10 时将被观察者调用的对象。你的观察者只需调用 Observable 进行注册(并在完成后取消注册)。 Observable 通常会维护一个观察者列表并立即通知他们,或者根据您的需要通知他们。 它也可以同步或异步完成,这取决于你。 这是非常简单的解释,无需编写代码。 一旦您理解了它,实现就可以在细节上有所不同,以满足您的特定需求。
Imagine that you have a object whose behavior (or state) you want to observe. For example, when field A hits value 10, you want to get informed on that event without actually getting coupled with the implementation details of this complex object you want to observe.
You define an interface, call it Observable and let your target implement this interface, it should have at least two methods to register and unregister an Observer, which in turn is the object which will get called by Observer when field A hits 10. Your Observer simply calls Observable to register (and unregister when done). Observable normally maintain a list of Observers and notifies them at once, or as you please. It can also be done synchronously or asynchronously, it's up to you. This is very simplistic explanation without writing code. Once you understand it, the implementations can differ in details to fit your particular needs.
观察者(发布/订阅)
当对象更改状态时,它会通知在运行时已注册其兴趣的其他对象。
通知对象(发布者)向其所有观察者(订阅者)发送事件(发布)。
Observer (Publish/Subscribe)
When an object changes state, it notifies other objects that have registered their interest at runtime.
The notifying object (publisher) sends an event (publication) to all its observers (subscribers).
一句话:
一个对象(主题)允许其他对象(观察者)注册通知。
实际示例:
假设您有一个应用程序,并且希望让其他开发人员构建插件。
您可以创建一个 PluginSubject 类,并在其上放置一个名为 NotifyOrderCreated 的方法。 每当在订单屏幕上创建新订单时,它都会调用 PluginSubject.NotifyOrderCreated。
当发生这种情况时,PluginSubject 会获取 PluginObserver 的列表,并对每个 PluginObserver 调用 PluginObserver.Notify,并传入描述该事件的消息。
这使得一些非常巧妙的功能成为可能。
比你想知道的更多:
我实际上最近这样做了,所以我将更深入地研究这个例子 - 如果你需要你的观察者实现一个特殊的接口,比如说IPluginObserver,你可以使用反射遍历程序集中的类型并动态实例化插件。
然后,您可以允许用户注册自己的程序集(您必须在某处存储程序集名称列表,然后遍历它),然后,您就获得了可扩展性!
In one sentence:
An object (the Subject) allows other objects (Observers) to sign up for notifications.
Practical example:
Let's say you've got an app and you want to let other devs build plugins.
You could create a PluginSubject class, and put a method on it called NotifyOrderCreated. Whenever a new order is created on your order screen, it calls PluginSubject.NotifyOrderCreated.
When that happens, the PluginSubject gets a list of PluginObservers and calls PluginObserver.Notify on each of them, passing in a message describing the event.
That enables some really neat functionality.
Way More Than You Want to Know:
I actually did this recently, so I will take the example one step deeper - if you require your Observers to implement a special interface, let's say IPluginObserver, you can use reflection to walk the types in your assembly and instantiate the plugins on the fly.
Then you can allow users to sign up their own assemblies (you have to store a list of assembly names somewhere and then walk it), and bam, you've got extensibility!
观察者是一种解耦的手段,即松开两个对象之间的联系。 您想要这样,因为它使您的代码更整洁且更易于维护。 这几乎是所有设计模式的目标:更易于阅读,更易于维护代码。
在此模式中,您有两个类、两个对象:发布者和观察者。 Publisher 是一个实际上做了一些工作的类,然后它经常会调用任何观察者的方法来告诉他们这件事。 它知道要调用哪些类,因为它保留了订阅的观察者列表。
因此,您的发布商可能看起来像这样:
发布商确实完成了大部分工作。 观察者所需要做的就是添加到列表中并实现被调用的方法。 就像这样:
这是关于它如何工作的一个非常简单的想法。 现在,为什么它有价值? 看起来耦合得很好吧? 原因之一是因为我不使用接口,这将允许我设置许多具有观察者功能的类,并且发布者不需要了解有关它们的更多信息,除了它们可以接收 Alert() 调用之外。 另请注意,发布者将尝试对其拥有的任何和所有观察者调用警报,即使它没有观察者。
现在,在 C# 世界中,该语言通过其 Event 对象拥有此模式的内置版本。 事件非常强大,并且利用委托,这是一种在另一个方法调用中将方法作为参数传递的方式。 它们允许一些严重的脱钩,但我会把它留到一个新问题。
Observer is a means of decoupling, that is, loosening the connections between two objects. You want that because it makes your code neater and easier to maintain. That's pretty much the goal of all design patterns: easier to read, easier to maintain code.
In this pattern, you have two classes, two objects: publisher and observer. Publisher is a class that actually does some work, then it every so often will call methods on any observers to tell them about it. It knows which classes to call because it keeps a list of observers who subscribed.
So your Publisher might look something like this:
Publisher really does most of the work. All the Observer needs to do is get added to the list and implement the called method. Like so:
That's a pretty barebones idea of how it works. Now, why is that valuable? Looks pretty well coupled up huh? One reason for that is because I don't use an Interface, which would allow me to set up many classes with Observer functionality, and Publisher need never know anything more about them except that they can receive an Alert() call. Also note that Publisher will attempt to call Alert on any and all Observers it has, even if it has none.
Now, in the C# world, the language has a built in version of this pattern through it's Event objects. Events are very powerful and make use of Delegates, which is a way of passing a method as a parameter in another method call. They allow for some serious decoupling, but I'd save that for a new question.
很少有实时示例:
订阅或任何一般订阅
沟通者
Very few real time Examples:
Subscription or any subscription in general
Communicator
那些认为 .NET 中的事件实际上是观察者模式的实现的人并没有拉动你的链条;他们只是在说。 这是真的。 至于它实际上是如何工作的,无论是从高层次的角度还是从更多特定于实现的细节来看,我将使用一个类比。
想想报纸出版商。 用面向对象编程的术语来说,我们可能会认为报纸是一个可观察的东西。 但它是如何运作的呢? 显然,报纸本身的实施细节(也就是说,在这个类比中,记者、作家、编辑等都在办公室工作以整理报纸)并没有公开曝光。 人们(观察员)不会聚集在一起观看报纸出版商的员工工作。 他们不能这样做,因为没有适当的协议(或接口)来做到这一点。
这就是您观察(即阅读)报纸的方式:您订阅它。 你把你的名字列入该报纸的订阅者名单上,然后出版商就会知道每天早上将一份报纸送到你家门口。 如果您不想再观察(阅读)它,您可以取消订阅; 您的名字将从该列表中删除。
现在,这似乎是一个抽象的类比; 但它实际上与 .NET 事件的工作方式几乎完美平行。
给定一些旨在可观察的类,其实现通常不需要为公众所知。 然而,它会向公众公开一种特定类型的接口,而该接口就是一个事件。 想要观察此事件的代码本质上将自己注册为订阅者:
当相同的代码决定不再希望收到此事件的通知时,它会取消订阅:
现在快速讨论委托以及此代码的实际工作原理。 您可能知道也可能不知道,委托本质上是一个存储方法地址的变量。 通常,此变量具有类型 - 就像声明为
int
、double
、string
等的 value 变量一样都有类型。 对于委托类型,此类型由方法的签名定义; 即它的参数和它的返回值。 特定类型的委托可以指向执行任何操作的任何方法,只要该方法具有适当的签名即可。回到报纸的类比:为了成功订阅报纸,您必须实际上遵循特定的模式。 具体来说,您需要提供要将报纸投递到的有效地址。 你不能只是说:“是的,将其发送给丹。” 你不能说:“我要一个培根芝士汉堡。” 您必须向出版商提供他们可以有意义地合作的信息。 在 .NET 事件领域,这意味着需要提供具有正确签名的事件处理程序。
在大多数情况下,此签名最终会成为如下所示的某种方法:
上面的方法可以存储在
EventHandler
类型的委托变量中。 对于更具体的情况,有通用的EventHandler
委托类型,它描述了与上面类似的方法,但具有某种派生类型的e
参数来自EventArgs
。请记住,委托实际上是指向方法的变量,因此在 .NET 事件和报纸订阅之间建立最终联系并不困难。 事件的实现方式是通过委托列表,可以向其中添加或删除项目。 这实际上就像报纸出版商的订阅者名单,每天早上分发报纸时,每个订阅者都会收到一份副本。
不管怎样,希望这能帮助您在某种程度上了解观察者模式。 当然,这种模式还有许多其他类型的实现,但 .NET 事件是大多数 .NET 开发人员都熟悉的范例,因此我认为这是一个很好的起点,可以加深人们的理解。
Those who've suggested that events in .NET really are an implementation of the Observer pattern aren't pulling your chain; it's true. As for how this actually works, both from a high level perspective and in terms of more implementation-specific details, I'll use an analogy.
Think of a newspaper publisher. In OOP terms we might think of a newspaper as an observable thing. But how does it work? Clearly the implementation details of the newspaper itself (that is, in this analogy, the journalists, writers, editors, etc. all working back at the office to put the newspaper together) are not publicly exposed. People (observers) do not gather together and watch the employees of the newspaper publisher doing their work. They can't just do that, because there is no protocol (or interface) in place for doing that.
This is how you observe (i.e., read) a newspaper: you subscribe to it. You get your name on a list of subscribers to that paper, and then the publisher knows to deliver one on your doorstep every morning. If you don't want to observe (read) it anymore, you unsubscribe; you get your name taken off that list.
Now, this might seem like an abstract analogy; but it's actually an almost perfect parallel to how .NET events work.
Given some class intended to be observable, its implementation in general need not be known to the public. However, it will expose a particular kind of interface to the public, and that interface is an event. Code that wants to observe this event essentially registers itself as a subscriber:
When this same code decides it doesn't want to be notified of this event anymore, it unsubscribes:
Now for a quick discussion of delegates and how this code actually works. A delegate, as you may or may not know, is essentially a variable that stores the address of a method. Typically, this variable has a type -- just like value variables declared as
int
,double
,string
, etc. all have types. In the case of delegate types, this type is defined by the method's signature; that is, its parameters and its return value. A delegate of a particular type can point to any method, that performs any action, as long as that method has the appropriate signature.So to come back to the newspaper analogy: in order to successfully subscribe to a newspaper, you have to actually follow a particular pattern. Specifically, you need to provide a valid address where you want the newspaper delivered. You can't just say, "Yeah, send it to Dan." You can't say, "I'll have a bacon cheeseburger." You have to give the publisher information that they can meaningfully work with. In the world of .NET events, this translates to needing to supply an event handler of the correct signature.
In most cases, this signature ends up being some method like this:
The above is a method that could be stored in a delegate variable of type
EventHandler
. For more specific cases, there's the genericEventHandler<TEventArgs>
delegate type, which desribes a method similar to the above, but with ane
parameter of some type derived fromEventArgs
.Keeping in mind that delegates are really variables pointing to methods, it isn't hard to make the final connection between .NET events and newspaper subscriptions. The way events are implemented is via a list of delegates, to/from which items may be added and removed. This is really just like a newspaper publisher's list of subscribers, each of whom receives a copy when the newspapers are distributed every morning.
Anyway, hopefully this has helped you get your head around the Observer pattern somewhat. There are many other kinds of implementations of this pattern, of course, but .NET events is a paradigm with which the majority of .NET developers are familiar, so I think it's a good starting point from which to develop one's understanding.