大话设计模式,让你的代码更完美9---旁观者模式

来源:互联网 发布:下载360root软件 编辑:程序博客网 时间:2024/06/09 15:22

需求:如果老板回到公司时,前台秘书就立马通知各个工作的同事,让其停下无关工作,立马进行工作。

菜鸟的第一次代码:

    //前台秘书类    class Secretary    {        //同事列表        private IList<StockObserver> observers = new List<StockObserver>();        private string action;        //增加所要通知的同事        public void Attach(StockObserver observer)        {            observers.Add(observer);        }        //减少所要通知的同事        public void Detach(StockObserver observer)        {            observers.Remove(observer);        }        //通知同事        public void Notify()        {            foreach (StockObserver o in observers)                o.Update();        }        //前台状态        public string SecretaryAction        {            get { return action; }            set { action = value; }        }    }

    //看股票的同事    class StockObserver    {        private string name;        private Secretary sub;        public StockObserver(string name, Secretary sub)        {            this.name = name;            this.sub = sub;        }        public void Update()        {            Console.WriteLine("{0} {1} 关闭股票行情,继续工作!", sub.SecretaryAction, name);        }    }


        static void Main(string[] args)        {            //前台小姐童子喆            Secretary tongzizhe = new Secretary();            //看股票的同事            StockObserver tongshi1 = new StockObserver("魏关姹", tongzizhe);            StockObserver tongshi2 = new StockObserver("易管查", tongzizhe);            //前台记下了两位同事            tongzizhe.Attach(tongshi1);            tongzizhe.Attach(tongshi2);            //发现老板回来            tongzizhe.SecretaryAction = "老板回来了!";            //通知两个同事            tongzizhe.Notify();            Console.Read();        }

菜鸟的第二次代码(将看股票的同事类和前台类进行解耦,因为不是所有同事都看股票,也可以看NBA,原有写法中如果想通知一个看NBA的同事,需要在前台秘书类和同事类都进行修改,比如要增加另外一种同事集合....显然下面的写法更方便一点):

    //前台秘书类    class Secretary    {        //同事列表        private IList<Observer> observers = new List<Observer>();        private string action;        //增加        public void Attach(Observer observer)        {            observers.Add(observer);        }        //减少        public void Detach(Observer observer)        {            observers.Remove(observer);        }        //通知        public void Notify()        {            foreach (Observer o in observers)                o.Update();        }        //前台状态        public string SecretaryAction        {            get { return action; }            set { action = value; }        }    }


    //抽象同事类    abstract class Observer    {        protected string name;        protected Secretary sub;        public Observer(string name, Secretary sub)        {            this.name = name;            this.sub = sub;        }        public abstract void Update();    }

    //看股票的同事    class StockObserver : Observer    {        public StockObserver(string name, Secretary sub)            : base(name, sub)        {        }        public override void Update()        {            Console.WriteLine("{0} {1} 关闭股票行情,继续工作!", sub.SecretaryAction, name);        }    }    //看NBA的同事    class NBAObserver : Observer    {        public NBAObserver(string name, Secretary sub)            : base(name, sub)        {        }        public override void Update()        {            Console.WriteLine("{0} {1} 关闭NBA直播,继续工作!", sub.SecretaryAction, name);        }    }

        static void Main(string[] args)        {            //前台小姐童子喆            Secretary tongzizhe = new Secretary();            //看股票的同事            StockObserver tongshi1 = new StockObserver("魏关姹", tongzizhe);            //看NBA的同事            NBAObserver tongshi2 = new NBAObserver("易管查", tongzizhe);            //前台记下了两位同事            tongzizhe.Attach(tongshi1);            tongzizhe.Attach(tongshi2);            //发现老板回来            tongzizhe.SecretaryAction = "老板回来了!";            //通知两个同事            tongzizhe.Notify();            Console.Read();        }

菜鸟的第三次代码(如果前台刚好不在,是否需要另外一个前台或老板通知大家呢?总不能没了之前的前台就没人通知大家工作了吧?将通知者提取出一个一口,然后让老板或者前台都实现它,都可以通知大家。)

注意:抽象同事类的构造函数里面要改为通知者类的抽象类,不能是具体实现类,无论通知者怎么改变我们都不要修改代码,这样写岂不是美滋滋!!

下列代码中可以理解为tongshi1没有被通知到,被老板发现他在看股票。

    //通知者接口    interface Subject    {        void Attach(Observer observer);        void Detach(Observer observer);        void Notify();        string SubjectState        {            get;            set;        }    }

    //前台秘书类    class Secretary : Subject    {        //同事列表        private IList<Observer> observers = new List<Observer>();        private string action;        //增加        public void Attach(Observer observer)        {            observers.Add(observer);        }        //减少        public void Detach(Observer observer)        {            observers.Remove(observer);        }        //通知        public void Notify()        {            foreach (Observer o in observers)                o.Update();        }        //前台状态        public string SubjectState        {            get { return action; }            set { action = value; }        }    }

    ///老板类    class Boss : Subject    {        //同事列表        private IList<Observer> observers = new List<Observer>();        private string action;        //增加        public void Attach(Observer observer)        {            observers.Add(observer);        }        //减少        public void Detach(Observer observer)        {            observers.Remove(observer);        }        //通知        public void Notify()        {            foreach (Observer o in observers)                o.Update();        }        //老板状态        public string SubjectState        {            get { return action; }            set { action = value; }        }    }

    //抽象同事类    abstract class Observer    {        protected string name;        protected Subject sub;        public Observer(string name, Subject sub)        {            this.name = name;            this.sub = sub;        }        public abstract void Update();    }

    //看股票的同事    class StockObserver : Observer    {        public StockObserver(string name, Subject sub)            : base(name, sub)        {        }        public override void Update()        {            Console.WriteLine("{0} {1} 关闭股票行情,继续工作!", sub.SubjectState, name);        }    }    //看NBA的同事    class NBAObserver : Observer    {        public NBAObserver(string name, Subject sub)            : base(name, sub)        {        }        public override void Update()        {            Console.WriteLine("{0} {1} 关闭NBA直播,继续工作!", sub.SubjectState, name);        }    }

        static void Main(string[] args)        {            //老板胡汉三            Boss huhansan = new Boss();            //看股票的同事            StockObserver tongshi1 = new StockObserver("魏关姹", huhansan);            //看NBA的同事            NBAObserver tongshi2 = new NBAObserver("易管查", huhansan);            huhansan.Attach(tongshi1);            huhansan.Attach(tongshi2);            huhansan.Detach(tongshi1);            //老板回来            huhansan.SubjectState = "我胡汉三回来了!";            //发出通知            huhansan.Notify();            Console.Read();        }

在上述代码中,通过抽象类和接口实现了代码的解耦,不会因为同事的改变或者通知同事人员的改变而修改相关代码。

这种模式叫观察者模式,又称发布-订阅模式:

根据上述代码改进得知,将一个系统分割成一系列相互协作的类有一个很不好的副作用就是需要维护对象间的一致性,而他们之间的紧密耦合导致维护扩展十分不便。

这时候使用观察者模式,使得双方都依赖抽象而不依赖于具体对象,使得一边的变化不会影响另外一边,也可以认为是一对多的依赖关系。


这种模式还是会有瑕疵的,比如说,所有的通知全都要依赖同事类,即同事类里面必须都要有抽象方法update,如果有的同事类里面没有该update方法,通知功能就要报废,换句话说就是前台通知每个同事一定要去要调用他们的update方法嘛??????),

对上述代码进行改进,先去掉看股票和看NBA同事的抽象类:


    //通知者接口    interface Subject    {        void Notify();        string SubjectState        {            get;            set;        }    }


    //事件处理程序的委托    delegate void EventHandler();


    class Secretary : Subject    {        //声明一事件Update,类型为委托EventHandler        public event EventHandler Update;        private string action;        public void Notify()        {            Update();        }        public string SubjectState        {            get { return action; }            set { action = value; }        }    }


    class Boss : Subject    {        //声明一事件Update,类型为委托EventHandler        public event EventHandler Update;        private string action;        public void Notify()        {            Update();        }        public string SubjectState        {            get { return action; }            set { action = value; }        }    }

    //看股票的同事    class StockObserver    {        private string name;        private Subject sub;        public StockObserver(string name, Subject sub)        {            this.name = name;            this.sub = sub;        }        //关闭股票行情        public void CloseStockMarket()        {            Console.WriteLine("{0} {1} 关闭股票行情,继续工作!", sub.SubjectState, name);        }    }    //看NBA的同事    class NBAObserver    {        private string name;        private Subject sub;        public NBAObserver(string name, Subject sub)        {            this.name = name;            this.sub = sub;        }        //关闭NBA直播        public void CloseNBADirectSeeding()        {            Console.WriteLine("{0} {1} 关闭NBA直播,继续工作!", sub.SubjectState, name);        }    }

        static void Main(string[] args)        {            //老板胡汉三            Boss huhansan = new Boss();            //看股票的同事            StockObserver tongshi1 = new StockObserver("魏关姹", huhansan);            //看NBA的同事            NBAObserver tongshi2 = new NBAObserver("易管查", huhansan);            huhansan.Update += new EventHandler(tongshi1.CloseStockMarket);            huhansan.Update += new EventHandler(tongshi2.CloseNBADirectSeeding);            //老板回来            huhansan.SubjectState = "我胡汉三回来了!";            //发出通知            huhansan.Notify();            Console.Read();        }

在上述的代码中,我们修改了看股票同事的关闭股票方法名,也修改了看NBA同事的关闭NBA方法名,因为现实就是这样,不同实现类其方法名不可能都相同的,而且通知者(前台或boss)不希望依赖抽象同事类,这里已经没有抽象同事类了,更不需要添加和删除同事类。


这里要说明一下委托,委托是一种引用方法的类型。一旦为委托分配了方法,委托将与该方法具有相同的行为,即具有相同的参数和返回值,可以认为是对方法的抽象。

在主函数中,new EventHandler(tongshi1.CloseStockMarket);就是一个委托的实例,等于将tongshi1.CloseStockMarket方法委托给了  huhansan.Update方法,且一个委托可以搭载多个方法,所有方法被依次唤起,且这些搭载方法不需要在同一个类。

delegate void EventHandler(); //可以理解为一个特殊的类

public event EventHandler Update;   //可以理解为声明了一个类的变量,这个变量为update.





阅读全文
0 0