响应式网站 开发,广州越秀网站制作,一个做微信文章的网站,兰州新区城乡建设管理局网站软件设计模式#xff08;四#xff09; 观察者模式一、观察者模式#xff08;Observer Pattern#xff09;1. 概念2. 模式结构3. UML 类图4. 实现方式C# 示例步骤1#xff1a;定义观察者接口步骤2#xff1a;定义主题接口步骤3#xff1a;实现具体主题步骤4#xff1a;… 软件设计模式四 观察者模式一、观察者模式Observer Pattern1. 概念2. 模式结构3. UML 类图4. 实现方式C# 示例步骤1定义观察者接口步骤2定义主题接口步骤3实现具体主题步骤4实现具体观察者步骤5使用观察者模式 Java 示例步骤1定义观察者接口步骤2定义主题接口步骤3实现具体主题步骤4实现具体观察者步骤5使用观察者模式 5. 优点6. 缺点7. 应用场景 二、观察者模式的变体与深入应用1. 观察者模式的变体变体1: 推与拉模式Push vs. Pull Model变体2: 同步与异步通知变体3: 链式观察者模式变体4: 优先级观察者 2. 实际应用场景场景1: GUI 事件系统中的事件分发场景2: 数据流处理与实时监控场景3: 发布-订阅系统场景4: MVCModel-View-Controller模式场景5: 微服务架构中的事件驱动 3. 观察者模式的实际挑战问题1: 过度依赖问题2: 循环依赖问题3: 内存泄漏 总结 观察者模式
一、观察者模式Observer Pattern
1. 概念
观察者模式是一种行为型设计模式它定义了一种一对多的依赖关系当一个对象的状态发生改变时所有依赖它的对象都会得到通知并自动更新。观察者模式常用于实现分布式事件处理系统特别是在GUI框架和事件驱动的系统中。
通过观察者模式多个对象可以观察同一个主题Subject当主题状态变化时所有观察者Observer都会收到通知并做出相应的响应。
2. 模式结构
观察者模式包含以下几个关键角色 主题Subject持有观察者的引用并负责管理观察者添加、移除、通知等。 观察者Observer定义一个更新接口当主题状态变化时更新自己。 具体主题Concrete Subject实现主题的通知逻辑维护内部状态并在状态变化时通知观察者。 具体观察者Concrete Observer实现观察者的更新接口根据主题通知的变化做出响应。
3. UML 类图 #mermaid-svg-P0ikQv9pVrBQ7vp7 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-P0ikQv9pVrBQ7vp7 .error-icon{fill:#552222;}#mermaid-svg-P0ikQv9pVrBQ7vp7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-P0ikQv9pVrBQ7vp7 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-P0ikQv9pVrBQ7vp7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-P0ikQv9pVrBQ7vp7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-P0ikQv9pVrBQ7vp7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-P0ikQv9pVrBQ7vp7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-P0ikQv9pVrBQ7vp7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-P0ikQv9pVrBQ7vp7 .marker.cross{stroke:#333333;}#mermaid-svg-P0ikQv9pVrBQ7vp7 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-P0ikQv9pVrBQ7vp7 g.classGroup text{fill:#9370DB;fill:#131300;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-P0ikQv9pVrBQ7vp7 g.classGroup text .title{font-weight:bolder;}#mermaid-svg-P0ikQv9pVrBQ7vp7 .nodeLabel,#mermaid-svg-P0ikQv9pVrBQ7vp7 .edgeLabel{color:#131300;}#mermaid-svg-P0ikQv9pVrBQ7vp7 .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-P0ikQv9pVrBQ7vp7 .label text{fill:#131300;}#mermaid-svg-P0ikQv9pVrBQ7vp7 .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-P0ikQv9pVrBQ7vp7 .classTitle{font-weight:bolder;}#mermaid-svg-P0ikQv9pVrBQ7vp7 .node rect,#mermaid-svg-P0ikQv9pVrBQ7vp7 .node circle,#mermaid-svg-P0ikQv9pVrBQ7vp7 .node ellipse,#mermaid-svg-P0ikQv9pVrBQ7vp7 .node polygon,#mermaid-svg-P0ikQv9pVrBQ7vp7 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-P0ikQv9pVrBQ7vp7 .divider{stroke:#9370DB;stroke:1;}#mermaid-svg-P0ikQv9pVrBQ7vp7 g.clickable{cursor:pointer;}#mermaid-svg-P0ikQv9pVrBQ7vp7 g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-P0ikQv9pVrBQ7vp7 g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-P0ikQv9pVrBQ7vp7 .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-P0ikQv9pVrBQ7vp7 .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-P0ikQv9pVrBQ7vp7 .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-P0ikQv9pVrBQ7vp7 .dashed-line{stroke-dasharray:3;}#mermaid-svg-P0ikQv9pVrBQ7vp7 #compositionStart,#mermaid-svg-P0ikQv9pVrBQ7vp7 .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-P0ikQv9pVrBQ7vp7 #compositionEnd,#mermaid-svg-P0ikQv9pVrBQ7vp7 .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-P0ikQv9pVrBQ7vp7 #dependencyStart,#mermaid-svg-P0ikQv9pVrBQ7vp7 .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-P0ikQv9pVrBQ7vp7 #dependencyStart,#mermaid-svg-P0ikQv9pVrBQ7vp7 .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-P0ikQv9pVrBQ7vp7 #extensionStart,#mermaid-svg-P0ikQv9pVrBQ7vp7 .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-P0ikQv9pVrBQ7vp7 #extensionEnd,#mermaid-svg-P0ikQv9pVrBQ7vp7 .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-P0ikQv9pVrBQ7vp7 #aggregationStart,#mermaid-svg-P0ikQv9pVrBQ7vp7 .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-P0ikQv9pVrBQ7vp7 #aggregationEnd,#mermaid-svg-P0ikQv9pVrBQ7vp7 .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-P0ikQv9pVrBQ7vp7 .edgeTerminals{font-size:11px;}#mermaid-svg-P0ikQv9pVrBQ7vp7 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Subject Attach() Detach() Notify() Observer Update() ConcreteObserver Update() 4. 实现方式
C# 示例
步骤1定义观察者接口
public interface IObserver
{void Update(string message);
}步骤2定义主题接口
public interface ISubject
{void Attach(IObserver observer);void Detach(IObserver observer);void Notify();
}步骤3实现具体主题
public class ConcreteSubject : ISubject
{private ListIObserver _observers new ListIObserver();private string _subjectState;public string State{get { return _subjectState; }set{_subjectState value;Notify();}}public void Attach(IObserver observer){_observers.Add(observer);}public void Detach(IObserver observer){_observers.Remove(observer);}public void Notify(){foreach (var observer in _observers){observer.Update(_subjectState);}}
}步骤4实现具体观察者
public class ConcreteObserver : IObserver
{private string _name;public ConcreteObserver(string name){_name name;}public void Update(string message){Console.WriteLine(${_name} received message: {message});}
}步骤5使用观察者模式
class Program
{static void Main(string[] args){// 创建主题ConcreteSubject subject new ConcreteSubject();// 创建观察者IObserver observer1 new ConcreteObserver(Observer 1);IObserver observer2 new ConcreteObserver(Observer 2);// 注册观察者subject.Attach(observer1);subject.Attach(observer2);// 改变主题状态并通知观察者subject.State State changed!;// Output: // Observer 1 received message: State changed!// Observer 2 received message: State changed!}
}Java 示例
步骤1定义观察者接口
public interface Observer {void update(String message);
}步骤2定义主题接口
public interface Subject {void attach(Observer observer);void detach(Observer observer);void notifyObservers();
}步骤3实现具体主题
import java.util.ArrayList;
import java.util.List;public class ConcreteSubject implements Subject {private ListObserver observers new ArrayList();private String state;public void setState(String state) {this.state state;notifyObservers();}Overridepublic void attach(Observer observer) {observers.add(observer);}Overridepublic void detach(Observer observer) {observers.remove(observer);}Overridepublic void notifyObservers() {for (Observer observer : observers) {observer.update(state);}}
}步骤4实现具体观察者
public class ConcreteObserver implements Observer {private String name;public ConcreteObserver(String name) {this.name name;}Overridepublic void update(String message) {System.out.println(name received message: message);}
}步骤5使用观察者模式
public class Main {public static void main(String[] args) {ConcreteSubject subject new ConcreteSubject();Observer observer1 new ConcreteObserver(Observer 1);Observer observer2 new ConcreteObserver(Observer 2);subject.attach(observer1);subject.attach(observer2);subject.setState(New State);// Output:// Observer 1 received message: New State// Observer 2 received message: New State}
}5. 优点
松耦合: 观察者与主题之间的耦合度较低主题不需要了解观察者的具体实现。动态观察者管理: 可以随时添加或移除观察者不影响系统的其他部分。事件通知机制: 广泛应用于事件驱动的系统中实现异步通知机制。
6. 缺点
通知顺序问题: 多个观察者的通知顺序可能无法确定导致结果的不可预测性。性能开销: 如果观察者数量过多通知操作可能带来性能开销。内存泄漏风险: 如果没有正确地解除观察者注册可能会造成内存泄漏。
7. 应用场景
场景1: GUI事件系统 观察者模式广泛用于GUI应用程序中按钮的点击、文本框的输入等事件都可以通过观察者模式通知相关对象。场景2: 订阅-发布系统 在消息系统中发布者可以是主题订阅者是观察者。当发布者有新消息时订阅者会收到通知。场景3: 数据绑定 在现代前端框架如React、Vue等中观察者模式用于实现数据的双向绑定当数据变化时自动更新视图。
二、观察者模式的变体与深入应用
1. 观察者模式的变体
观察者模式虽然简单但可以通过不同的方式进行扩展和变体以适应更复杂的场景。以下是几种常见的变体
变体1: 推与拉模式Push vs. Pull Model
在经典的观察者模式中通常采用推Push模式即主题在状态改变时主动将更新数据推送给所有观察者。而在拉Pull模式中主题只通知观察者有变化但具体数据由观察者自行从主题中拉取。
推模式主题向观察者提供足够的更新数据观察者直接获取并处理。适合更新数据明确的场景。拉模式主题只提供最小的信息观察者决定是否拉取完整数据通常用于复杂的更新场景以减少不必要的传输。
// 拉模式下Observer在收到通知后主动获取数据
public void Update(ConcreteSubject subject)
{var state subject.GetState();Console.WriteLine($Observer fetched new state: {state});
}变体2: 同步与异步通知
经典的观察者模式通常是同步通知即主题在通知观察者时所有观察者都同步更新。但在某些高负载或分布式系统中可以采用异步通知即主题在状态改变时异步通知观察者允许观察者在不同的时间点处理更新。
同步通知适合简单或小规模系统能确保观察者在通知后立即获取最新状态。异步通知适合需要并发处理或系统延迟的场景如消息队列、分布式系统。
变体3: 链式观察者模式
在链式观察者模式中观察者之间形成链式依赖一个观察者更新后会通知下一个观察者依次传递下去。适合于需要逐步更新的场景如责任链模式与观察者模式结合使用。
public class ChainedObserver : IObserver
{private IObserver _nextObserver;public ChainedObserver(IObserver nextObserver){_nextObserver nextObserver;}public void Update(string message){Console.WriteLine($Processing in current observer: {message});_nextObserver?.Update(message);}
}变体4: 优先级观察者
在某些系统中可能需要不同观察者根据优先级来接收通知。优先级观察者模式允许为每个观察者分配一个优先级主题按照优先级的顺序通知观察者。这样可以确保关键观察者优先响应减少延迟。
// 基于优先级的通知系统
public class PriorityObserver : IObserver, IComparablePriorityObserver
{public int Priority { get; }public PriorityObserver(int priority){Priority priority;}public void Update(string message){Console.WriteLine($Priority {Priority} observer received: {message});}public int CompareTo(PriorityObserver other){return Priority.CompareTo(other.Priority);}
}2. 实际应用场景
场景1: GUI 事件系统中的事件分发
现代GUI应用程序如WPF、Swing、WinForms等经常使用观察者模式处理用户界面上的事件。例如当用户点击按钮时按钮是主题它的点击事件会触发多个观察者如回调函数、事件处理程序以执行相关逻辑。
推模式可以将用户操作的详细信息如点击坐标、鼠标状态推送给观察者。拉模式观察者只收到“按钮被点击”这一基本通知然后自行获取相关的事件数据。
场景2: 数据流处理与实时监控
在金融市场、物联网、实时监控等场景中观察者模式可以用于数据流的处理与监控。例如股票价格实时变动时所有观察者投资分析工具、交易平台等都会被通知。
异步通知由于数据量大可以通过消息队列实现异步更新观察者可以根据需要处理数据流。优先级通知关键监控设备或算法可能需要优先处理以确保及时响应市场变化。
场景3: 发布-订阅系统
观察者模式在发布-订阅系统中广泛使用。发布者主题发布消息时所有订阅者观察者会接收消息并更新。这种系统应用于多种场景如新闻订阅、RSS订阅、电子邮件通知等。
拉模式订阅者可以根据消息头信息判断是否需要拉取完整内容适合减少带宽占用的场景。异步处理特别适合分布式系统订阅者可以异步接收消息并处理。
场景4: MVCModel-View-Controller模式
MVC是软件开发中的一种常见架构模式观察者模式在其中扮演了重要角色。模型Model作为主题持有应用程序的数据当数据改变时通知视图View视图可以通过观察者模式动态更新界面。控制器Controller作为观察者与模型交互推动状态的变化。
// MVC中的简单应用
public class Model : ISubject
{private ListIObserver _observers new ListIObserver();private string _data;public string Data{get _data;set{_data value;Notify();}}public void Attach(IObserver observer) _observers.Add(observer);public void Detach(IObserver observer) _observers.Remove(observer);public void Notify(){foreach (var observer in _observers)observer.Update(_data);}
}public class View : IObserver
{public void Update(string message){Console.WriteLine($View updated with new data: {message});}
}场景5: 微服务架构中的事件驱动
在微服务架构中不同的服务之间通过事件进行通信。每个服务可以作为一个观察者当某个服务发布事件时所有依赖的服务都会收到通知并做出相应处理。这种架构通过事件驱动模式实现高解耦和灵活扩展。
异步通知通过消息队列如RabbitMQ、Kafka实现异步事件驱动确保服务的独立性与高效通信。优先级处理某些关键服务可以优先获取事件并立即作出响应。 3. 观察者模式的实际挑战
问题1: 过度依赖
观察者模式虽然在解耦上提供了灵活性但在某些复杂场景下系统可能会过度依赖观察者导致难以追踪更新的来源和原因。
问题2: 循环依赖
如果多个观察者之间相互依赖可能会引发循环通知的问题导致系统陷入死循环或过多的无效更新。
问题3: 内存泄漏
如果主题没有正确地管理观察者如没有移除已经不需要的观察者可能会导致内存泄漏特别是在长生命周期的系统中。 总结
观察者模式的变体为不同的应用场景提供了灵活性从推拉模型到优先级处理和异步通知它适用于从简单的GUI事件系统到复杂的分布式微服务架构。在选择具体变体时需要权衡性能、响应速度、解耦程度等多方面因素以便为系统设计提供最佳的解决方案。