在开发过程中经常遇到一个模块中的 一个方法调用了其他模块中相关的方法

比如说在一个系统中,如果出现了错误,就调用专门进行错误处理的模块中的方法进行错误处理

而因为错误处理的操作有很多,所以将这些具体的操作封装在其他的模块中

在专门进行错误处理的模块中调用其他模块中的错误操作方法

这样一来在主系统中只要实例化专门进行错误处理的模块对象

并调用其相关的方法,其他模块中的具体方法也都会被执行

这时专门进行错误处理的模块被称为发布者

其他拥有具体错误操作的模块称为订阅者

只要发布者一发布信息(方法被调用)

所有的订阅者都会相应(具体方法也会执行)

最直接的做法是在模块中引用并实例化其他模块的对象

然后调用其方法

下面给出一个简单的例子,用类来模拟模块

首先是具体的错误操作

在例子中有三个具体的错误操作

分别为:发送邮件,发出警报,窗口抖动

/// <summary>
/// 具体的错误处理方式类(模块)1,此为订阅者
/// </summary>
public class Handle1
{
/// <summary>
/// 出现错误时做出发送邮件的处理
/// </summary>
public void ErrorHanding()
{
Console.WriteLine("出现错误!发送了一封邮件到管理员!");
}
}
/// <summary>
/// 具体的错误处理方式类(模块)2,此为订阅者
/// </summary>
public class Handle2
{
/// <summary>
/// 出现错误时做出发出警报的处理
/// </summary>
public void ErrorHanding()
{
Console.WriteLine("出现错误!警报!!!!!!!!!!!!!!!!");
}
}
/// <summary>
/// 具体的错误处理方式类(模块)3,此为订阅者
/// </summary>
public class Handle3
{
/// <summary>
/// 出现错误时做出窗口抖动的处理
/// </summary>
public void ErrorHanding()
{
Console.WriteLine("出现错误!我抖!!!!!!!!!!!!!!!!!!!!!!!!");
}
}

专门进行错误处理的模块

/// <summary>
/// 系统错误处理相关的类(模块),此为信息的发布者
/// </summary>
public class ErrorAbout
{ /// <summary>
/// 错误处理的方法,主模块中通过调用此方法来触发一系列的错误处理
/// </summary>
public void ErrorHanding()
{
//实例化每一个订阅者,并调用其方法
Handle1 handle1 = new Handle1();
handle1.ErrorHanding();
Handle2 handle2 = new Handle2();
handle2.ErrorHanding();
Handle3 handle3 = new Handle3();
handle3.ErrorHanding(); }
}

主系统中:

class Program
{
static void Main(string[] args)
{
//假设在这个位置,系统出现了错误,需要进行错误处理
Console.WriteLine("系统出现了严重错误,需要进行一些处理~~~"); //实例化错误处理相关的类(发布者)
ErrorAbout errorAbout = new ErrorAbout();
//只要发布者的方法一执行,所有订阅者的方法也都会被执行
errorAbout.ErrorHanding(); Console.ReadKey();
}
}

运行结果如下:

这么做完全可以实现需要的功能

但是有何不妥呢?

在主模块中发现错误的时候new一个错误模块的实例,然后调用处理错误的方法

在错误模块中直接new各个子模块的实例,然后在处理错误的方法中依次执行

这样一来,错误模块和子模块之间就直接耦合在一起

这是面向过程的处理方式

在子模块方法发生改变的时候,或者错误模块需要添加新的处理错误的方法时

要对已经开发完毕的错误模块进行修改,违反了开放封闭原则

所以要对错误模块和子模块进行解耦(面向对象思想)

这时候就用到了观察者(Observer)模式,又称为发布-订阅模式等

实现的方法有两种

方法一:使用委托

方法二:使用接口

两种方法都实现了对错误模块和子模块的隔离

对两个模块的操作都是在主模块中完成的

Demo1:使用委托实现

具体错误处理方法的委托

/// <summary>
/// 具体错误处理方法的委托
/// </summary>
public delegate void ErrorHandle();
/// <summary>
/// 系统错误处理相关的类(模块),此为信息的发布者
/// </summary>
public class ErrorAbout
{
//定义一个 具体错误处理方法委托 的变量
private ErrorHandle errorHandle; //向外界提供一个可以向内部委托变量添加的方法
public void AddErrorHanding(ErrorHandle errorHandle)
{
//将传进来的方法加入委托变量中
if (this.errorHandle == null)
{
this.errorHandle = new ErrorHandle(errorHandle);
}
else
{
this.errorHandle += new ErrorHandle(errorHandle);
}
} /// <summary>
/// 错误处理的方法,主模块中通过调用此方法来触发一系列的错误处理
/// </summary>
public void ErrorHanding()
{
//调用委托,相当于调用了委托中的所有方法
errorHandle();
}
}

在使用委托时要注意

不能直接让外界操作内部的委托

一定要封装一个方法提供外界以一个安全的方式来操作内部的委托(为什么说这是一个安全的方式呢?因为在这个方法里面只能给委托添加方法,不能进行其他的任何操作)

