设计模式--观察者模式

今日看了《Head First》一书中关于“观察者”模式的讲解,突然间了解到自己对“观察者”的了解很是肤浅。今天采取与策略模式相同的方法来学习观察者模式。

 1、什么是观察者模式?

观察者模式又叫做“发布-订阅”模式,它定义了对象之间的一对多依赖关系,这样当一个对象的状态改变时,他的所有依赖者都会收到通知并自动更新。这种情景在我们的生活中很常见,比如订阅报纸,关注某人的微博等,你订阅了(或者关注了)什么报纸(什么人),在这个报纸到达报社(这个人发了微博有了更新)时都会给你送到(都会通知你)。

这里所谓的“一”就是被观察者,我们习惯称它为“主题”(Subject),而“多”就是关心主题变化的一些“观察者”(Observer)了。主题是真正拥有数据的人,观察者是主题的依赖者,在数据变化时更新,这样比起让许多对象控制同一份数据来,设计要干净的多。

观察者模式步骤观察者模式步骤

2、观察者模式的设计原则

  2.1封装变化

      在观察者模式中,会改变的是主题的状态,以及观察者的数目和类型。使用观察者模式,可以改变依赖依赖于主题状态的观察者对象,而不必改变主题,这就是提前规划。 

  2.2 针对接口编程,不针对实现编程

      主题与观察者都使用接口,主题接口中提供注册观察者(registerObserver),删除观察者(removeObserver)和通知观察者(notifyObserver)的方法,继承该接口的主题应该实现这些方法。观察者接口提供更新(update)方法,所有继承自观察者接口的观察者类都应该实现这个方法。这样,观察者就能利用主题的接口向主题注册(registerObserver),而主题利用观察者接口通知观察者(update),这样能够让两者之间运作正常,又同时具有松耦合的特点。    

  2.3多用组合,少用继承”

      在观察着模式中,主题需要维护一个观察者容器(数组),将许多观察者组合进主题中,对象之间的这种关系不是通过继承产生的,而是在运行时利用组合的方式产生的。

3、如何实现观察者模式 ?

     根据以上的设计原则,实现观察者模式需要有两个接口,一个是被观察者接口(ISubject),提供注册观察者(registerObserver),删除观察者(removeObserver)和通知观察者(notifyObserver)的方法;另一个是观察者接口(IObserver),提供更新(update)方法。

     3.1 那么如何将观察者注册呢?

     在主题类(被观察者类,继承自主题接口的具体类)中应该有一个观察者容器,注册观察者就是将这个观察者添加到该容器。

     3.2 那么被观察者如何送出通知呢?

     被观察者类的数据在发生变化时,一般这个变化通过数据的setter方法检测,遍历观察者容器,让每一个观察者调用自己的update()方法更新自己的状态。

     3.3 松耦合

     关于观察者的一切,主题只需要知道观察者实现了某个接口(Observer的接口),它并不关心观察者具体是谁,做了什么东西和其他任何细节。并且任何时候我们都可以添加新的观察者,只要它实现了Observer的接口,并且当有新观察者出现时,主题代码不需要修改。我们可以独立的复用主题和观察者,因为二者并非紧耦合。

4、观察者模式类图

下面看一个简单观察者模式例子的类图(学生关注老师手机号码的变化)

5、学生关心老师电话号码变更的代码

//主题接口
    interface Subject
    {
        void registerObserver(object o);
        void removeObserver(object o);
        void notifyObserver();
    }
//观察者接口
    interface Observer
    {
        void update(object o);
    }
//被观察者--教师类
    class Teacher:Subject
    {
        private string name;

        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        private string phone;

        public string Phone
        {
            get { return phone; }
            set {
                phone = value;
                notifyObserver();
            }
        }

        private ArrayList observers;

        public Teacher()
        {
            this.observers = new ArrayList();
        }

        public void registerObserver(object o)
        {
            this.observers.Add(o);
        }
        public void removeObserver(object o)
        {
            this.observers.Remove(o);
        }
        public void notifyObserver()
        {
            for (int i = 0; i < observers.Count;i++ )
            {
                ((Observer)observers[i]).update(Phone);
            }
        }
    }
//观察者类--学生类
    class Student:Observer
    {
        private string name;

        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        private string Tphone;

        public Student(string aname)
        {
            this.Name = aname;
        }
        public void update(object o)
        {
            this.Tphone  = (string)o;
        }
        public void show()
        {
            Console.WriteLine("Name:"+Name+"\nTeacher's Phone:"+Tphone);
        }

    }
