大道至简

无尽
XP agile SE
UT Mock T
  博客园 :: 首页 ::  :: 联系 :: 订阅 订阅 :: 管理

公告

DesignPattern2s——(2)observer

Posted on 2011-08-26 17:29 梅桦 阅读(82) 评论(0) 编辑 收藏

场景:水加热器,如果水温到95以上时,加热器的警报器会响且水温显示屏会显示当前的温度。

实现一:加热行为时,如果水温(状态)发生改变,到95度以上,那会同时调用行为:警报和显示。

public class Heater
{
  
int _temperature;
  
public void BoilWater()
  {
    
for (int i = 0; i < 101; i++)
    {
      
if (i > 95)
      {
        _temperature 
= i;
        MakeAlert(_temperature);
        ShowMessage(_temperature);
      }
    }
  }
  
public void MakeAlert(int iTemperature)
  {
    Console.WriteLine(
"alarm:"+iTemperature);
  }
  
public void ShowMessage(int iTemperature)
  {
    Console.WriteLine(
"current temperature is:" + iTemperature);
  }
}

 

以上实现了这个加热器,现在的情况是:当加热时,且加热到95度以上,那么会调用警报行为和显示行为。如果现在加热器

要添加一个行为:水温到95以上时,要跳动Jump。那么要在这个类中添加这个行为,同时更改BoilWater

通过观察者模式,可以不用修改BoilWater。当加热器水温到达95度以上时,会同时通知三个行为:警报,显示,跳动。

观察者模式包括:subject主题,也就是被观察者;Observer,观察者。它可以描述为一种订阅和发布关系。

在以上场景中,加热器是被观察者,而三个行为是观察者,它们都依赖于被观察者的状态信息;而加热器加热到95度,会调用这三个行为,构成了对这三个行为的依赖。

 

实现二:通过观察者模式实现加热器

先把行为:警报和显示分离开来。

public class Heater
{
    
int _temperature;
    
public void BoilWater()
    {
        
for (int i = 0; i < 101; i++)
        {
            
if (i > 95)
            {
                _temperature 
= i;
            }
        }
    }
}
public class Alert
{
    
public void MakeAlert(int iTemperature)
    {
        Console.WriteLine(
"alarm:" + iTemperature);
    }
}
public class Display
{
    
public void ShowMessage(int iTemperature)
    {
        Console.WriteLine(
"current temperature is:" + iTemperature);
    }
}

警报器类和显示屏类中的行为都依赖于被观察者中的数据状态。观察者中的主题要求能够注册和注销这些行为。这里的注册注销可以动态添加这两个行为到一个行为集体,然后在执行到状态改变时同时调用这些行为。现在尝试把两个行为能够添加到一

个列表集合中。

把行为添加到集合列表

两个行为MakeAlertShowMessage除方法名不同外,签名的其它都相同。所以可以考虑通过委托列表集合来实现。

定义一个委托并修改主题:

public delegate void DelegateHeater(int iTemperature);
public class Heater
{
    List
<DelegateHeater> _list = new List<DelegateHeater>();
    
int _temperature; 

    
public void BoilWater()
    {
        
for (int i = 0; i < 101; i++)
        {
            
if (i > 95)
            {
                _temperature 
= i;
                Notify();
            }
        }
    }
    
public void Notify()
    {
        
foreach (DelegateHeater heater in _list)
            heater(_temperature);
    }
    
public void Registe(DelegateHeater dheater)
    {
        _list.Add(dheater);
    }
    
public void RegisteCancel(DelegateHeater dheater)
    {
        _list.Remove(dheater);
    }
}

使用加热器:

public void Test_5_heater_2()
{
    Heater heater 
= new Heater();
    
//警报
    Alert aa=new Alert();
    
//显示
    Display dd = new Display(); 

    
//注册两个行为
    heater.Registe(new DelegateHeater(aa.MakeAlert));
    heater.Registe(
new DelegateHeater(dd.ShowMessage));
    heater.BoilWater(); 

    Console.WriteLine(
"************************************");

    
//显示器坏了,注销显示行为
    heater.RegisteCancel(new DelegateHeater(dd.ShowMessage));
    heater.BoilWater();
}

这样可以随意注册和注销加热器的行为。

现在实现了观察者模式,通过委托列表集合来保存注册的行为。委托类型可以把多个方法绑定到一个委托。在使用时,需要定

义委托类型实例,可以考虑把委托类型定义到主题(水加热器内部)并进行封装。

public class Heater
{
    
public delegate void DelegateHeater(int iTemperature);
    
public DelegateHeater _selfHeater;
    
int _temperature; 

    
public void BoilWater()
    {
        
for (int i = 0; i < 101; i++)
        {
            
if (i > 95)
            {
                _temperature 
= i;
                _selfHeater(_temperature);
            }
        }
    }
}

 

去掉注册和注销的行为,通过添加到或删除从委托保存行为对象,然后通过调用委托,执行已经注册的行为。

public void Test_5_heater_2()
{
    Heater heater 
= new Heater();

    
//警报
    Alert aa=new Alert();
    
//显示
    Display dd = new Display(); 

    
//注册两个行为
    heater._selfHeater += aa.MakeAlert;
    heater._selfHeater 
+= dd.ShowMessage;
    heater.BoilWater(); 

    Console.WriteLine(
"************************************");

    
//显示器坏了,注销显示行为
    heater._selfHeater -= dd.ShowMessage;
    heater.BoilWater();
}

 

访问类中的字段,通过采用属性方式访问。通过属性对域进行封装。它主要体现面向对象的封装特性,且可以提高安全性,和