如果直接将委托变量暴露给外界

那么外界就可以调用委托变量的所有方法

有可能会造成将原本的方法删除或者覆盖等情况

(这就是为什么会有事件这个东西存在的原因)

这也是一种面向对象的思想

在主模块中

class Program
{
static void Main(string[] args)
{
//假设在这个位置,系统出现了错误,需要进行错误处理
Console.WriteLine("系统出现了严重错误,需要进行一些处理~~~"); //实例化错误处理相关的类(发布者)
ErrorAbout errorAbout = new ErrorAbout();
//向发布者添加订阅者
Handle1 handle1 = new Handle1();
errorAbout.AddErrorHanding(handle1.ErrorHanding);
Handle2 handle2 = new Handle2();
errorAbout.AddErrorHanding(handle1.ErrorHanding);
Handle3 handle3 = new Handle3();
errorAbout.AddErrorHanding(handle1.ErrorHanding);
//只要发布者的方法一执行,所有订阅者的方法也都会被执行
errorAbout.ErrorHanding(); Console.ReadKey();
}
}

这样一来就实现了对错误模块和其他模块的解耦

任何错误具体的操作模块发生了变化

只要在其使用者--主模块中修改即可

Demo2:使用接口实现

首先需要提供一个统一的接口给具体的错误处理方式类(模块)

在发布者中可以通过这个接口调用实现了这个接口的所有订阅者

具体的错误处理方式类(模块)需要实现的接口

 /// <summary>
/// 具体的错误处理方式类(模块)需要实现的接口,在发布者中通过此接口可以统一调用订阅者的方法
/// </summary>
public interface IHandle
{
void ErrorHanding();
}

具体的错误处理方式类(模块)

 /// <summary>
/// 具体的错误处理方式类(模块)1,此为订阅者
/// </summary>
public class Handle1:IHandle
{
/// <summary>
/// 出现错误时做出发送邮件的处理
/// </summary>
public void ErrorHanding()
{
Console.WriteLine("出现错误!发送了一封邮件到管理员!");
}
}
/// <summary>
/// 具体的错误处理方式类(模块)2,此为订阅者
/// </summary>
public class Handle2:IHandle
{
/// <summary>
/// 出现错误时做出发出警报的处理
/// </summary>
public void ErrorHanding()
{
Console.WriteLine("出现错误!警报!!!!!!!!!!!!!!!!");
}
}
/// <summary>
/// 出现错误时做出窗口抖动的处理
/// </summary>
public void ErrorHanding()
{
Console.WriteLine("出现错误!我抖!!!!!!!!!!!!!!!!!!!!!!!!");
}

发布者

/// <summary>
/// 系统错误处理相关的类(模块),此为信息的发布者
/// </summary>
public class ErrorAbout
{
/// <summary>
/// 订阅者接口的集合
/// </summary>
private List<IHandle> handles = new List<IHandle>(); /// <summary>
/// 在主模块中可以通过此方法向 订阅者接口的集合 中添加新的订阅者(具体处理错误的方法)
/// </summary>
/// <param name="handle"></param>
public void AddErrorHanding(IHandle handle)
{
handles.Add(handle);
} /// <summary>
/// 错误处理的方法,主模块中通过调用此方法来触发一系列的错误处理
/// </summary>
public void ErrorHanding()
{
//遍历订阅者接口的集合
foreach (var handle in handles)
{
//执行集合中的每个错误处理的方法
handle.ErrorHanding();
}
}
}

主模块中

class Program
{
static void Main(string[] args)
{
//假设在这个位置,系统出现了错误,需要进行错误处理
Console.WriteLine("系统出现了严重错误,需要进行一些处理~~~");   //实例化错误处理相关的类(发布者)
ErrorAbout errorAbout = new ErrorAbout();
//向发布者添加订阅者
errorAbout.AddErrorHanding(new Handle1());
errorAbout.AddErrorHanding(new Handle2());
errorAbout.AddErrorHanding(new Handle3());
//只要发布者的方法一执行,所有订阅者的方法也都会被执行
errorAbout.ErrorHanding(); Console.ReadKey();
}
}

委托实现C#观察者模式简单例子下载:

点击打开链接

接口实现C#观察者模式简单例子下载:

点击打开链接

