当前位置: 首页 > news >正文

免费网站源码wordpress网站布置视频教程

免费网站源码,wordpress网站布置视频教程,win优化大师官网,php网站开发教程 pdf引言 在 C#中的委托和事件 一文的后半部分#xff0c;我向大家讲述了Observer(观察者)模式#xff0c;并使用委托和事件实现了这个模式。实际上#xff0c;不使用委托和事件#xff0c;一样可以实现Observer模式。在本文中#xff0c;我将使用GOF的经典方式#xff0c;再… 引言 在 C#中的委托和事件 一文的后半部分我向大家讲述了Observer(观察者)模式并使用委托和事件实现了这个模式。实际上不使用委托和事件一样可以实现Observer模式。在本文中我将使用GOF的经典方式再次实现一遍Observer模式同时将讲述在 C#中的委托和事件 一文中没有提及的推模式(Push)和拉模式(Pull)。 设计思想概述 在 C#中的委托和事件 一文后半部分中我已经较详细的讲述了Observer设计模式的思想所以这里仅简单的提及一下。Observer设计模式中实际上只包含了两类对象一个是Subject(主题)一个是Observer(观察者)。它们之间的角色是 Subject主题(被监视对象)它往往包含着Observer所感兴趣的内容。Observer观察者它观察Subject。当Subject中的某件事发生的时候(通常是它所感兴趣的内容改变的时候)会被自动告知而Observer则会采取相应的行动(通常为更新自身状态或者显示输出)。它们之间交互的核心工作流程就是 Subject 提供方法比如Register()和UnRegister()用于Observer进行注册(表示它对Suject感兴趣)和取消注册(不再对它感兴趣)。 Register()方法实现为它接收一个Observer的引用作为参数并保存此引用。保存的方式通常为在 Subject内声明一个集合类比如ListObserver。一个Subject可以供多个Observer注册。 调用Subject实例的Register()方法并将一个Observer的引用传递进去。Observer 包含一个Update()方法此方法供 Subject(通过保存的Observer的引用)以后调用。Subject 包含一个Notify()方法当某件事发生时调用Notify()通知Subject。 Notify的方法实现为遍历保存Observer引用的集合类然后在Observer的引用上调用Update方法更新Observer。某件事是一个不确定的事对于热水器来说这个事就是“温度达到一定高度”。它对外界暴露的方法应该是“烧水” -- BoilWater()而不是Notify()所以Notify通常实现为私有方法。 Observer 向 Subject 注册的序列图表示如下 Subject事件触发时通知Observer调用Update()方法的序列图如下 模式的接口定义 按照面向对象设计的原则面向接口编程而非面向实现编程。那么现在应该首先定义Subject和Observer的接口我们可能很自然地会想到将这两个接口分别命名为 ISubjcet 和 IObserver。而实际上据我查阅的一些资料这里约定俗成的命名为IObservable 和 IObserver其中由 Subject 实现 IObservable。 NOTE可能很多人和我当初一样困惑命名为ISubject不是很好么为什么叫 IObservable我参考了一些资料大概的解释是这样的接口定义的是一个行为表示的是一种能力所以对于接口的命名最好用动词的形容词或者名词变体。这里Observe是一个动词意为观察Observer是动词的名词变体意为观察者Observable是动词的形容词变体表示为可观察的。类似的例子有很多比如IComparable 和 IComparer 接口、IEnumerable 和 IEnumerator 接口等。 现在我们先来看Subject需要实现的接口IObservable。 IObservable接口 首先创建解决方案ObserverPattern并在其下添加控制台项目ConsoleApp然后假如IObservable.cs文件来完成这个接口。如同我们上面分析的Suject将实现这个接口它只用定义两个方法 Register()和Unregister public interface IObservable {    void Register(IObserver obj);       // 注册IObserver    void Unregister(IObserver obj);     // 取消IObserver的注册} 注意它的两个方法接收 IObserver类型的对象分别用于注册和取消注册。 IObserver 接口 现在我们再来完成IObserver接口所有的Observer都需要实现这个接口以便在事件发生时能够被 自动告知(自动调用其Update()方法改变自身状态)它仅包含一个Update()方法 public interface IObserver {    void Update();      // 事件触发时由Subject调用更新自身状态} 再强调一遍这里的关键就是Update()方法不是由Observer本身调用而是由Subject在某事发生时调用。 抽象基类 SubjectBase 注意到上面序列图中的Container(容器)它用于保存IObserver引用的方式对于很多IObservable的实现来说可能都是一样的比如说都用ListIObserver或者是Hashtable等。所以我们最好再定义一个抽象类让它实现 IObservable 接口并使用ListIObserver作为容器的一个默认实现以后我们再创建实现IObservalbe的类(Subject)只需要继承这个基类就可以了这样可以更好地代码重用 public abstract class SubjectBase : IObservable {    // 使用一个 ListT 作为 IObserver 引用的容器    private ListIObserver container new ListIObserver();        public void Register(IObserver obj) {       container.Add(obj);    }    public void Unregister(IObserver obj) {       container.Remove(obj);    }    protected virtual void Notify() {       // 通知所有注册了的Observer       foreach (IObserver observer in container) {           observer.Update();           // 调用Observer的Update()方法       }    }} 有了这样两个接口一个抽象类我们的UML类图便可以画出来了 注意这里也可以不使用IObservable接口直接定义一个抽象类定义IObservable接口能进一步的抽象更灵活一些可以基于这个接口定义出不同的抽象类来(主要区别为Container的实现不同可以用其他的集合类)。 Observer模式的实现 现在我们来实现Observer模式我们先创建我们的实体类(Concrete Class)热水器(Heater)报警器(Alarm)显示器(Screen)。其中热水器是Subject报警器和显示器是Observer。报警器和显示器关心的东西是热水器的水温当热水器的水温大于97度时显示器需要显示“水快烧开了”报警器发出声音也提示“嘟嘟嘟水快烧开了”。 下面的代码非常的简单明了也添加了注释我就不做说明了 热水器(Subject)的实现 热水器继承自SujectBase基类并添加了BoilWater()方法。 public class Heater : SubjectBase {    private string type;              // 添加型号作为演示    private string area;              // 添加产地作为演示    private int temprature;         // 水温    public Heater(string type, string area) {       this.type type;       this.area area;       temprature 0;    }    public string Type { get { return type; } }    public string Area { get { return Area; } }    public Heater() : this(RealFire 001, China Xian) { }    // 供子类覆盖以便子类拒绝被通知或添加额外行为    protected virtual void OnBoiled() {       base.Notify(); // 调用父类Notify()方法进而调用所有注册了的Observer的Update()方法    }    public void BoilWater() {       // 烧水       for (int i 0; i 99; i) {           temprature i1;           if (temprature 97) {       // 当水快烧开时(温度97度)通知Observer              OnBoiled();           }       }    }} 报警器 和 显示器 (Observer)的实现 报警器(Alarm)和显示器(Screen)的实现是类似的仅仅为了说明多个Observer可以注册同一个Subject。 // 显示器public class Screen : IObserver {    // Subject在事件发生时调用通知Observer更新状态(通过Notify()方法)    public void Update() {        Console.WriteLine(Screen.PadRight(7) : 水快烧开了。);    }}// 报警器public class Alarm : IObserver {    public void Update() {       Console.WriteLine(Alarm.PadRight(7) 嘟嘟嘟水温快烧开了。);    }} 运行程序 接下来我们运行一下程序 class Program {    static void Main(string[] args) {       Heater heater new Heater();       Screen screen new Screen();       Alarm alarm new Alarm();       heater.Register(screen);     // 注册显示器       heater.Register(alarm);         // 注册热水器       heater.BoilWater();             // 烧水       heater.Unregister(alarm);    // 取消报警器的注册       Console.WriteLine();       heater.BoilWater();             // 再次烧水    }} 输出为 Screen : 水快烧开了。Alarm  嘟嘟嘟水快烧开了。Screen : 水快烧开了。Alarm  嘟嘟嘟水快烧开了。Screen : 水快烧开了。Alarm  嘟嘟嘟水快烧开了。Screen : 水快烧开了。Screen : 水快烧开了。Screen : 水快烧开了。 推模式 和 拉模式 像上面这种实现方式基本上是没有太大意义的。比如说我们通常会希望在Screen上能够即时地显示水的温度而且当水在100度的时候显示“水已经烧开了”而非“水快烧开了”。我们还可能希望显示热水器的型号和产地。所以我们需要 在Observer的Update()方法中能够获得 Subject中所发生的事件的进展状况 或者事件触发者Suject的状态和属性。在本例中事件的进展状况就是水的温度事件触发者(Suject)的状态和属性则为 热水器的型号和产地。此时我们有两种策略一种是 推模式一种是拉模式我们先看看推模式。 Observer中的推模式 顾名思义推模式就是Subject在事件发生后调用Notify时将事件的状况(水温)以及自身的属性(状态)封装成一个对象推给Observer。而如何推呢当然是通过Notify()方法让Notify()方法接收这个对象在Notify()方法内部再次将对象传递给Update()方法了。那么现在要做两件事1、创建新类型这个类型封装了我们想要推给Observer(显示器)的事件进展状况(水温)以及事件触发者Subject(热水器)的属性(或者叫状态)。 我们在ObserverPattern解决方案下重新建一个控制台项目起名为ConsoleApp2并设置为启动项目。将上一项目ConsoleApp中的文件复制进来然后我们创建一个新类型BoiledEventArgs用它来封装我们推给Observer的数据。 public class BoiledEventArgs {    private int temperature;     // 温度    private string type;         // 类型    private string area;         // 产地    public BoiledEventArgs(int temperature, string type, string area) {       this.temperature temperature;       this.type type;       this.area area;    }    public int Temperature { get { return temperature; } }    public string Type { get { return type; } }    public string Area { get { return area; } }} 注意这个类型的命名虽然为BoiledEventArgs但是和.Net中的内置类型EventArgs没有任何联系只是起了这样一个名字。 2、我们需要依次修改 IObserver接口Screen类的Update()方法SubjectBase类以及Heater类让他们可以接收这个EventArgs参数。出于示范的目的后面的例子我都将不再使用警报器Alarm类它的存在仅仅是为了说明多个Observer可以注册一个Subject上面我们已经示范过了所以现在我们把它删掉。 我们先来看下IObserver接口 public interface IObserver {    // 推模式的实现方式接收一个BoiledEventArgs    void Update(BoiledEventArgs e);} 接口变了显示器(Screen)的实现也需要修改 public class Screen : IObserver {    private bool isDisplayedType false;      // 标记变量标示是否已经打印过    public void Update(BoiledEventArgs e) {       // 打印产地和型号只打印一次       if (!isDisplayedType) {           Console.WriteLine({0} - {1}: , e.Area, e.Type);           Console.WriteLine();           isDisplayedType true;       }       if (e.Temperature 100) {              Console.WriteLine(              String.Format(Alarm.PadRight(7) 水快烧开了当前温度{0}。, e.Temperature));       } else {           Console.WriteLine(              String.Format(Alarm.PadRight(7) 水已经烧开了));                        }    }} 现在可以看到在Update()方法中通过传递进来的BoiledEventArgs参数我们可以获得事件进展(温度)以及事件触发者的信息(产地和型号)了。 接下来我们看这个 BoiledEventArgs是如何传递给 Update()方法的我们看下SubjectBase基类 和 热水器Heater需要做怎样的修改 public abstract class SubjectBase : IObservable {    // 其余略...    protected virtual void Notify(BoiledEventArgs e) {    // 通知所有注册了的Observer       foreach (IObserver observer in container) {           observer.Update(e);          // 调用Observer的Update()方法       }    }}public class Heater : SubjectBase {    // 其余略 ...    // 供子类覆盖以便子类拒绝被通知或者添加额外行为    protected virtual void OnBoiled(BoiledEventArgs e) {       base.Notify(e);          // 调用基类方法通知Observer    }    public void BoilWater() {       // 烧水       for (int i 0; i 99; i) {           temprature i 1;           if (temperature 97) {      // 当水快烧开时(温度97度)通知Observer             BoiledEventArgs e new BoiledEventArgs(temperature, type, area);              OnBoiled(e);           }       }    }} 我们看到在事件发生时(水温97度)我们根据事件进展状况和热水器的属性创建了BoiledEventArgs类型的实例并且传递给了OnBoiled()方法进而调用了基类的方法传递了该实例。 我们再次对程序进行一下测试 class Program {    static void Main(string[] args) {       Heater heater new Heater();       Screen screen new Screen();       heater.Register(screen);     // 注册显示器       heater.BoilWater();             // 烧水    }}输出为ChinaXian - RealFire 001:Alarm  水快烧开了当前温度98。Alarm  水快烧开了当前温度99。Alarm  水已经烧开了 Observer 中的拉模式 继续进行之前我们在ObserverPattern解决方案下再创建一个新的Console项目命名为ConsoleApp3然后把ConsoleApp2 项目下的文件拷贝过来把启动项目设置为ConsoleApp3。 拉模式的意思就是说Subject(热水器)在事件发生时(水温超过97度)并非将自身状态封装成对象通过Notify()方法进而再通过Observer的引用调用Update()方法传递给Observer(显示器)而是直接将自身的引用(以基类或者Object的形式)传递过去。Observer在Update()方法中对传递进来的引用进行一个向下转换(Downcast)转换成具体的Subject类(比如热水器)然后通过这个引用调用Subject实体类(热水器)的公共属性获取状态信息(从中把有用数据拉出来 :-)。 我们需要再次对IObserver接口的Update()方法修改相应的修改还要修改SubjectBase基类、Heater类 以及 IObserver接口的实现--显示器类(Screen)。 public interface IObserver {    // 拉模式的Update()方法定义    void Update(IObservable sender);} 注意这里接收一个IObservable类型作为Update()方法的参数而IObservable接口本身只包含Regesiter()和Unregister()两个方法所以在IObserver的实现中这里要进行向下转换转换为响应的实体类对象才能获得对象的属性。这里也可以接受一个Object类型参数。 我们现在看这个接口的实现显示器类(Screen) public class Screen : IObserver {    private bool isDisplayedType false;    public void Update(IObservable obj) {       // 这里存在一个向下转换(由继承体系中高级别的类向低级别的类转换)。       Heater heater (Heater)obj;        // 打印产地和型号只打印一次       if (!isDisplayedType) {           Console.WriteLine({0} - {1}: , heater.Area, heater.Type);           Console.WriteLine();           isDisplayedType true;                    }       if (heater.Temperature 100) {     // 通过热水器引用heater获取温度           Console.WriteLine(              String.Format(Alarm.PadRight(7) 水快烧开了当前温度{0}。, heater.Temperature));       } else {           Console.WriteLine(              String.Format(Alarm.PadRight(7) 水已经烧开了));                        }    }} 接下来我们再看下 SubjectBase基类以及热水器Heater的修改 public class SubjectBase    // 其余略...    // 接受一个 IObservable 类型    protected virtual void Notify(IObservable obj) {      // 通知所有注册了的Observer       foreach (IObserver observer in container) {           observer.Update(obj);        // 调用Observer的Update()方法       }    }}public class Heater : SubjectBase {     // 其余略...    // 新添属性 Temperature    public int Temperature { get { return temperature; } }    // 供子类覆盖以便子类拒绝被通知或者添加额外行为    protected virtual void OnBoiled() {       base.Notify(this);           // -- 将本身传递过去    }    public void BoilWater() {       // 烧水       for (int i 0; i 99; i) {           temperature i1;           if (temperature 97) {         // 当水快烧开时(温度97度)通知Observer              OnBoiled();           // -- 修改了这里           }       }    }} 注意Heater类以前不提供对temperature字段的访问而为了能在Observer(显示器)的Update()方法中的通过引用访问到temperature我们需要为Heater类再添加一个 Temperature属性 public int Temperature { get { return temperature; } } 而在调用Notify()方法时我们通过this关键字将对热水器Heater本身的引用传递了进去 base.Notify(this);           // -- 将本身传递过去 我们再来做个测试 class Program {    static void Main(string[] args) {       Heater heater new Heater();       Screen screen new Screen();       heater.Register(screen);     // 注册显示器       heater.BoilWater();             // 烧水    }}输出为ChinaXian - RealFire 001:Alarm  水快烧开了当前温度98。Alarm  水快烧开了当前温度99。Alarm  水已经烧开了 可以看到和前面完全一样的输出。 推模式和拉模式 的区别 那么大家一定想问使用推模式和拉模式有什么区别呢 推模式的好处是 按需供给想要提供给 Observer端什么数据就将这些数据封装成对象传递给Observer缺点是需要创建自定义的EventArgs对象。拉模式的好处 则是不需要另外定义对象直接将自身的引用传递进去就可以了。但是缺点是我们可能会需要暴露我们不想暴露的内部成员比如本例中的temperature。我们期望将它作为类的内部数据仅提供给显示器。但是使用拉模式你只得为它再提供一个公共的Temperature访问器这样在程序的其他的地方也可以访问到了比如说在Program里。除此以外我们不期望Screen可以进行烧水BoilWater()这一动作但是由于它获得了Heater的引用而BoilWater()方法又是Public公共的所以在Update()方法中也具备了对热水器操作的能力比如调用 BoilWater() 方法。.Net 中没有内置的IObserver和IObservable接口因为在.Net中可以通过委托和事件来完成但是一样面临选择推模式还是拉模式的问题何时使用哪种策略完全依赖于设计者你也可以将两种方式都实现了比如将IObserver接口定义成这样 // 类似微软的实现两个都用 ...void Update(Object sender, BoiledEventArgs e); 注意这里我用得是BoiledEventArgs作为Update()的参数这里显然不够合适如果期望这个接口可以为各种Observer服务而不仅限于烧水这一事件那么最好定义一个基类 EventArgs然后对于各种不同的事件定义不同的EventArgs类再让它们去继承EventArgs。如此可以得到下面的接口定义 void Update(Object sender, EventArgs e); 呵呵看到这里诸君应该都明白了吧微软对这个方法原型定义了一个委托叫做EventHandler public delegate void EventHandler(object sender, EventArgs e); 再谈下去又绕到委托和事件了我们回到主题将本文的内容做个总结吧。 总结 本文我再次使用热水器的例子实现了Observer设计模式但这一次我没有使用委托和事件而是通过经典的GOF方式。我同时还讨论了实现Observer模式时Subject向Observer提供数据值可以采用的两种方式--推模式和拉模式。最后我们对这两种模式进行了一个简单的比较并简要介绍了.Net Framework中采用的方式。 感谢阅读希望这篇文章能给你带来帮助转载于:https://www.cnblogs.com/GeneralXU/archive/2009/09/09/1563117.html
http://www.zqtcl.cn/news/93316/

