设计模式记--Observer Pattern观察者模式

  观察者模式——定义了对象之间的一对多依赖,这样一来,当一个对像改变状态时,它的所有依赖者都会收到通知并自动更新.
  从定义可以看出,OBSERVER(观察者)模式逻辑上需要两组对象来实现.首先它必需要有发布者(Publish),也可称为被观察的目标 (Subject)(习惯上都称它为目标Subject,后面我们都称它作目标Subject),另外就是订阅者(Subscribe),习惯上称为观察 者(Observer).一个目标对象对应多个观察者对象,目标对象发生变化时,所有在目标对象中注册的观察者对象会得到通知,自动更新自己.
  观察者模式UML图如下:
                        
  
  观察者模式的相关角色:
  1、抽象主体(Subject)角色:也 就是被关注的对象,是一对多关系中的那个“一”。它的相关信息的变化将会通知给订阅这个变化的观察者。主体角色把所有对观察考对象的引用保存在一个集合 (List,ArrayList.....)里,每个主体可能管理若干数量的观察者。抽象主体提供一个接口,可以增加和删除观察者对象,主体角色又叫做抽 象被观察者(Observable)角色,一般用一个抽象类或者一个接口实现。
  2、抽象观察者(Observer)角色:为所有的具体观察者定义一个接口,在得到主体的通知时更新自己。这个接口叫做更新接口(Update)。抽象观察者角色一般用一个抽象类或者一个接口实现。在这个示意性的实现中,更新接口只包含一个方法(即Update()方法),这个方法叫做更新方法。
  3、具体主体(ConcreteSubject)角色:将有关状态存入具体现察者对象;在具体主体的内部状态改变时,给所有登记过的观察者发出通知。具体主体角色又叫做具体被观察者角色(Concrete Observable)。具体主题角色通常用一个具体子类实现。
  4、具体观察者(ConcreteObserver)角色:存储与主体的状态自恰的状态。具体现察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主体的状态相协调。如果需要,具体现察者角色可以保存一个指向具体主体对象的引用。具体观察者角色通常用一个具体子类实现。

 下面,我们用代码来示例观察者模式。
 程序如下图:
                         
 一、观察者模式的基本思路
  1、抽象主体Subject

ContractedBlock.gifExpandedBlockStart.gif
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;


namespace MyObserver
{
    
//定义Subject抽象类,它是'ConcreteSubject'具体目标对象的基类
    
//要实现Observer模式时,通常将数据对象作为目标(Subject),各个显示数据的对象作为观察者Observer
    
//每一个观察者(Observer)通过调用目标(Subject)中的一个公有(public)方法,在他所感兴趣的数据中注册(registers)自己。
    
//这样,当数据改变时,每一个目标(Subject)通过观察者(Observer)的接口发送更新通知。

    
abstract class Subject
    { 
        
#region 定义一个List来装盛所有与此数据对象相联系的观察者Observer
        
private List<Observer> _observers = new List<Observer>();
        
#endregion

        
#region 附加或解除Observer功能(注册或注销功能)
        
public void Attach(Observer observer)
        {
            _observers.Add(observer); 
//observer观察者在此数据对象中注册(registers)自己。
        }

        
public void Detach(Observer observer)
        {
            _observers.Remove(observer);
//在此数据对象中取消注册,也即让对象变更时不用再通知此observer观察者
        }
        
#endregion

        
#region 通知在_observers列表中的所有观察者
        
public void Nofity()
        {
            
//遍历观察者列表,按列表的名录逐一通知
            foreach (Observer o in _observers)
            {
                o.Update();
            }
        }
        
#endregion
    }
 
}

  2、具体主体ConcreteSubject 

ContractedBlock.gifExpandedBlockStart.gif
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyObserver
{
    
//定义具体数据对象类,它继承自Subject抽象类
    class ConcreteSubject:Subject
    {
        
#region SubjectState属性
        
private string _subjectState;
        
public string SubjectState
        {
            
get { return _subjectState; }
            
set { _subjectState = value; }
        }
        
#endregion
    }
}

  3、抽象观察者Observer

