第16章 观察者模式(Oberver Pattern)

原文  第16章 观察者模式(Oberver Pattern)

观察者模式

 概述:

 

在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系”
——一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好地抵御变化。使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。[GOF 《设计模式》]

结构图:

 

   举例:商品竞拍





1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

    /// <summary>

    /// 竞价平台

    /// </summary>

   public class Platform

   {

       private string _name;

       public Platform(string name)

       {

           _name = name;

       }

       public void SendMeg(Goods good)

       {

           Console.WriteLine("{0}出价{1}",_name,good.Price);

       }

   }

   public class Goods

   {

       public Platform plat;

       //价格

       public int Price

       {

           get;

           set;

       }

       //出价

       public void Send()

       {

           plat.SendMeg(this);

       }

   }

   //client

    class Program

    {

        static void Main(string[] args)

        {

            Platform p = new Platform("A");

            Goods g = new Goods();

            g.plat = p;

            g.Price = 1000;

            g.Send();

            Console.ReadLine();

        }

    }

上面这段代码是没有什么问题的,也实现了我们需要的功能,但是在Goods跟Platform之间进行了相互的方法及属性调用,形成了一个双向依赖的关系,这样假如其中一个变化,另一个也会发生变化。假设我们增加一个微信平台竞拍





1

2

3

4

5

6

7

8

9

10

11

12

13

   public class WeiXin

   {

       private string _name;

       public WeiXin(string name)

       {

           _name = name;

       }

       public void SendMeg(Goods good)

       {

           Console.WriteLine("{0}出价{1}",_name,good.Price);

       }

   }

那我们的Goods类也得做修改





1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

   public class Goods

   {

       public Platform plat;

       public WeiXin wx;

       //价格

       public int Price

       {

           get;

           set;

       }

       //出价

       public void Send()

       {

           plat.SendMeg(this);

           wx.SendMeg(this);

       }

   }

很显然,这样的设计违反了“开放封闭”原则,仅仅是增加了一个平台就需要我们改动Goods类,这不不是我们想要的效果,同时这个样的设计是比较糟糕的。我们在做进一步的处理。对变化进行封装。





1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

    /// <summary>

    /// 抽象类

    /// </summary>

    public interface ISend

    {

        void SendMeg(Goods good);

    }

    /// <summary>

    /// 竞价平台

    /// </summary>

    public class Platform : ISend

   {

       private string _name;

       public Platform(string name)

       {

           _name = name;

       }

       public void SendMeg(Goods good)

       {

           Console.WriteLine("{0}出价{1}",_name,good.Price);

       }

   }

   public class WeiXin : ISend

   {

       private string _name;

       public WeiXin(string name)

       {

           _name = name;

       }

       public void SendMeg(Goods good)

       {

           Console.WriteLine("{0}出价{1}",_name,good.Price);

       }

   }

   public class Goods

   {

       public ISend s;

     

       //价格

       public int Price

       {

           get;

           set;

       }

       //出价

       public void Send()

       {

           s.SendMeg(this);

       

       }

   }

我们让Goods里面的依赖Isend,我们已经弱化了对单个平台的依赖,这算是成功了第一步。但是我们要是增加平台,我们依然要改动Goods类,我们子在做进一步的封装。修改Goods类





1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

    public class Goods

    {

        public List<ISend> list = new List<ISend>();

        public void Add(ISend s)

        {

            list.Add(s);

        }

        //价格

        public int Price

        {

            get;

            set;

        }

        //出价

        public void Send()

        {

            foreach (var item in list)

            {

                item.SendMeg(this);

            }

        }

    }

客户端调用:





1

2

3

4

5

6

7

8

9

10

11

12

13

14

    class Program

    {

        static void Main(string[] args)

        {

            Platform p = new Platform("A");

            WeiXin wx = new WeiXin("B");

            Goods g = new Goods();

            g.Add(p);

            g.Add(wx);

            g.Price = 1000;

            g.Send();

            Console.ReadLine();

        }

    }

到了这一步,我们基本上已经解决了大部分的问题了,但是我们还有一个小小的问题,就是平台需要依赖具体的Goods(商品),假如我们有多个商品呢?那还需要改平台类,所以我们在对Goods类进行封装。