C#设计模式---观察者模式简单例子的更多相关文章

  1. Java-马士兵设计模式学习笔记-观察者模式-AWT简单例子

    1.AWT简单例子 TestFrame.java import java.awt.Button; import java.awt.Frame; import java.awt.event.Action ...

  2. JAVA设计模式之简单粗暴学建造者模式

    文章由浅入深,先用简单例子说明建造者,然后分析模式的优缺点,最后结合优秀开源框架Mybatis,说明该模式的用处. 1.先定义一个机器人模型 package com.jstao.model; publ ...

  3. 18. 星际争霸之php设计模式--观察者模式

    题记==============================================================================本php设计模式专辑来源于博客(jymo ...

  4. C#设计模式——观察者模式(Observer Pattern)1

    一.概述在软件设计工作中会存在对象之间的依赖关系,当某一对象发生变化时,所有依赖它的对象都需要得到通知.如果设计的不好,很容易造成对象之间的耦合度太高,难以应对变化.使用观察者模式可以降低对象之间的依 ...

  5. C#设计模式——观察者模式(Observer Pattern)

    一.概述在软件设计工作中会存在对象之间的依赖关系,当某一对象发生变化时,所有依赖它的对象都需要得到通知.如果设计的不好,很容易造成对象之间的耦合度太高,难以应对变化.使用观察者模式可以降低对象之间的依 ...

  6. C#设计模式之简单工厂模式

    简单工厂模式解释:  简单工厂模式(Simple Factory Pattern)属于类的创新型模式,又叫静态工厂方法模式(Static FactoryMethod Pattern) 是通过专门定义一 ...

  7. C#设计模式(2)——简单工厂模式(转)

    C#设计模式(2)——简单工厂模式   一.引言 这个系列也是自己对设计模式的一些学习笔记,希望对一些初学设计模式的人有所帮助的,在上一个专题中介绍了单例模式,在这个专题中继续为大家介绍一个比较容易理 ...

  8. js设计模式-观察者模式

    定义: 观察者模式又叫发布订阅模式,它定义了对象间的一种一对多的依赖关系.观察者模式让两个对象松耦合地联系在一起,虽然不太清楚彼此的细节,但这不影响他们之间的互相通信. 思路 定义一个对象,在对象中实 ...

  9. Jquery如何序列化form表单数据为JSON对象 C# ADO.NET中设置Like模糊查询的参数 从客户端出现小于等于公式符号引发检测到有潜在危险的Request.Form 值 jquery调用iframe里面的方法 Js根据Ip地址自动判断是哪个城市 【我们一起写框架】MVVM的WPF框架(三)—数据控件 设计模式之简单工厂模式(C#语言描述)

    jquery提供的serialize方法能够实现. $("#searchForm").serialize();但是,观察输出的信息,发现serialize()方法做的是将表单中的数 ...

随机推荐

  1. The UVALIVE 7716 二维区间第k小

    The UVALIVE 7716 二维区间第k小 /** 题意:给一个n * n的矩阵,有q个查询 每次查询r,c,s,k表示已(r,c)为右上角 大小为s的正方形中 第k小的元素 n <= 2 ...

  2. ServletContext结合Servlet接口中的init()方法和destroy()方法的运用----网站计数器

    我们一般知道Servlet接口中的init()方法在tomcat启动时调用,destroy()方法在tomcat关闭时调用.那么这两个方法到底在实际开发中有什么作用呢?这就是这个随笔主要讲的内容. 思 ...

  3. java 复习整理(四 String类详解)

    String 类详解   StringBuilder与StringBuffer的功能基本相同,不同之处在于StringBuilder是非线程安全的,而StringBuffer是线程安全的,因此效率上S ...

  4. RQNOJ123_多人背包_C++_Pascal

    题目:http://www.rqnoj.cn/problem/123 不得不说,RQNOJ 的机子跑得好慢呀,5*107 的数据范围本地跑 0.2s,服务器上愣是把我卡掉了,最后只好写了一份 Pasc ...

  5. C#中axWindowsMediaPlayer控件的用法

    属性/方法名: 说明:[基本属性] URL:String; 指定媒体位置,本机或网络地址uiMode:String; 播放器界面模式,可为Full, Mini, None, Invisibleplay ...

  6. 找出数字数组中最大的元素(使用Math.max函数)

    从汤姆大叔的博客里看到了6个基础题目:本篇是第1题 - 找出数字数组中最大的元素(使用Match.max函数) 从要求上来看,不能将数组sort.不能遍历.只能使用Math.max,所以只能从java ...

  7. 51nod 1101 换零钱 【完全背包变形/无限件可取】

    1101 换零钱  基准时间限制:1 秒 空间限制:131072 KB 分值: 20 难度:3级算法题  收藏  关注 N元钱换为零钱,有多少不同的换法?币值包括1 2 5分,1 2 5角,1 2 5 ...

  8. hadoop之linux常用命令

    Linux的命令后面会有命令选项,有的选项还有选项值.选项的前面有短横线“-”,命令.选项.选项值之间使用空格隔开.有的命令没有选项,会有参数.选项是命令内置的功能,参数是用户提供的符合命令格式的内容 ...

  9. Python的并发并行[2] -> 队列[0] -> queue 模块

    queue 模块 / queue Module 1 常量 / Constants Pass 2 函数 / Function Pass 3 类 / Class 3.1 Queue类 类实例化:queue ...

  10. Python的并发并行[2] -> 队列[1] -> 使用队列进行任务控制

    使用队列进行任务控制 1 FIFO与LIFO队列 FIFO(First In First Out)与LIFO(Last In First Out)分别是两种队列形式,在FIFO中,满足先入先出的队列方 ...