ContractedBlock.gifExpandedBlockStart.gif
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyObserver
{
    
#region 定义Observer抽象类,它是ConcreteObserver的基类
    
abstract  class Observer
    {
        
//定义一个用于发送更新通知的接口
        
//这样,当数据改变时,每一个目标(Subject)通过观察者(Observer)的接口发送更新通知。
        public abstract void Update();
    }
    
#endregion
}

  4、具体观察者ConcreteObserver

ContractedBlock.gifExpandedBlockStart.gif
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyObserver
{
    
class ConcreteObserver:Observer 
    {
        
private string _name;
        
private string _observerState;

        
private ConcreteSubject _subject;
        
public ConcreteSubject Subject
        {
            
get { return _subject; }
            
set { _subject = value; }
        }


        
#region 构造函数
        
public ConcreteObserver(ConcreteSubject subject, string name)
        {
            
this._subject = subject;
            
this._name = name;
        }
        
#endregion

        
#region 实现目标数据更新通知接口
        
public override void Update()
        {
            _observerState 
= _subject.SubjectState;
            Console.WriteLine(
"观察者 {0} 收到的数据对象的新状态值是 {1}",_name,_observerState);

        }
        
#endregion


    }
}

  5、客户端代码 

ContractedBlock.gifExpandedBlockStart.gif
            #region 基本思路示例
            Console.WriteLine(
"----------观察者模式基本思路示例--------");
            ConcreteSubject s 
= new ConcreteSubject(); //首先创建一个数据对象
            s.Attach(new ConcreteObserver(s, "X")); //向这个数据对象内注册三个观察者X,Y,Z
            s.Attach(new ConcreteObserver(s, "Y"));
            s.Attach(
new ConcreteObserver(s, "Z"));

            s.SubjectState 
= "ABC"//改变数据对象的状态值
            s.Nofity(); //调用数据对象的通知功能来依次通知已经注册的观察者

            Console.ReadKey();
            
#endregion

 二、我的团长我的团使用观察者模式
 这里,我们让孟烦了在前哨望风,当他发现敌情时,他马上通知所有兄弟们准备战斗。这里,孟烦了就是具体主体ConcreteSubject,他的那些兄弟:要麻,迷龙,豆饼....等等都是具体观察者(ConcreteObserver)
  1、抽象主体Subject:Guard

ContractedBlock.gifExpandedBlockStart.gif
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace MyObserver
{
    
abstract class Guard
    {
        
#region 定义一个List来收集所有需要通知到的其它士兵
        
private List<Soldier> _soldiers = new List<Soldier>();
        
#endregion

        
#region 附加或解除Observer功能(注册或注销功能)
        
public void Attach(Soldier observer)
        {
            _soldiers.Add(observer); 
//observer观察者在此数据对象中注册(registers)自己。
        }

        
public void Detach(Soldier observer)
        {
            _soldiers.Remove(observer);
//在此数据对象中取消注册,也即让对象变更时不用再通知此observer观察者
        }
        
#endregion

        
#region 通知在_observers列表中的所有观察者
        
public void Nofity()
        {
            
//遍历观察者列表,按列表的名录逐一通知
            foreach (Soldier o in _soldiers)
            {
                o.Update();
            }
        }
        
#endregion
    }
}

  2、具体主体ConcreteSubject:ConcreteGurad

ContractedBlock.gifExpandedBlockStart.gif
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyObserver
{
    
class ConcreteGurad:Guard
    {
        
private string _name;
        
public string Name
        {
            
get { return _name; }
            
set { _name = value; }
        }

        
#region FightInfo属性
        
private string  _fightInfo;
        
public string FightInfo
        {
            
get { return _fightInfo; }
            
set { _fightInfo = value; }
        }
        
#endregion

        
public ConcreteGurad(string name)
        {
            
this._name = name;
        }
    }
}

  3、抽象观察者Observer:Soldier

ContractedBlock.gifExpandedBlockStart.gif
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyObserver
{
    
abstract  class Soldier
    {
        
public abstract void Update();
    }
}

  4、具体观察者ConcreteObserver:ConcreteSoldier