1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

   //竞拍

    /// <summary>

    /// 抽象类

    /// </summary>

    public interface ISend

    {

        void SendMeg(Obj good);

    }

    /// <summary>

    /// 竞价平台

    /// </summary>

    public class Platform : ISend

    {

        private string _name;

        public Platform(string name)

        {

            _name = name;

        }

        public void SendMeg(Obj good)

        {

            Console.WriteLine("{0}出价{1}", _name, good.Price);

        }

    }

    /// <summary>

    /// 微信平台

    /// </summary>

    public class WeiXin : ISend

    {

        private string _name;

        public WeiXin(string name)

        {

            _name = name;

        }

        public void SendMeg(Obj good)

        {

            Console.WriteLine("{0}出价{1}", _name, good.Price);

        }

    }

    /// <summary>

    /// 任何东西

    /// </summary>

    public abstract class Obj

    {

        public List<ISend> list = new List<ISend>();

        //价格

        public int _price;

        public Obj(int price)

        {

            _price = price;

        }

        public int Price

        {

            get return _price; }

        }

        public void Add(ISend s)

        {

            list.Add(s);

        }

        //出价

        public void Send()

        {

            foreach (var item in list)

            {

                item.SendMeg(this);

            }

        }

    }

    /// <summary>

    /// 具体的商品

    /// </summary>

    public class Goods : Obj

    {

        public Goods(int price)

            base(price)

        { }

    }

客户端调用





1

2

3

4

5

6

7

8

9

10

11

12

13

    class Program

    {

        static void Main(string[] args)

        {

            Platform p = new Platform("A");

            WeiXin wx = new WeiXin("B");

            Obj g = new Goods(1000);

            g.Add(p);

            g.Add(wx);

            g.Send();

            Console.ReadLine();

        }

    }

      实现效果

 

1.使用面向对象的抽象,Observer模式使得我们可以独立地改变目标与观察者,从而使二者之间的依赖关系达到松耦合。

2.目标发送通知时,无需指定观察者,通知(可以携带通知信息作为参数)会自动传播。观察者自己决定是否需要订阅通知。目标对象对此一无所知。

3.在C#中的Event。委托充当了抽象的Observer接口,而提供事件的对象充当了目标对象,委托是比抽象Observer接口更为松耦合的设计。

适用场景:

 

1.当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。

2.当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。

3.当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。

设计模式系列文章入口:http://www.diyibk.com/post/39.html

时间: 2024-10-10 16:09:43

第16章 观察者模式(Oberver Pattern)的相关文章

第 16 章 观察者模式【Observer Pattern】

以下内容出自:<<24种设计模式介绍与6大设计原则>> <孙子兵法>有云:“知彼知己,百战不殆:不知彼而知己,一胜一负:不知彼,不知己,每战必殆”,那怎么才能知己知彼呢?知己是很容易的,自己的军队嘛,很容易知道,那怎么知彼呢?安插间谍是很好的一个办法,我们今天就来讲一个间谍的故事. 韩非子大家都应该记得吧,法家的代表人物,主张建立法制社会,实施重罚制度,真是非常有远见呀,看看现在社会在呼吁什么,建立法制化的社会,在2000 多年前就已经提出了.大家可能还不知道,法家还有

设计模式之第18章-观察者模式(Java实现)

设计模式之第18章-观察者模式(Java实现) 话说曾小贤,也就是陈赫这些天有些火,那么这些明星最怕的,同样最喜欢的是什么呢?没错,就是狗仔队.英文的名字比较有意思,是paparazzo,这一说法据说来自意大利电影<滴露牡丹开>中一个专门偷拍明星照片的一个摄影师的名字,“Paparazzo”,中文译为帕帕拉齐,俗语就是狗仔队.这些明星因狗仔队而荣,获得曝光率,也因狗仔队而损,被曝光负面新闻,不管怎么说,总之是“火起来了”,让明星们又爱又恨.(众人:鱼哥,你扯远了).咳咳,这个狗仔队其实嘛,也就

设计模式@第16章:模板方法模式