相关文章:

  • 找做网站的人网站改版 301跳转
  • 网站备案一次就可以了吧营销管理培训课程
  • 怎么做网站背景专做民宿预定的网站
  • wordpress安装谷歌分析代码建网站seo
  • 百度外卖网站建设与维护方法建设 银行网网站
  • 小程序开发定制开发上海优化价格
  • 来宾住房和城乡建设局网站做外贸推广要做哪些平台
  • 无锡建设网站制作wordpress 知乎
  • 动漫网站源码免费怎么怎么做网站
  • 和两个黑人同时做网站中工互联网站建设
  • windows10PHP 网站建设app应用分发平台开发
  • 中唯建设工程有限公司网站做网站页面对PS切图
  • 个人网页制作成品欣赏seo网站沙盒期
  • 亚马逊站外推广网站怎么做制作营销网站模板免费下载
  • 加拿大网站后缀设计师互联网
  • 做物流的网站有哪些内容共同建设网站心得
  • 主题资源网站建设什么网站做污水处理药剂的好
  • 河北建设厅网站修改密码在哪58同城宿迁二手房
  • 淘宝联盟的购物网站怎么做免费网站模板素材
  • 淄博市网站云平台长沙seo 优化选智投未来no1
  • 手机网站导航模板wordpress子域名设置
  • 济南市网站推广公司甘肃网站建设方案优化
  • 网站排名西安工商所什么网站可做年报
  • 网站怎样做反向链接哪个网站可以做代码题目
  • opencart做外贸网站怎样丽水市城乡建设局网站
  • 黑色网站配色typora wordpress
  • 哪个网站做的系统好用吗开一家网站建设公司好
  • 高仿服装网站建设高端网站建设服务
  • 网站怎么做前后台存取旅游网站建设的目的与意义是什么意思
  • 网站一年了百度不收录自己做的网站怎么植入erp