ContractedBlock.gifExpandedBlockStart.gif
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyObserver
{
    
class ConcreteSoldier:Soldier 
    {
        
private string _observerState;
        
private string _name;


        
private ConcreteGurad _subject;
        
public ConcreteGurad Subject
        {
            
get { return _subject; }
            
set { _subject = value; }
        }


        
#region 构造函数
        
public ConcreteSoldier(ConcreteGurad subject, string name)
        {
            
this._subject = subject;
            
this._name = name;
        }
        
#endregion

        
#region 实现目标数据更新通知接口
        
public override void Update()
        {
            _observerState 
= _subject.FightInfo ;
            Console.WriteLine(
"士兵 '{0}'  收到哨兵 '{1}' 的信号:{2}",_name,_subject.Name ,_observerState);

        }
        
#endregion
    }
}

  5、客户端代码

ContractedBlock.gifExpandedBlockStart.gif
            #region 我的团长我的团
            Console.WriteLine(
"----------我的团长我的团观察者模式示例--------");
            ConcreteGurad cg 
= new ConcreteGurad("孟烦了");
            ConcreteSoldier yaoma 
= new ConcreteSoldier(cg, "要麻");
            ConcreteSoldier sepigu 
= new ConcreteSoldier(cg, "蛇屁股");
            ConcreteSoldier doubing 
= new ConcreteSoldier(cg, "豆饼");
            ConcreteSoldier kangya 
= new ConcreteSoldier(cg, "康丫");
            ConcreteSoldier milong 
= new ConcreteSoldier(cg, "迷龙");
            ConcreteSoldier bula 
= new ConcreteSoldier(cg, "不辣");


            cg.Attach(yaoma);
            cg.Attach(sepigu);
            cg.Attach(doubing);
            cg.Attach(kangya);
            cg.Attach(milong);
            cg.Attach(bula);

            cg.FightInfo 
= "鬼子从右边摸上来了,大家准备歼灭他们.";
            cg.Nofity();
            Console.ReadKey();
            
#endregion

程序运行后效果如下:
 

总结:
1、要点
 (1)抽象主体角色公开了自身的事件,可以给任意观察者订阅。
 (2)象观察者角色定义了统一的处理行为,在C#中使用事件-代理模式的话,统一的处理行为并不这么重要,有的时候甚至还会限制灵活性。
 (3)观察者往往只需要实现响应方法即可。
 (4)有多个主体角色、多个观察者角色交错,也可以一个类型是两个角色,主体也可以提供多个事件。从应用上来说观察者模式变化是非常多的。
2、优缺点
  观察者模式的优缺点
Observer模式的优点是实现了表示层和数据逻辑层的分离,并定义了稳定的更新消息传递机制,类别清晰,并抽象了更新接口,使得可以有各种各样不同的表示层(观察者)。
  但是其缺点是每个外观对象必须继承这个抽像出来的接口类,这样就造成了一些不方便,比如有一个别人写的外观对象,并没有继承该抽象类,或者接口不对,我们 又希望不修改该类直接使用它。虽然可以再应用Adapter模式来一定程度上解决这个问题,但是会造成更加复杂烦琐的设计,增加出错几率。
观察者模式的效果有以下几个优点:
(1)观察者模式在被观察者和观察者之间建立一个抽象的耦合。被观察者角色所知道的只是一个具体现察者聚集,每一个具体现察者都符合一个抽象观察者的接 口。被观察者并不认识任何一个具体观察者,它只知道它们都有一个共同的接口。由于被观察者和观察者没有紧密地耦合在一起,因此它们可以属于不同的抽象化层 次。
(2)观察者模式支持广播通信。被观察者会向所有的登记过的观察者发出通知。
观察者模式有下面的一些缺点:
(1)如果一个被观察者对象有很多直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
(2)如果在被观察者之间有循环依赖的话,被观察者会触发它们之间进行循环调用,导致系统崩溃。在使用观察考模式时要特别注意这一点。
(3)如果对观察者的通知是通过另外的线程进行异步投递的话,系统必须保证投递是以自恰的方式进行的。
(4)虽然观察者模式可以随时使观察者知道所观察的对象发生了变化,但是观察者模式没有相应的机制使观察者知道所观察的对象是怎么发生变化的。

 

 

