编辑推荐: |
本文主要介绍设计模式之代理模式,希望对您有所帮助。
本文来源于网络,由火龙果软件Linda编辑,推荐。 |
|
设计模式之代理模式(十)
一、引出模式
代理,在生活我我们非常常见。比如在中学时期,总追过女孩子吧!身为码农的我们,没追过也没关系,总看过别人追过女孩子吧!这里出现了3个重要人物,甲想追求乙,但甲又不认识乙,幸好乙的闺蜜丙是甲的小学同学,于是呢,甲就通过丙给乙传递一些纸条,情书之类的。在这里面同学丙就充当着一个代理的角色,甲无法直接找到乙说话,只能通过丙给乙传递消息。
二、认识模式
1.定义:
代理模式为目标对象提供一种代理,并由该代理对象控制对这个目标对象的访问。
2.模式结构和说明
Subject:目标接口,定义代理和具体目标对象的接口,这样就可以在任何地方使用具体目标对象的地方使用代理对象。
RealSubject:具体的目标对象,真正实现目标接口的功能。
Proxy:代理对象,主要有如下功能:
* 实现和具体的目标对象一样的接口,这样可以使用代理来代替具体的目标对象
* 保存一个指向具体目标对象的引用,可以在需要时调用具体的目标对象。
* 可以控制对具体目标对象的访问,并可以负责创建和删除它。
3.示例代码
class Program { static void Main(string[] args) { Subject subject = new Proxy(new RealSubject()); subject.Request(); Console.ReadKey(); } } /// <summary> /// 定义具体的目标对象和代理公有接口 /// </summary> public abstract class Subject { public abstract void Request(); } /// <summary> /// 具体的目标对象,是真正被代理的对象 /// </summary> public class RealSubject : Subject { public override void Request() { //执行真正要实现的功能 Console.WriteLine("调用真实对象"); } } /// <summary> /// 代理对象 /// </summary> public class Proxy : Subject { //持有被代理的具体目标对象 private RealSubject subject = null; //注入具体被代理的目标对象 public Proxy(Subject subject) { this.subject = (RealSubject) subject; } public override void Request() { //转调之前可以做一些事情,比如权限判断等 //转调具体目标对象的方法 subject.Request(); //转调之后也可以做一些事情 }
|
三、理解模式
1.模式功能:
代理模式通过创建一个代理对象,用这个对象去代表真实对象,当客户端操作这个给代理对象时,实际上功能最终还是会有真实对象来实现,只不过是通过代理操作的。
正是因为代理对象夹在客户端与真实对象之间,相当于中转站,所以在中转前后可以做很多事情,比如权限判断。
2.代理分类:
* 远程代理:为一个位于不同的地址空间的对象提供一个局域代表对象。这个不同的地址空间可以是在本机器中,也可是在另一台机器中。远程代理又叫做大使(Ambassador)。
* 虚拟代理:根据需要创建一个资源消耗较大的对象,使得此对象只在需要时才会被真正创建。
* Copy-on-Write代理:虚拟代理的一种。把复制(克隆)拖延到只有在客户端需要时,才真正采取行动。
* 保护代理:控制对一个对象的访问,如果需要,可以给不同的用户提供不同级别的使用权限。
* Cache代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。
* 防火墙代理:保护目标,不让恶意用户接近。
* 同步化代理:使几个用户能够同时使用一个对象而没有冲突。
* 智能指引:当一个对象被引用时,提供一些额外的操作,比如将对此对象调用的次数记录下来等。
在所有种类的代理模式中,虚拟(Virtual)代理、远程(Remote)代理、智能引用代理(Smart Reference Proxy)和保护(Protect or Access)代理是最为常见的代理模式。
3.具体目标对象和代理对象的关系
从模式图来看,好像一个具体目标类就有一个代理类,其实不是这样的。如果代理类能完全通过接口来操作它所代理的目标对象,那么代理对象就不需要知道具体的目标对象,这样就无需为每一个具体目标对象都创建一个代理类。
4.保护代理细说
保护代理是一种控制对原始对象访问的代理,多用于对象应该有不同的访问权限的时候。保护代理会检查调用者是否具有请求所必需的访问权限,如果没有,就不会调用目标对象,从而实现对目标对象的保护。
示例:现在有个订单系统,一旦订单被创建,那么只有创建人才能需改和删除订单。
class Program { static void Main(string[] args) { #region 保护代理
#endregion OrderApi orderApi = new ProxyOrder(new Order("设计模式", 100, "zxj"));
orderApi.SetProductName("哈哈哈", "zxj"); Console.ReadKey();
}
}
#region 保护代理 /// <summary>
/// 订单接口
/// </summary>
public abstract class OrderApi
{
public abstract void SetProductName(string ProductName, string ProductUser);
} /// <summary>
/// 订单
/// </summary>
public class Order : OrderApi
{
/// <summary>
/// 产品名称
/// </summary>
public string ProductName { get; set; } /// <summary>
/// 产品数量
/// </summary>
public int ProductNum { get; set; } /// <summary>
/// 操作员
/// </summary>
public string ProductUser { get; set; } public Order(string PeoductName, int ProductNum, string ProductUser)
{
this.ProductName = PeoductName;
this.ProductNum = ProductNum;
this.ProductUser = ProductUser;
} public override void SetProductName(string ProductName, string ProductUser)
{
throw new NotImplementedException();
}
} public class ProxyOrder : OrderApi
{
private Order order; public ProxyOrder(Order order)
{
this.order = order;
} public override void SetProductName(string ProductName, string ProductUser)
{
if (order.ProductName != null && order.ProductUser.Equals(ProductUser))
{
Console.WriteLine("{0}可以修改", ProductUser);
}
else
{
Console.WriteLine("{0}无权修改", ProductUser);
}
}
}
#endregion
|
5.模式特点
* 远程代理:隐藏了一个对象存在不同的地址空间的事实。
* 虚代理:可以根据需要创建“大”对象,只要到必须创建对象的时候,虚代理才会创建对象。
* 保护代理:可以在访问一个对象的前后,附加执行很多操作,除了权限控制之外,还可以进行很多业务,也就是说,可以通过代理来给目标对象增加功能。
* 智能指引:和保护代理类似,可以在访问一个对象前后附加其他操作,如果存有保护性质的,就算保护代理,其他就算是智能指引。
6.代理模式的选用
* 需要为一个对象在不同的地址空间提供局部代表的时候,可以使用远程代理。
* 需要按照需要创建开销很大的时候,可以使用虚代理。
* 需要控制原始对象的访问的时候,可以使用保护代理。
* 需要在访问对象执行一些附加操作的时候,可以使用智能代理。
7.模式本质
控制对象访问
代理模式通过代理目标对象,把代理对象插入到客户和目标对象之间,从而为客户和目标引入一定的间接性。正是这个间接性,给力代理对象很大的活动空间,可以在目标对象前后,附加很多操作,从而实现新的功能或是扩展。
从实现哈桑看,代理模式主要使用对象的组合和委托,尤其是在静态代理的实现里面。也可以采用对象继承的方式来实现代理,比如上面的保护代理示例
8.相关模式
* 代理模式与适配器模式
这两个模式有一定的相似性,它们都为另一个对象提供间接性访问,而且都是从自身以外的一个借口想这个对象转发请求。
但是在功能上,适配器模式主要用来解决接口之间不匹配的问题。它通常是为所适配的对象提供一个不同的接口;而代理模式会实现和目标对象相同的接口。
* 代理模式和装饰模式
这两个模式从实现上相似,都是在转掉调其他对象的前后执行一定的功能。
但是它们的功能和目的是不同的,装饰模式目的是为了让你不生成子类就可以给对象添加指着,也就是为了动态的添加功能;而代理模式的主要目的是控制对对象的访问。
设计模式之中介者模式(十一)
一、引出模式
在生活中,当电脑缺少了一块主板,那会怎么样?如果有人这样问我的话,我就会马上跳出来说“这电脑肯定报废了”,当然这不是重点。假如少了主板电脑还可以用的话,想想,里面的CPU、显卡、声卡、光驱、硬盘等等,不是就要我们自己用线把它们连起来。想想就觉得头疼,那么现在你觉得主板在电脑里扮演着什么角色呢?
在软件的开发过程中,势必会碰到这样一种情况,多个类或多个子系统相互交互,而且交互很繁琐,导致每个类都必须知道他需要交互的类,这样它们的耦合会显得异常厉害。牵一发而动全身又不木有啊!
好了,既然问题提出来了,那有请我们这期的主角——中介者模式出场吧!
二、认识模式
1.模式定义
用一个中介者对象来封装一系列的对象交互。中介者使得各对象不需要显式地相互引用,从而使其松散耦合,而且可以独立地改变它们之间的交互。
2.解决思路
出现问题的根本原因在于多个对象需要相互交互,导致了紧密耦合,不利于对象的修改和维护。
中介者模式的解决思路,就和“主板”一样,通过引入一个中介对象,让其他对象都只和这个中介者对象交互,而中介者对象是知道怎样和其他对象交互。这样一来,对象直接的交互关系就没有了,从而实现了松散耦合。
对于中介对象而言,所有相互交互的对象,都视为同事类,中介对象就是用来维护各个同事对象之间的关系,所有的同事类都只和中介对象交互,也就是说,中介对象是需要知道所有的同事对象的。
当一个同事对象自身发生变化时,它是不知道会对其他同事对象产生什么影响,它只需要通知中介对象,“我发生变化了”,中介对象会去和其他同事对象进行交互的。这样一来,同事对象之间的依赖就没有了。
有了中介者之后,所有的交互都封装在了中介对象里面,各个对象只需要关心自己能做什么就行,不需要再关系做了之后会对其他对象产生什么影响,也就是无需再维护这些关系了。
3.模式结构
Mediator:中介者接口。在里面定义了各个同事之间相互交互所需要的方法,可以是公共的方法,如Change方法,也可以是小范围的交互方法。
ConcreteMediator:具体的中介者实现对象。它需要了解并为维护每个同事对象,并负责具体的协调各个同事对象的交互关系。
Colleague:同事类的定义,通常实现成为抽象类,主要负责约束同事对象的类型,并实现一些具体同事类之间的公共功能,比如,每个具体同事类都应该知道中介者对象,也就是每个同事对象都会持有中介者对象的引用,这个功能可定义在这个类中。
ConcreteColleague:具体的同事类,实现自己的业务,需要与其他同事对象交互时,就通知中介对象,中介对象会负责后续的交互。
4.模式示例代码
internal class Program
{
private static void Main(string[] args)
{
}
}
/// <summary>
/// 同事类的抽象父类
/// </summary>
public abstract class Colleague
{
/// <summary>
/// 持有中介者对象,每一个同事类都知道它的中介者对象
/// </summary>
private Mediator mediator;
/// <summary>
/// 构造方法,传入中介者对象
/// </summary>
/// <param name="mediator">中介者对象</param>
protected Colleague(Mediator mediator)
{
this.mediator = mediator;
}
/// <summary>
/// 获取当前同事类对应的中介者对象
/// </summary>
/// <returns>对应的中介者对象</returns>
public Mediator GetMediator()
{
return mediator;
}
}
/// <summary>
/// 具体的同事类A
/// </summary>
public class ConcreteColleagueA : Colleague
{
/// <summary>
/// 调用父类的构造函数
/// </summary>
/// <param name="mediator"></param>
public ConcreteColleagueA(Mediator mediator):base(mediator)
{
}
/// <summary>
/// 执行某些业务功能
/// </summary>
public void SomeOperation()
{
//在需要跟其他同事通信的时候,通知中介者对象
base.GetMediator().Change(this);
}
}
/// <summary>
/// 具体的同事类B
/// </summary>
public class ConcreteColleagueB : Colleague
{
/// <summary>
/// 调用父类的构造函数
/// </summary>
/// <param name="mediator"></param>
public ConcreteColleagueB(Mediator mediator)
: base(mediator)
{
}
/// <summary>
/// 执行某些业务功能
/// </summary>
public void SomeOperation()
{
//在需要跟其他同事通信的时候,通知中介者对象
base.GetMediator().Change(this);
}
}
/// <summary>
/// 中介者,定义各个同事对象通信的接口
/// </summary>
public interface Mediator
{
void Change(Colleague colleague);
}
/// <summary>
/// 具体的中介者实现
/// </summary>
public class ConcreteMediator
{
/// <summary>
/// 持有并维护同事A
/// </summary>
private ConcreteColleagueA colleagueA;
/// <summary>
/// 持有并维护同事B
/// </summary>
private ConcreteColleagueB colleagueB;
/// <summary>
/// 设置中介者需要了解并维护的同事A对象
/// </summary>
/// <param name="colleague">同事A对象</param>
public void SetConcreteColleagueA(ConcreteColleagueA colleague)
{
colleagueA = colleague;
}
/// <summary>
/// 设置中介者需要了解并维护的同事B对象
/// </summary>
/// <param name="colleague">同事B对象</param>
public void SetConcreteColleagueB(ConcreteColleagueB colleague)
{
colleagueB = colleague;
}
public void Changed(Colleague colleague)
{
//某个同事类发生了变化,通常需要与其他同事交互
//具体协调相应的同事对象来实现协作行为
}
|
三、理解模式
1.中介者模式的功能
中介者的功能就是封装对象之间的交互。如果一个对象的操作会引起其他相关对象的变化,而这个对象又不希望自己来处理这些关系,那么就可以去找中介者,让它来处理这些麻烦的关系。
2.需要Mediator接口吗?
这一问题,首先得弄清楚接口用来干嘛的?接口是用来“封装隔离”的,Mediator接口用来封装中介者对象,使得中介者对象的客户对象跟具体的中介者实现对象分离开。
如果只有一个中介者对象,预期中也没打算扩展,那就可以不定义Mediator接口。
3.同事关系
在标准的中介者模式中,将使用中介者对象来交互的那些对象称为同事类,这是要求这些类都要继承自相同的类,也就是说它们算是兄弟对象。
4.同事和中介者的关系
在模式中,当一个同事对象发生变化时,需要主动通知中介者,让中介者去处理和其他同事对象的相关交互。
这就导致同事对象与中介对象必须有关系,同事对象需要知道中介对象,以便通知;中介对象需要知道同事对象,一边交互。所有它们的关系是相互依赖的。
5.如何实现同事和中介者的通信
一种方式是在Mediator接口中定义一个特殊的通知接口,作为一个通用的方法,让各个同事来调用这个方法。
另一种就是采用观察者模式,把Mediator实现成为观察者,各个同事对象实现为Subject.这些同事对象发生改变时,就会通知Mediator。
6.广义中介者
在实际开发中,经常会简化中介者模式,来是开发变得简单,比如有如下的简化。
1、通常会去掉同事对象的父类,这样可以让任意的对象,只需要有交互,就可以成为同事
2、通常不定义Mediator接口,把具体的中介者对象实现成为单例
3、同事对象不再持有中介者,而是在需要的时候直接获取中介者对象并调用;中介者也不再持有同事对象,而是在具体处理方法里面去创建,或获取,或从数据传入需要的同事对象。
经过这样的简化、变形的情况称为广义中介者。
7.何时选用中介者模式
如果一组对象之间的通信方式比较复杂,导致相互依赖,结构混乱,可以采用中介者模式
如果一个对象引用很多对象,并且跟这些对象交互,导致难以服用该对象
8.模式本质
中介者模式的本质在于“封装交互”
中介者模式的目的,就是封装多个对象的交互,这些交互的多在中介者对象里实现。
只要是实现封装对象的交互,就可以使用中介者模式,不必拘泥于模式结构。
9.相关模式
中介者模式和外观模式
外观模式多用于封装一个子系统内部的多个模块,目的是向子系统外部提供简单易用的接口。
中介者模式是提供多个平等的同事对象之间交互关系的封装,一般是用在内部实现上。
中介者模式和观察者模式
这两个模式可以组合使用。
中介者模式可以组合使用观察者模式,来实现当同事对象发生改变时,通知中介对象,让中介对象去进行与其他相关对象的交互。
最后是一个中介者模式的实例:打开电脑看电影
比如有如下的交互:
1.首先光驱读取光盘上的数据,然后告诉主板,它的状态已经改变了。
2.主板得到光驱的数据,将数据交给CPU进行分析处理。
3.CPU处理完,将数据分成了视频数据和音频数据,通知主板,已将处理好了。
4.主板得到数据,将数据交给显卡和声卡进行输出。
代码示例:
class Program { static void Main(string[] args) { //1.创建中介者——主板对象 ConcreteMediator mediator = new ConcreteMediator();
//2.创建同事类
CDDriver cd = new CDDriver(mediator);
CPU cpu = new CPU(mediator);
VideoCard videocard = new VideoCard(mediator);
SoundCard soundcard = new SoundCard(mediator);
//3.让中介知道所有的同事
mediator.SetCDDriver(cd);
mediator.SetCPU(cpu);
mediator.SetVideoCard(videocard);
mediator.SetSoundCard(soundcard);
//4.开始看电影
cd.ReadCD();
Console.Read();
}
}
#region 同事抽象类
/// <summary>
/// 同事抽象类
/// </summary>
public abstract class Colleague
{
//持有中介者对象的引用,因为每个同事类都应该知道中介者对象
private Mediator mediator;
//构造函数,传入中介者对象
public Colleague(Mediator mediator)
{
this.mediator = mediator;
}
//得到当前同事类的中介者对象
public Mediator GetMediator()
{
return this.mediator;
}
}
#endregion
#region 同事具体类
/// <summary>
/// 光驱类
/// </summary>
public class CDDriver : Colleague
{
public CDDriver(Mediator mediator)
: base(mediator)
{
}
/// <summary>
/// 光驱读取出来的数据
/// </summary>
private string Data = null;
/// <summary>
/// 获取光驱读取出来的数据
/// </summary>
/// <returns></returns>
public string GetData()
{
return this.Data;
}
public void ReadCD()
{
//逗号前是视频数据,逗号后是声音数据
this.Data = "这是视频数据,这是声音数据";
//通知主板,自己的状态反生了改变
this.GetMediator().Change(this);
}
}
/// <summary>
/// CPU类
/// </summary>
public class CPU : Colleague
{
public CPU(Mediator mediator)
: base(mediator)
{
}
/// <summary>
/// 分解出来的视频数据
/// </summary>
private string videioData = null;
/// <summary>
/// 分解出来的视频数据
/// </summary>
private string soundData = null;
/// <summary>
/// 获取分解出来的视频数据
/// </summary>
/// <returns></returns>
public string GetVideioData()
{
return this.videioData;
}
/// <summary>
/// 获取分解出来的声音数据
/// </summary>
/// <returns></returns>
public string GetSoundData()
{
return this.soundData;
}
/// <summary>
/// 处理数据
/// </summary>
public void ExecuteData(string data)
{
string[] ss = data.Split(',');
this.videioData = ss[0];
this.soundData = ss[1];
//通知主板,CPU工作已完成
this.GetMediator().Change(this);
}
}
/// <summary>
/// 显卡类
/// </summary>
public class VideoCard : Colleague
{
public VideoCard(Mediator mediator)
: base(mediator)
{
}
/// <summary>
/// 显示视频数据源
/// </summary>
public void ShowData(string data)
{
Console.WriteLine("您正在看:" + data);
}
}
/// <summary>
/// 声卡类
/// </summary>
public class SoundCard : Colleague
{
public SoundCard(Mediator mediator)
: base(mediator)
{
}
/// <summary>
/// 显示声音数据源
/// </summary>
public void ShowData(string data)
{
Console.WriteLine("您正在听:" + data);
}
}
#endregion
#region 中介者抽象类
/// <summary>
/// 中介者抽象类
/// </summary>
public abstract class Mediator
{
public abstract void Change(Colleague colleague);
}
#endregion
#region 中介者具体类
/// <summary>
/// 中介者具体类
/// </summary>
public class ConcreteMediator : Mediator
{
private CDDriver cdDriver;
private CPU cpu;
private VideoCard video;
private SoundCard sound;
public void SetCDDriver(CDDriver cdDriver)
{
this.cdDriver = cdDriver;
}
public void SetCPU(CPU cpu)
{
this.cpu = cpu;
}
public void SetVideoCard(VideoCard video)
{
this.video = video;
}
public void SetSoundCard(SoundCard sound)
{
this.sound = sound;
}
public override void Change(Colleague colleague)
{
if (colleague == cdDriver)
{
openCDAndReadData((CDDriver)colleague);
}
else if (colleague == cpu)
{
OpenCPU((CPU)colleague);
}
}
/// <summary>
/// 打开CD,并读取数据
/// </summary>
/// <param name="cs"></param>
private void openCDAndReadData(CDDriver cs)
{
//获取光驱读取的数据
string data = cdDriver.GetData();
//把这些数据传递给CPU进行处理
this.cpu.ExecuteData(data);
}
/// <summary>
/// CPU处理
/// </summary>
/// <param name="cpu"></param>
private void OpenCPU(CPU cpu)
{
//获取数据
string videoData = cpu.GetVideioData();
string soundData = cpu.GetSoundData();
//显示数据
this.video.ShowData(videoData);
this.sound.ShowData(soundData);
}
}
#endregion |
设计模式之观察者模式(十二)
一、引出模式
在生活中,观察者模式非常的常见,比如到邮局、报社订阅报纸,比如QQ邮箱订阅,在比如你玩微博,关注了某“大婶”,“大婶”发布消息时,你也会相应的收到信息。
在软件开发中,就是这么一种情况。当一个对象的状态发生改变时,如何让依赖于它的所有对象得到通知,并进行相应的处理?
二、认识模式
1.模式定义
定义对象间一种一对多的依赖关系。当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
关键词:一对多,状态改变,得到通知,自动更新
2.解决思路
我们来看下订阅报纸的例子。对于报社来说,它开始是不知道有多少个订阅者来订阅报纸,因此,报社是需要维护一个订阅者的列表,这样,当新一期报纸出版时,就可以将报纸顺利地发到订阅者手里。对于订阅者来说,一份报纸应该会有多个订阅者进行订阅。
这样就出现了典型的一对多的对象关系,一份报纸对象,对应着多个订阅者对象,当报纸对象改变时,是需要通知多个订阅者的。那么怎么来维护它们之间的关系呢?
观察者模式正好可以处理这样一种情况。观察者模式把多个订阅者称为观察者(Observee),多个观察者观察的对象称为目标:(Subject)。
3.模式结构
Subject:目标对象,通常具有如下功能:
1.一个目标可以被多个观察者观察。
2.目标提供对观察者的注册与退订的维护。
3.当目标状态发生变化时,目标负责通知所有注册的、有效的观察者。
Observer:定义观察者的接口,提供目标通知时对应的更新方法,这个更新方法会进行相应的业务处理,可以在这个方法里面回调目标对象,以获取目标对象的数据。
ConcreteSubject:具体的目标实现对象,用来维护目标状态,当目标对象的状态发生变化时,通知所有的注册的、有效的观察者,让观察者进行相应的业务处理。
ConcreteObserver:观察者的具体实现对象,用来接收目标的通知,并进行相应的后续处理。
4.模式示例代码
class Program { static void Main(string[] args) { } }
/// <summary>
/// 观察者接口,定义一个更新的接口给那些在目标发生改变的时候被通知的对象
/// </summary>
public interface Observer
{
/// <summary>
/// 更新的接口
/// </summary>
/// <param name="subject">传入目标对象,好获取相应的目标对象的状态</param>
void Update(Subject subject);
}
/// <summary>
/// 目标对象,它知道观察它的观察者,并提供注册和删除观察者的接口
/// </summary>
public abstract class Subject
{
/// <summary>
/// 用来保存注册的观察者对象
/// </summary>
private List<Observer> observerList = new List<Observer>();
/// <summary>
/// 注册观察者对象
/// </summary>
/// <param name="observer">观察者对象</param>
public void Attach(Observer observer)
{
observerList.Add(observer);
}
/// <summary>
/// 删除观察者对象
/// </summary>
/// <param name="observer">观察者对象</param>
public void Detach(Observer observer)
{
observerList.Remove(observer);
}
/// <summary>
/// 通知所有注册的观察者对象
/// </summary>
public void NoyifyObserver()
{
foreach (var observer in observerList)
{
observer.Update(this);
}
}
}
/// <summary>
/// 具体观察者对象,实现更新的方法,使自身的状态和目标的状态保持一致
/// </summary>
public class ConcreteObserver:Observer
{
/// <summary>
/// 示意,观者者的状态
/// </summary>
private string subjectSate;
/// <summary>
/// 具体的更新实现
/// </summary>
/// <param name="subject"></param>
public void Update(Subject subject)
{
//这里可能需要更新观察者的状态,使其与目标的状态保持一致
subjectSate = ((ConcreteSubject) subject).GetSubject();
}
}
/// <summary>
/// 具体的目标对象,负责把有关状态存入到相应的观察者对象,并在自己状态发生改变时,通知各个观察者
/// </summary>
public class ConcreteSubject : Subject
{
/// <summary>
/// 示意,目标对象的状态
/// </summary>
private string subjectState;
/// <summary>
/// 获取目标对象的状态
/// </summary>
/// <returns></returns>
public string GetSubject()
{
return subjectState;
}
public void SetSubjectSate(string subjectState)
{
this.subjectState = subjectState;
//状态发生了改变,通知各个观察者
base.NoyifyObserver();
}
} |
5.模式订阅报纸实例代码
class Program { static void Main(string[] args) { //创建一个报纸,作为被观察者 NewsPaper news=new NewsPaper();
//创建阅读者,也就是观察者
Reader reader1=new Reader();
reader1.Name = "张三";
Reader reader2=new Reader();
reader2.Name = "李四";
Reader reader3=new Reader();
reader3.Name = "王五";
//注册阅读者
news.Attach(reader1);
news.Attach(reader2);
news.Attach(reader3);
//要出报纸啦,即状态改变了
news.SetContent("观察者模式");
Console.ReadKey();
}
}
/// <summary>
/// 观察者,比如报纸的读者
/// </summary>
public interface Observer
{
void Update(Subject subject);
}
/// <summary>
/// 目标对象,作为被观察者
/// </summary>
public abstract class Subject
{
/// <summary>
/// 用来保存注册的观察者对象,也就是报纸的订阅者
/// </summary>
private List<Observer> observers=new List<Observer>();
/// <summary>
/// 报纸的读者需要先向报社订阅,先要注册
/// </summary>
/// <param name="observer">报纸的读者 </param>
public void Attach(Observer observer)
{
observers.Add(observer);
}
/// <summary>
/// 报纸的读者可以取消订阅
/// </summary>
/// <param name="observer">报纸的读者</param>
public void Detach(Observer observer)
{
observers.Remove(observer);
}
/// <summary>
/// 当每期报纸印刷出来后,就要迅速的主动的被送到读者的手中,相当于通知读者,让他们知道
/// </summary>
public void NotifyObserver()
{
foreach (var observer in observers)
{
observer.Update(this);
}
}
}/// <summary>
/// 报纸对象,具体的目标实现
/// </summary>
public class NewsPaper : Subject
{
/// <summary>
/// 报纸的具体内容
/// </summary>
public string Content { get; private set; }
public void SetContent(string content)
{
Content = content;
Console.WriteLine("报社出版报纸啦!本期的主题是:" + Content);
//内容有了,说明又出报纸了,那就通知所有的读者
base.NotifyObserver();
}
}
/// <summary>
/// 真正的读者,为了简单就描述一下姓名
/// </summary>
public class Reader:Observer
{
/// <summary>
/// 读者的姓名
/// </summary>
public string Name { get; set; }
public void Update(Subject subject)
{
//这是采用拉的方式
Console.WriteLine(Name+"收到报纸了,本期的主题是:"+((NewsPaper)subject).Content);
}
} |
三、理解模式
1.目标和观察者之间的关系
按照模式的定义,目标和观察者之间是典型的一对多关系。
但是,不是就是说观察者就要多个,观察者只有一个也是可以的,只是变成了一对一关系。
同样的,一个观察者是可以观察多个目标的,如果这样的话,就要注意这个更新通知来自于哪个目标对象。
一般情况下,观察者应该为不同的目标定义不同的回调方法,这样实现最简单,不需要在update方法内部进行区分。
2.单向依赖
在观察者模式中,观察者和目标是担心依赖的,只有观察者依赖于目标对象,而目标对象是不依赖与观察者的。
它们之间联系的主动权是掌握在目标对象手里的,只有目标知道什么时候需要通知观察者。在整个过程中,观察者始终是被动的,被动的等待目标的通知,等待目标对象传值给它。
3.基本的实现说明
1.l具体的目标实现对象要能维护观察者者的注册信息,最简单的方法莫过于如前面例子那样,采用一个集合来保存观察者的注册信息。
2.具体的目标对象需要维护一起通知的状态,一般是目标的自身状态。
3.具体的观察者实现对象需要能接收目标的通知,能够接收目标传递的数据,或主动去获取目标的数据,并进行后续处理。
4.如果一个观察者观察多个目标,那么在观察者的更新方法里面,需要去判断是来之哪个目标的通知。一种方案是在Update方法,通过参数来区分,另一种简单方案就是直接定义不同的回调方法。
4.推模型和拉模型
1、推模型
目标对象主动向观察者推送目标的详细信息,不管观察者需不需要,推送的信息通常是目标对象的全部或部分数据,相当与广播通信。
2、 拉模型
目标对象在通知观察者的时候,值传递少量信息。如果观察者需要更具体的信息,有观察者主动到目标对象中获取,相当于是观察者从目标对象中拉数据。一般这种模型的实现中,会把目标对象自身通过Update方法传递给观察者,这样,观察者需要数据时,就可以通过这个引用获取。上面订阅报纸的例子就是一个拉模型。
5.观察者模式的优点
1、 观察者模式实现了观察者和目标之间的抽象耦合。
原本目标对在状态改变时,需要直接调用所有的观察者对象,但是,抽象出观察者接口之后,目标和观察者只是抽象层面上的耦合。也就是说,目标只知道观察者的接口,而不是到具体的观察者。
2、 观察者模式实现了动态联动。
由于观察者模式对观察者注册实现管理,那就可以在运行期间,通过动态控制注册的观察者,来控制某个动作的联动范围。
3、 观察者模式支持广播通信。
6.何时选用观察者模式
1.当一个抽象模型有两个方面,其中一个方面的操作依赖与另一方面的状态变化就可以使用观察者模式。
2.如果在更改一个状态的时候,需要同事连带改变其他对象,而且不知道有多少个对象需要被连带改变时,可以选用观察者模式。
3.当一个对象必须通知其它对象,但你又希望这个对象和其它被它通知的对象是松散耦合的,也可以使用观察者模式。
7.观察者模式的本质
观察者模式的本质就是:触发联动。
当修改目标对象的状态时,就会触发相应的通知,然后会循环调用所有注册的观察者对象的相应放哪广发,其实就是相当于联动调用这些观察者的方法。
|