第16章:模板方法模式 一.豆浆制作问题 编写制作豆浆的程序,说明如下: 制作豆浆的流程 选材--->添加配料--->浸泡--->放到豆浆机打碎 通过添加不同的配料,可以制作出不同口味的豆浆 选材.浸泡和放到豆浆机打碎这几个步骤对于制作每种口味的豆浆都是一样的 请使用 模板方法模式 完成 (说明:因为模板方法模式,比较简单,很容易就想到这个方案,因此就直接使用, 不再使用传统的方案来引出模板方法模式 ) 二.模板方法模式基本介绍 基本介绍 模板方法模式(Template Method P

《Cracking the Coding Interview》——第16章:线程与锁——题目5

2014-04-27 20:16 题目:假设一个类Foo有三个公有的成员方法first().second().third().请用锁的方法来控制调用行为,使得他们的执行循序总是遵从first.second.third的顺序. 解法:你应该想到了用lock的方法类阻塞,不过这里面有个概念问题使得直接用ReentrantLock不能通过编译(对于一个锁对象,不同在A线程中锁定,又在B线程中解锁,不允许这样的归属关系),可以用Semaphore来达到相同的目的.请看下面的代码. 代码: 1 // 16

《Cracking the Coding Interview》——第16章:线程与锁——题目3

2014-04-27 19:26 题目:哲学家吃饭问题,死锁问题经典模型(专门用来黑哲学家的?). 解法:死锁四条件:1. 资源互斥.2. 请求保持.3. 非抢占.4. 循环等待.所以,某砖家拿起一只筷子后如果发现没有另一只了,就必须把手里这只筷子放下,这应该是通过破坏"请求保持"原则来防止死锁产生,请求资源失败时,连自己的资源也进一步释放,然后在下一轮里继续请求,直到成功执行. 代码: 1 // This is the class for chopsticks. 2 import j

《Cracking the Coding Interview》——第16章:线程与锁——题目2

2014-04-27 19:14 题目:如何测量上下文切换的时间? 解法:首先,上下文切换是什么,一搜就知道.对于这么一个极短的时间,要测量的话,可以通过放大N倍的方法.比如:有A和B两件事,并且经常一起发生,每件只需要花几纳秒.如果你把A事件连续做几百万次,而B时间只做了几次,这样就能排除B事件对于测量的影响.如果总时间S = mA + nB.当m >> n 时,A≈S / m.下面的测量方法类似于打乒乓球,在主线程和副线程间互相传递一个令牌,这个令牌可以是变量.管道之类的用于通信的工具.与

《Cracking the Coding Interview》——第16章:线程与锁——题目1

2014-04-27 19:09 题目:线程和进程有什么区别? 解法:理论题,操作系统教材上应该有很详细的解释.我回忆了一下,写了如下几点. 代码: 1 // 16.1 What is the difference between process and thread? 2 Answer: 3 Process: 4 1. Basic element of resource allocation in the operating system. 5 2. Possesses independent

第16章 CSS盒模型下

第 16章 CSS盒模型[下]学习要点:1.元素可见性2.元素盒类型3.元素的浮动 本章主要探讨 HTML5中 CSS盒模型,学习怎样了解元素的外观配置以及文档的整体布局. 一.元素可见性使用visibility属性可以实现元素的可见性,这种样式一般可以配合 JavaScript来实现效果.样式表如下:属性 visibility 值 说明 CSS版本visible 默认值,元素在页面上可见 2hidden 元素不可见,但会占据空间. 2collapse 元素不可见,隐藏表格的行与列. 2 如果不

4.13日第12次作业,16章外包,17章需求,19章组织级与大型项目

29-高项-田哲琦 16章.外包管理 1.外包的形式有哪五种?什么是利益关系?P346-348 答:企业现行采用的主要外包形式如下: 1).活动外包 2).服务外包 3).内包 4).合包 5).利益关系. 利益关系(benfit-based relationship),这是一种长期合作关系,双方先为此关系进行投资,再根据预先拟定的协议分享利益,共同承担风险,同时共享利益. 2.外包管理的目标是什么?要实现这个目标,对外包管理提出哪四个方面的要求?P348 答:软件外包管理总的目标是用强有力的手