转载于:https://www.cnblogs.com/smallfa/archive/2009/11/19/1606147.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/423330.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++深度剖析教程9】初探C++标准库

在这之前&#xff0c;我写的C程序不能叫做标准的C程序&#xff0c;因为里面写的大多数还带有C语言的影子。今天我们来学习C标准库。 首先看一下例子&#xff1a;操作符<<的原生意义是按位左移。那么我们重载这个操作符&#xff0c;将变量或者常量&#xff0c;左移到一个…

员工考核UI网页界面(PS大屏文件资料)

现分享人员管理可视化数据统计网页UI、员工考核数据可视化UI网页界面模版的UI源文件&#xff0c;供UI设计师们快速获取PSD源文件完成工作。 若需更多 大屏组件&#xff0c;请移步小7的另一篇文章&#xff1a;数据可视化大屏组件&#xff0c;大屏PSD设计源文件(大屏UI设计规范)…

docker 使用中遇到的问题

1.执行docker run hello 命令建立镜像过程中报错 开始以为是加速器的问题&#xff0c;将/etc/docker/daemon.json 文件删除还是不行 后来执行了这两条命令&#xff1a;就不报错了如下图&#xff1a; systemctl daemon-reloadsystemctl restart docker.service 参考&#xff1…

maven 解决冲突

1.Maven之jar包冲突解决&#xff08;理解maven 产生冲突的原因&#xff09; 导致jar包冲突的原因 1、mvn的传递依赖特性&#xff1a;mvn编译打包除了会引入直接申明的依赖&#xff0c;还会引入间接申明的依赖 2、mvn的依赖仲裁规则&#xff1a; 1&#xff09;. 按照项目总控P…

jz2440开发板修改UBOOT支持NAND FLASH

很多天没有看嵌入式的东西了&#xff0c;今天来看一下&#xff0c;继续之前移植uboot到jz2440开发板。今天我们来实现Uboot支持NAND FLASH。 在之前的文章里&#xff08;点击连接查看之前的记录&#xff09;&#xff0c;我们为了编译通过把NAND FLASH 给屏蔽掉了&#xff0c;现…

i.MX6网卡驱动程序fec.c的分析(AR8035网卡驱动程序的详细分析)之二

学习交流加 个人qq&#xff1a; 1126137994个人微信&#xff1a; liu1126137994学习交流资源分享qq群&#xff1a; 962535112 今天接着分析上次没有分析完的i.MX6网卡驱动程序。上一篇分析了iMX6网卡驱动程序的driver与device的加载过程&#xff08;点击可以查看上一篇文章&…

阅读ethercat官方文档关于ethercat网卡驱动程序的一些内容

学习交流加 个人qq&#xff1a; 1126137994个人微信&#xff1a; liu1126137994学习交流资源分享qq群&#xff1a; 962535112 改造iMX6&#xff08;fec&#xff09;网卡驱动程序前期工作之&#xff1a;阅读ethercat-1.5.2.pdf文档的第四章内容。 ethercat-1.5.2.pdf文档链接&a…

jz2440开发板移植U-boot之修改代码支持DM9000网卡

今天我们来移植U-boot到jz2440开发板&#xff0c;修改代码支持DM9000网卡。查看之前写的移植记录请点击链接&#xff1a;点击查看之前的移植记录 现在大多数开发板都支持DM9000网卡。我们的U-boot源码里面也是有DM9000网卡的驱动程序的。文件为Dm9000x.c&#xff08;drivers\n…

移植U-BOOT之裁剪和修改默认参数(易用性)启动内核,以及对uboot进行分区

今天我们来裁剪U-BOOT&#xff0c;使其更加易用&#xff0c;修改默认参数&#xff0c;以及制作最终修改好得补丁文件方便以后的快速移植。 那么如果想看之前的关于网卡以及flash等的移植&#xff0c;请点击链接查看&#xff1a;点击链接查看 在裁剪修改之前呢&#xff0c;我们…

移植U-BOOT之支持烧写YAFFS文件系统以及制作U-BOOT补丁