//测试代码
        static void Main(string[] args)
        {
            Teacher csl = new Teacher();
            Student stu1 = new Student("Ann");
            Student stu2 = new Student("Tom");
            Student stu3 = new Student("Jhon");

            csl.registerObserver(stu1);
            csl.registerObserver(stu2);
            csl.registerObserver(stu3);
            csl.Name = "CHENG SHAO LEI";
            csl.Phone = "13333333333";

            stu1.show();
            stu2.show();
            stu3.show();

            csl.Phone = "12345678996";
            stu3.show();

            Console.ReadKey();

        }

6、《Head First》中有一个非常好的观察者模式小例题--气象观测站,如下是程序的代码和类图

这个代码的实现和上一个程序的代码稍微有点区别,就是注册观察者的时机,第一个程序在客户使用时注册观察者,第二个程序则是在观察者出生时(构造器里)就制订了它的观察对象,这两中实现方法没有本质区别。

代码如下:

public interface Subject
    {
        void registerObserver(Observer o);

        void removeObserver(Observer o);

        void notifyObserver();
    }
public interface Observer
    {
        void update(float temp, float humidity, float pressure);
    }
public interface DisplayElement
    {
        void display();
    }
public class ForecastDisplay : Observer, DisplayElement
    {
        private Subject weatherData;

        public ForecastDisplay(Subject weather)
        {
            this.weatherData = weather;
            weatherData.registerObserver(this);
        }
        public void update(float temp, float humidity, float pressure)
        {
            this.display();
        }

        public void display()
        {
            Console.WriteLine("Improving weather on the way");
        }
    }
public class StatisticsDisplay : Observer, DisplayElement
    {
        private float maxTemp;
        private float minTemp;
        private float aveTemp;

        private Subject weatherData;

        public StatisticsDisplay(Subject weather)
        {
            this.weatherData = weather;
            weatherData.registerObserver(this);
        }
        public void update(float temp, float humidity, float pressure)
        {
            this.maxTemp = 80;
            this.minTemp = 80;
            this.aveTemp = 80;
        }

        public void display()
        {
            Console.WriteLine("Avg/Max/Min temperature  = "+this.aveTemp+" / "+this.maxTemp+" / "+this.minTemp);
        }
    }
public class CurrentConditionsDisplay : Observer, DisplayElement
    {
        private float temperater;
        private float humidity;
        private Subject weatherData;

        public CurrentConditionsDisplay(Subject weatherdata)
        {
            this.weatherData = weatherdata;
            weatherData.registerObserver(this);
        }

        public void update(float temp, float humidity, float pressure)
        {
            this.temperater = temp;
            this.humidity = humidity;
            display();
        }

        public void display()
        {
            Console.WriteLine("Current conditions:"+temperater +" F degrees and "+humidity + "%humidity");
        }
    }
public class WeatherData : Subject
    {
        private ArrayList obersers;
        private float temperature;
        private float humidity;
        private float pressure;

        public WeatherData()
        {
            this.obersers = new ArrayList();
        }
        public void registerObserver(Observer o)
        {
            this.obersers.Add(o);
        }

        public void removeObserver(Observer o)
        {
            int i = this.obersers.IndexOf(o);
            if (i>=0)
            {
                this.obersers.Remove(o);
            }
        }

        public void notifyObserver()
        {
            for (int i = 0; i < this.obersers.Count;i++ )
            {
                Observer observer = (Observer)this.obersers[i];
                observer.update(temperature, humidity, pressure);
            }
        }

        public void measurementsChange()
        {
            this.notifyObserver();
        }

        public void setMeasureMents(float temperature, float humidity, float pressure)
        {
            this.temperature = temperature;
            this.humidity = humidity;
            this.pressure = pressure;
            measurementsChange();
        }
    }
static void Main(string[] args)
        {
            WeatherData weatherData = new WeatherData();
            CurrentConditionsDisplay current = new CurrentConditionsDisplay(weatherData);
            StatisticsDisplay statistics = new StatisticsDisplay(weatherData);
            ForecastDisplay forecase = new ForecastDisplay(weatherData);

            weatherData.setMeasureMents(80,65,30.4f);
            Console.ReadKey(); 
        }

7、关于JAVA内置的观察者模式
内置的不一定是好的!java.util.Observable也有“黑暗”的一面。Observable(可观察者类)是一个类,而不是一个“接口”,也就是说要使用他的方法我们只能继承他,而JAVA又不提供多继承,这很容易使程序员陷入两难境地。并且,这个类将关键的方法保护起来(protected),这样就更只能用继承了,这就违反了我们“多用组合,少用继承”的原则。


 

posted on 2013-03-27 20:52  雨过晴空  阅读(211)  评论(0编辑  收藏  举报

导航