数据的完整性。例如可以通过设置读和写属性来控制域的读写属性。但平时多从封装性方面来使用,在3.x4.0版本中,出现了

自动属性。同样对于委托来说,可以通过事件类型来封装。

通过事件来封装委托实现水加热器。

public class Heater
{
    
public delegate void BoilWaterHandler(int iTemperature);
    
public event BoilWaterHandler BoilEvent;
    
int _temperature;
    
public void BoilWater()
    {
        
for (int i = 0; i < 101; i++)
        {
            
if (i > 95)
            {
                _temperature 
= i;
                BoilEvent(_temperature);
            }
        }
    } 
}

 

调用时,和上一段实现没有区别:

public void Test_5_heater_2()
{
    Heater heater 
= new Heater();

    
//警报
    Alert aa=new Alert();
    
//显示
    Display dd = new Display();
 
    
//注册两个行为
    heater.BoilEvent += aa.MakeAlert;
    heater.BoilEvent 
+= dd.ShowMessage;
    heater.BoilWater(); 

    Console.WriteLine(
"************************************");

    
//显示器坏了,注销显示行为
    heater.BoilEvent -= dd.ShowMessage;
    heater.BoilWater();
}

 

在加热器加热时,也就是在执行BoilWater时,如果温度到达95度以上,就会调用注册到BoilEvent事件的行为,它通过委托实现。但如果还没有注册行为,在BoilWater执行时,到达条件调用两个注册行为,会引发异常,因为现在BoilEvent未注册行为,所以,还应该在Heater类中BoilWater中添加对事件为空的判断。

if(BoilEvent!=null)
     BoilEvent(_temperature);

 

现在通过委托且用事件封装委托实现了观察者。在c#中,事件的实现模式就是观察者模式。下面通过系统的自定义事件实现这个水加热器。

先看下常用的page_load事件

 

protected void Page_Load(object sender, EventArgs e)

 

可以知道,这个事件的委托类型委托的方法带两个参数: sender参数就是主题对象。而e为主题对象多个依赖的要获得的信息数

据,例如在热水器中,其中的温度就是这个e

public class Heater
{
    
public delegate void BoilWaterEventHandler(object sender, BoildEventArgs e);
    
public event BoilWaterEventHandler Boiled;
    
int _temperature;

    
public void BoilWater()
    {
        
for (int i = 0; i < 101; i++)
        {
            
if (i > 95)
            {
                _temperature 
= i;
                
if (Boiled != null)
                {
                    BoildEventArgs args 
= new BoildEventArgs(_temperature);
                    Boiled(
this, args);
                }
            }
        }
    }
}

public class BoildEventArgs : EventArgs
{
    
public BoildEventArgs(int iTemperature)
    {
        Temperature 
= iTemperature;
    }
    
public int Temperature { getset; }
}

public class Alert
{
    
public void MakeAlert(object sender, BoildEventArgs e)
    {
        Console.WriteLine(
"alarm:" + e.Temperature);
    }


public class Display
{
    
public void ShowMessage(object sender, BoildEventArgs e)
    {
        Console.WriteLine(
"current temperature is:" + e.Temperature);
    }
}

 

因为委托变了,所以还要调用被委托的行为的签名,所以ShowMessage方法和MakeAlert方法都做了相应的调整。

 

实现三:通过.net内置的观察者模式实现水加热器

.net4.0中,添加了对观察者模式的支持。添加了两个接口:

IObservable<T>这个就是主题

IObserver<T>这个是观察者。

 

public class Heater:IObservable<int>
{
    List
<IObserver<int>> _list = new List<IObserver<int>>();
    
int _temperature;
    
public IDisposable Subscribe(IObserver<int> observer)
    {
        _list.Add(observer);
        
return observer as IDisposable;
    }
    
public void BoilWater()
    {
        
for (int i = 0; i < 101; i++)
        {
            
if (i > 95)
            {
                _temperature 
= i;
                Notify(_temperature);
            }
        }
    }
    
public void Notify(int iTemperature)
    {
        
foreach (IObserver<int> observer in _list)
            observer.OnNext(iTemperature);
 
    }
 
    
public IDisposable UnSubscribe(IObserver<int> observer)
    {
        _list.Remove(observer);
        
return observer as IDisposable;
    }
}
public class Alert:IObserver<int>
{
    
public void OnCompleted()
    {
        
throw new NotImplementedException();
    }
 
    
public void OnError(Exception error)
    {
        
throw new NotImplementedException();
    }
 
    
public void OnNext(int value)
    {
        Console.WriteLine(
"alarm:" + value);
    }
 
}
 
public class Display : IObserver<int>
{
    
public void OnCompleted()
    {
        
throw new NotImplementedException();
    }
 
    
public void OnError(Exception error)
    {
        
throw new NotImplementedException();
    }
    
public void OnNext(int value)
    {
        Console.WriteLine(
"current temperature is:" + value);
    }
}
 
public void Test_7_observer()
{
    Heater heater 
= new Heater();
    Alert aa 
= new Alert();
    Display dd 
= new Display();
 
    heater.Subscribe(aa);
    heater.Subscribe(dd);
    heater.BoilWater();
 
    Console.WriteLine(
"************************************");
    heater.UnSubscribe(dd);
    heater.BoilWater();
}

 

Observer模式使得我们可以独立地改变目标与观察者,从而使二者之间的依赖关系达到松耦合。委托就是一种观察者模式的应

用,应用的前提是观察者(被委托的方法)具有相同的接口 ,或说具有相同的高层抽象。在这里被委托的方法,方法名可以

不同,但方法签名的其他部分相同。 

(评论功能已被博主禁用)