今天&#xff0c;我们来移植U-BOOT让其支持YAFFS文件系统映像的烧写&#xff0c;以及最后的终极目标&#xff0c;制作Uboot补丁&#xff0c;因为我们信心苦苦移植好了Uboot&#xff0c;如果换一个地方的或者换一台电脑之类的&#xff0c;我们也不想再浪费时间从头开始移植&…

【数据结构学习之完全从零实现所有数据结构的代码编写之一】泛型编程简介

学习交流加 个人qq&#xff1a; 1126137994个人微信&#xff1a; liu1126137994学习交流资源分享qq群&#xff1a; 962535112 今天开始系统性学习数据结构内容&#xff0c;之前也看过大话数据结构这本书&#xff0c;对大多数概念以及数据结构都有一定的了解&#xff0c;但是就是…

zookeeper 安装和使用

1.Windows安装和使用zookeeper 之前整理过一篇文章《zookeeper 分布式锁服务》&#xff0c;本文介绍的 Zookeeper 是以 3.4.5 这个稳定版本为基础&#xff0c;最新的版本可以通过官网 http://hadoop.apache.org/zookeeper/来获取&#xff0c;Zookeeper 的安装非常简单&#xf…

【移植Linux 3.4.2内核第一步】之简单修改

前一阵子已经将U-boot移植好了&#xff0c;从今天开始&#xff0c;我们开始移植linux内核。移植的内核为3.4.2&#xff0c;移植的开发板为&#xff1a;jz2440开发板。 想看之前移植U-boot的记录&#xff0c;可以查看我的博客专栏&#xff0c;点击链接&#xff1a;点击查看U-bo…

前端学习(77):css中常见margin塌陷问题之解决办法

塌陷问题 当两个盒子在垂直方向上设置margin值时&#xff0c;会出现一个有趣的塌陷现象。 ①垂直并列 首先设置两个DIV,并为其制定宽高 1 1 /*HTML部分*/2 <body>3 <div class"box1">box1</div>4 <div class"box2">box2…

HBase2.0 vs HBase1.x 延时比较

hbase2.0已经正式发布&#xff0c;对比之前1.x版本&#xff0c;2.0在读写链路上做了完善的优化&#xff0c;offheap、netty rpc等&#xff0c;这里做个小测试实验对比1.x和2.0在读写上的延时情况。本测试基于特定测试环境与软件版本得到的结果&#xff0c;仅供参考。 测试介绍 …

【数据结构学习之完全从零实现所有数据结构的代码编写之二】智能指针

今天我们依然暂时不讲解数据结构里面的内容&#xff0c;我们来复习一下昨天学的模板技术用于数据结构编程的思想&#xff0c;给出一个模板技术的实例&#xff1a;智能指针的应用。喜欢看我分享的加我q:1126137994 加我共同学习交流各种技术。 为什么会引入智能指针呢&#xff…

职责链模式(Chain of Responsibility)

重要概念 1. 使多个对象都有机会处理请求&#xff0c;从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链&#xff0c;并沿着这条链传递该请求&#xff0c;直到有一个对象处理它为止。 2.请求是沿着链条传递到有一个处理点对象负责处理这个请求为止。请求者无需…

mootools

$();// 选择ID为”body_wrap“的元素$(body_wrap);.getElement();// 选择ID为”body_wrap“的元素下面的第一个链接$(body_wrap).getElement(a); or #xxx or .xxx.getElements();// 选择ID为”body_wrap“的元素下面的所有链接$(body_wrap).getElements(a); $(body_wrap).getE…

图形化界面客户端连接phoenix操作hbase

下载客户端软件 DBeaver https://dbeaver.io/download/ 选择对应系统的版本&#xff0c;我这里选择解压版windows64位 创建连接 注意&#xff1a;URL模板就不要一般是默认 选择合适的版本&#xff08;跟你服务器的版本一致&#xff09;&#xff0c;下载jar包 点击测试或完成即…

【C++深度剖析教程12】数组操作符的重载

之前写的C学习记录忘记打编号了&#xff0c;从今天开始&#xff0c;所有内容&#xff0c;记录编号&#xff0c;方便以后的查阅复习。今天学习的是C中&#xff0c;数组操作符的重载。 上一篇博文写的是介绍C中的字符串类&#xff0c;我们知道&#xff0c;C标准库中通过string类…