初读Observer模式
动机(Motivation)
在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系”--一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象(都将得到通知。如果这样的依赖关系过于紧密,将合软件不能很好地抵御变化。
细节依赖抽象,不稳定的去依赖稳定的,软件就会变得松耦合。
使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。
意图(Intent)
定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
public class ATM
{
BankAccount bakAccount;
void process(int data);
{
bakAccount.Withdraw(data);
}
}
public class BankAccount
{
Emailer emailer; //强依赖关系
Mobile mobile; //强依赖关系
public void Withdraw(int data)
{
//...
emailer.SendEmail(userEmail);
mobile.SendNotification(phoneNumber);
}
}
public class Emailer
{
public void SendEmail(string toAddress)
{
//...
}
}
public class Mobile
{
public void SendNotification(string phoneNumber)
{
//...
}
}
Observer模式的几个要点
l 使用面向对象的抽象,Observer模式使得我们可以独立地改变目标与观察者,从而使二者之间的依赖关系达致松耦合。(这里的改变,是指扩展)
l 目标发送通知时,无需指定观察者,通知(可以携带通知信息作为参数)会自动传播。观察者自己决定是否需要订阅通知,目标对象对此一无所知。
l 在C#的event中,委托充当了抽象的Observer接口,而提供事件的对象充当了目标对象。委托是比抽象Observer接口更为松耦合的设计。
public abstract class Subject
{
ArrayList<IAccountObserver> observerList = new ArrayList<IAccountObserver>();
protected virtual void Notify(UserAccountArgs args) <-参数
{
[提]/*
UserAccountArgs args = new UserAccountArgs();
//...
*/
foreach(IAccountObserver observer in observerList)
{
observer.Update(args);
}
}
public void AddObserver(IAccountObserver observer)
{
observerList.Add(observer);
}
public void RemoveObserver(IAccountObserver observer)
{
observerList.Remove(observer);
}
}
[改]public class BankAccount : Subject
{
public void Withdraw(int data)
{
//...
UserAccountArgs args = new UserAccountArgs();
//... 设置属性
Notify();
}
}
class App
{
public static void Main()
{
Subject subject = new Subject();
IObserver observer = new Emailer();
subject.Add(observer);
//...
bankAccount.Withdraw(234);
}
}
public interface IAccountObserver
{
void Update(UserAccountArgs args);
}
public class Emailer : IAccountObserver
{
public void Update(UserAccountArgs args)
{
//...
string toAddress = args.ToAddress;
}
}
public class Mobile : IAccountObserver
{
public void Update(UserAccountArgs args)
{
//...
string mobileNumber = args.MobileNumber;
}
}
public class BankAccount :Subject
{
//IAccountObserver emailer; //弱依赖关系(依赖的是一个接口,面向接口编程是因为接口稳定嘛)
ArrayList<IAccountObserver> observerList = new ArrayList<IAccountObserver>();
public void Withdraw(int data)
{
//...
/*[提]UserAccountArgs args = new UserAccountArgs(); */
//...
//emailer.Update(userEmail);
[提]/*
foreach(IAccountObserver observer in observerList)
{
observer.Update(args);
}
*/
}
[提]/*
public void Notify(UserAccountArgs args) <-参数
{
[提]/*
UserAccountArgs args = new UserAccountArgs();
//...
*/
foreach(IAccountObserver observer in observerList)
{
observer.Update(args);
}
}
public void AddObserver(IAccountObserver observer)
{
observerList.Add(observer);
}
public void RemoveObserver(IAccountObserver observer)
{
observerList.Remove(observer);
}
*/
}
难点1:Update中参数的抽取,这里在接口中定义UserAccountArgs
难点2:对于BankAccount类中多个IAccountObserver对象的处理,因为有两个,一个emailer,一个mobile,所以很容易想到用集合的方式来处理
难点3:要让BankAccount依赖稳定的,那么还要抽提出foreach(IAccountObserver observer in observerList)这一部分
public delegate void AccountChangeEventHandler( //-->接口 表达一个约定,在强类型语言下要保证语法安全
object sender,
AccountChangeEventArgs args); //委托最后会被转化成一个类,其本身就是一个类,内部可以理解为一个链表
//一个Add,Remove方法,相当于前面的Subject中的Arraylist<>和Add,Remove
//interface的约定比delegate更严格
public class BankAccount : Subject
{
public event AccountChangeEventHandler AccountChange;
public void Withdraw(int data)
{
//...
UserAccountArgs args = new UserAccountArgs();
//..
OnAccountChange(args);
}
protected virtual OnAccountChange(AccountChangeEventArgs args) //相当于之前NOtify方法
{
if(AccountChange != null) //这里需要一个小小的CHECK,担心事件为空,之前OO模型不
//需要Check,是因为它Subject一上去就给New ArrayList了
{
AccountChange(args); //在一个委托类型的字段上去调用一个方法的时候,它会按照多波
//委托的方式一直调用下去,把委托链上的所有委托都调用一遍
//其实就是一个遍历,跟前OO的foreach
}
}
}
public class Emailer //这里的Emailer没有实现接口,其实这里是一个隐含的假设接口,
{
public void Update(object sender,UserAccountArgs args) //这里在写Update方法的时候实际上就是实现
{ //了上面委托定义的接口
//... //这里方法名可以任意,事件没有方法限制
string toAddress = args.ToAddress;
}
}
public class Mobile : IAccountObserver
{
public void Update(UserAccountArgs args)
{
//...
string mobileNumber = args.MobileNumber;
}
}
class App
{
public static void Main()
{
BankAccount bankAccount = new BankAccount();
Emailer emailer = new Emailer();
banAccount.AccountChange +=
new AccountChangeEventHandler(emailer.Update); // 背后就是实现Add
//...
bankAccount.Withdraw(234);
}
}
只要delegate稳定下来,整个都会稳定,delegate可以把它充当接口来对象
接口只要:一稳定,二保证契约这个合同
源自:李建忠老师