设计模式专栏:http://t.csdnimg.cn/U54zu
目录
一、引言
核心概念
应用场景
可以解决的问题
二、场景案例
2.1 不用设计模式实现
2.2 存在问题
2.3 使用设计模式实现
2.4 成功克服
三、工作原理
3.1 结构图和说明
3.2 工作原理详解
3.3 实现步骤
四、 优势
4.1 好处和优势
4.2 应用示例
4.3 系统性优势
五、局限性和注意事项
5.1 局限性与不适用的场景
5.2 实际应用中的注意事项与建议
一、引言
核心概念
优雅方案
观察者模式的核心概念主要包含以下几个关键部分: 1. 主题(Subject):
2. 观察者(Observer):
3. 具体主题(Concrete Subject):
4. 具体观察者(Concrete Observer):
5. 状态(State):
6. 更新接口(Update Method):
|
简单而言
观察者模式允许对象之间建立一种一对多的依赖关系,当一个对象改变状态时,所有依赖它的对象都会得到通知并且自动更新。这种通知机制使得对象之间的通信更加松散耦合、灵活可扩展。无需直接相互引用,各个对象能够独立变化而互不影响。通过观察者模式,我们能够轻松实现实时更新、动态同步等功能,从而为我们的应用带来更好的用户体验和可维护性。无论是构建响应式界面、事件驱动系统,还是实现即时通信、发布-订阅模式,观察者模式都是一个不可或缺的设计选择。 |
接下来,让我们深入探索观察者模式的内部工作原理和实际应用案例,享受软件开发的乐趣吧!
应用场景
观察者模式在软件设计中的应用场景
1. 用户界面(UI)交互:
2. 事件监测系统:
3. 发布/订阅系统:
4. 数据模型与视图同步:
|
可以解决的问题
在现代软件开发中,组件间的交互和状态同步是一项常见而又至关重要的挑战。随着业务逻辑的复杂化及用户需求的不断变化,如何设计出灵活、低耦合的系统成为软件工程师面临的一大课题。对于系统中存在的一个实体状态改变需要通知到一个或多个依赖该状态的实体的场景,如何高效率地处理这种状态同步与通知呢?传统的紧耦合联系很难应对系统的迅速变化和扩展,而观察者模式在此场景下应运而生,提供了一种优雅且实用的设计解决方案。 |
使用观察者模式可以解决的问题包括但不限于
|
二、场景案例
经典场景:新闻发布系统
最经典的观察者模式场景之一是“新闻发布系统”。在这个场景中,有多个订阅者(观察者)对新闻感兴趣,他们希望在有新闻发布时能够立即得到通知。系统管理员(主题)负责发布新闻。 |
2.1 不用设计模式实现
一坨坨代码实现
import java.util.ArrayList;
import java.util.List; // 新闻类
class News { private String content; public News(String content) { this.content = content; } public String getContent() { return content; }
} // 新闻发布器
class NewsPublisher { private List<NewsSubscriber> subscribers = new ArrayList<>(); // 订阅新闻 public void subscribe(NewsSubscriber subscriber) { subscribers.add(subscriber); } // 取消订阅 public void unsubscribe(NewsSubscriber subscriber) { subscribers.remove(subscriber); } // 发布新闻 public void publish(News news) { for (NewsSubscriber subscriber : subscribers) { subscriber.receiveNews(news); } }
} // 新闻订阅者接口
interface NewsSubscriber { void receiveNews(News news);
} // 网页新闻订阅者
class WebNewsSubscriber implements NewsSubscriber { @Override public void receiveNews(News news) { System.out.println("WebNewsSubscriber: News received - " + news.getContent()); }
} // 邮件新闻订阅者
class EmailNewsSubscriber implements NewsSubscriber { @Override public void receiveNews(News news) { System.out.println("EmailNewsSubscriber: News received - " + news.getContent()); }
} // 客户端代码
public class NewsSystemClient { public static void main(String[] args) { NewsPublisher publisher = new NewsPublisher(); NewsSubscriber webSubscriber = new WebNewsSubscriber(); NewsSubscriber emailSubscriber = new EmailNewsSubscriber(); publisher.subscribe(webSubscriber); publisher.subscribe(emailSubscriber); News news = new News("Breaking News: World Peace Achieved!"); publisher.publish(news); }
}
在这个实现中,NewsPublisher
类充当了新闻发布的中心角色,它维护了一个订阅者列表。当有新闻发布时,NewsPublisher
会遍历订阅者列表,并调用每个订阅者的 receiveNews
方法来传递新闻。
2.2 存在问题
上述不使用设计模式实现的新闻发布系统确实存在一些问题,尽管它能够实现基本的新闻发布和接收功能。以下还是存在如紧耦合、缺乏灵活性、可扩展性差、错误处理不足、缺乏抽象层次 和 动态性受限 等问题。 |
2.3 使用设计模式实现
1. 观察者接口(Observer):定义一个更新方法,当新闻发布时,所有观察者都将调用此方法。
public interface Observer { void update(String news);
}
2. 具体观察者(ConcreteObserver):实现观察者接口,当有新闻发布时,执行具体的操作。例如,将新闻显示在网页上、发送到用户的电子邮箱等。
public class WebObserver implements Observer { @Override public void update(String news) { System.out.println("WebObserver: Display news on website - " + news); }
} public class EmailObserver implements Observer { @Override public void update(String news) { System.out.println("EmailObserver: Send news to email - " + news); }
}
3. 主题接口(Subject):定义一个注册观察者、移除观察者和通知观察者的方法。
import java.util.ArrayList;
import java.util.List; public interface Subject { void registerObserver(Observer observer); void removeObserver(Observer observer); void notifyObservers(String news);
}
4. 具体主题(ConcreteSubject):实现主题接口,维护一个观察者列表,并在有新闻发布时通知所有观察者。
public class NewsSubject implements Subject { private List<Observer> observers = new ArrayList<>(); @Override public void registerObserver(Observer observer) { observers.add(observer); } @Override public void removeObserver(Observer observer) { observers.remove(observer); } @Override public void notifyObservers(String news) { for (Observer observer : observers) { observer.update(news); } }
}
5. 客户端代码(Client):创建主题和观察者对象,并将观察者注册到主题上。当有新闻发布时,主题会通知所有观察者。
public class Client { public static void main(String[] args) { NewsSubject newsSubject = new NewsSubject(); Observer webObserver = new WebObserver(); Observer emailObserver = new EmailObserver(); newsSubject.registerObserver(webObserver); newsSubject.registerObserver(emailObserver); newsSubject.notifyObservers("Breaking News: World Peace Achieved!"); }
}
在这个场景中,新闻发布系统(主题)负责发布新闻,而网页观察者(WebObserver)和电子邮件观察者(EmailObserver)则负责在新闻发布时显示和发送新闻。通过使用观察者模式,系统管理员可以轻松地添加或移除观察者,而无需修改主题代码。此外,当有新闻发布时,所有观察者都会自动收到通知并更新,从而实现了松耦合和可扩展性。
2.4 成功克服
使用观察者模式在上述示例中成功克服了多个问题,这些问题在使用直接的方法调用和对象间显式交互时可能会出现。以下是观察者模式成功克服的问题:
1. 紧耦合:
2. 缺乏灵活性:
3. 可扩展性差:
4. 错误处理不足:
5. 缺乏抽象层次:
6. 动态性受限:
|
三、工作原理
3.1 结构图和说明
- Subject:目标对象,通常具有如下功能。
- Observer:定义观察者的接又,提供目标通知时对应的更新方法,这个更新方法进行相应的业务处理,可以在这个方法里面回调目标对象,以获取目标对象的数据。
-
ConcreteSubject:具体的目标实现对象,用来维护目标状态,当目标对象的状态发生改变时,通知所有注册的、有效的观察者,让观察者执行相应的处理
-
ConcreteObserver:观察者的具体实现对象,用来接收目标的通知,并进行相应的后续处理,比如更新自身的状态以保持和目标的相应状态一致。
3.2 工作原理详解
1. 注册与订阅:
2. 状态变化:
3. 通知更新:
4. 解耦:
5. 灵活性和扩展性:
|
3.3 实现步骤
使用观察者模式实现功能时,你可以按照以下思考步骤进行:
1. 定义主题和观察者之间的关系:
2. 设计通用接口:
3. 实现注册与移除观察者功能:
4. 定义通知机制:
5. 更新观察者状态:
6. 考虑线程安全与性能问题:
7. 实现解耦:
8. 处理异常与错误:
9. 测试:
10. 细节优化:
|
在应用观察者模式时,始终需要关注设计的整体清晰度、灵活性以及扩展性,确保最终实现的模式适合应用的上下文环境。
四、 优势
4.1 好处和优势
使用观察者模式可以带来以下明显的好处和优势:
1. 解耦:
2. 灵活性:
3. 动态响应:
4. 简化通信:
|
4.2 应用示例
考虑一个电商平台上的商品定价系统。传统的设计方法可能会要求每个依赖商品价格信息的组件都直接从价格数据库中获取更新。随着系统的发展,这种紧耦合的方式导致了若干问题:每当价格更新逻辑变化时,所有依赖组件都需要作出相应修改;系统的可扩展性差,添加新的依赖组件会带来额外的维护负担。 |
采用观察者模式改进后,价格系统作为主题,各个依赖组件(如库存管理、促销引擎、前端显示等)作为观察者。当商品价格更新时,价格系统仅需通知这些观察者。这样,库存管理系统可以自动调整库存采购策略,促销引擎可以同步更新促销活动,用户界面也可以即时显示最新价格。这种方式不仅使得价格更新流程更加清晰,而且让各组件能够更加独立地开发和维护。 |
4.3 系统性优势
观察者模式通过解耦观察者和被观察者之间的关系,提高了系统的灵活性、扩展性和可维护性。具体来说:
1. 灵活性:
2. 扩展性:
3. 可维护性:
|
综上所述,观察者模式通过解耦观察者和被观察者之间的关系,提高了系统的灵活性、扩展性和可维护性。它简化了对象之间的通信和协作,使得代码更加清晰、简洁和易于维护。因此,在现代软件开发中,观察者模式被广泛应用于处理不同组件之间的高效通信和动态响应问题。
五、局限性和注意事项
5.1 局限性与不适用的场景
尽管观察者模式为软件开发带来了许多好处,但在某些情况下,它也可能存在局限性和不适用的场景:
1. 复杂的依赖关系:
2. 性能考虑:
3. 循环依赖:
4. 错误处理:
|
5.2 实际应用中的注意事项与建议
1. 推送 vs 拉取:
2. 通知顺序:
3. 通知效率:
4. 循环依赖:
5. 内存管理:
6. 异常处理:
7. 状态一致性:
8. 设计模式的组合使用:
|
在遵守这些注意事项和建议的同时,应该记住设计模式不是万能的,不应该强行适配模式。在选择应用观察者模式前,确保它适合当前的问题场景,并充分考虑它可能带来的设计复杂性。