【设计模式】不同设计模式体现IOC控制反转
使用过Spring的开发者应该都对IOC控制反转功能有所了解,最开始学习时应该都知道使用依赖注入来实现IOC的功能,本文来介绍使用IOC控制反转思想的几种设计模式。
依赖注入来实现IOC
注入依赖是IOC最基本的一种实现方式,也是最常用的一种面向对象设计方式之一。注入依赖如何达到控制反转效果,先以一个例子开始:
public interface UserQueue {
void add(User user);
void remove(User user);
User get();
}
public abstract class AbstractUserQueue implements UserQueue {
protected LinkedList<User> queue = new LinkedList<>();
@Override
public void add(User user) {
queue.addFirst(user);
}
@Override
public void remove(User user) {
queue.remove(user);
}
@Override
public abstract User get();
}
public class UserFifoQueue extends AbstractUserQueue {
public User get() {
return queue.getLast();
}
}
public class UserLifoQueue extends AbstractUserQueue {
public User get() {
return queue.getFirst();
}
}
UserQueue 接口定义了公共的方法,用于在一个队列中去存放User对象。AbstractUserQueue则是为后续的继承类,提供了一些公用的方法实现。最后的UserFifoQueue 和 UserLifoQueue,则是分别实现了FIFO 和 LIFO 队列。
这是实现子类多态性的一种有效方式。
通过创建一个依赖于UserQueue抽象类型(也称为DI术语中的服务)的客户端类,可以在运行时注入不同的实现,无需会重构使用客户端类的代码:
public class UserProcessor {
private UserQueue userQueue;
public UserProcessor(UserQueue userQueue) {
this.userQueue = userQueue;
}
public void process() {
// process queued users here
}
}
UserProcessor展示了依赖注入确实是IOC的一种方式。
我们可以通过一些硬编码方式 如 new 操作,直接在构造函数中实例化在UserProcessor中获取对队列的依赖关系。但这是典型的代码硬编程,它引入了客户端类与其依赖关系之间的强耦合,并大大降低了可测性。
该类在构造函数中声明对抽象类 UserQueue 的依赖。也就是说,依赖关系不再通过在构造函数中使用 new 操作, 相反,通过外部注入的方式,要么使用依赖注入框架,要么使用factory或builders模式。
使用依赖注入,客户端类的依赖关系的控制,不再位于这些类中;而是在注入器中进行,看如下代码:
public static void main(String[] args) {
UserFifoQueue fifoQueue = new UserFifoQueue();
fifoQueue.add(new User("user1"));
fifoQueue.add(new User("user2"));
fifoQueue.add(new User("user3"));
UserProcessor userProcessor = new UserProcessor(fifoQueue);
userProcessor.process();
}
上述方式达到了预期效果,而且对UserLifoQueue的注入也简单明了。
观察者模式实现IOC
直接通过观察者模式实现IOC,也是一种常见的直观方式。广义上讲,通过观察者实现IOC,观察者模式通常用于在模型视图的上下文中,跟踪模型对象的状态的变迁。
在一个典型的实现中,一到多个观察者绑定到可观察对象(也称为模式术语中的主题),例如通过调用addObserver方法进行绑定。一旦定义了被观察者和观察者之间的绑定,则被观察者状态的变迁都会触发调用观察者的操作。看下面例子:
public interface SubjectObserver {
void update();
}
值发生改变时,会触发调用上述这个很简单的观察者。真实情况下,通常会提供功能更丰富的API,如需要保存变化的实例,或者新旧值,但是这些都不需要观察action(行为)模式,所以这里举例尽量简单。
下面,给出一个被观察者类:
public class User {
private String name;
private List<SubjectObserver> observers = new ArrayList<>();
public User(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
notifyObservers();
}
public String getName() {
return name;
}
public void addObserver(SubjectObserver observer) {
observers.add(observer);
}
public void deleteObserver(SubjectObserver observer) {
observers.remove(observer);
}
private void notifyObservers(){
observers.stream().forEach(observer -> observer.update());
}
}
User类中,当通过setter方法变更其状态事,都会触发调用绑定到它的观察者。
使用主题观察者和主题,以下是实例给出了观察方式:
public static void main(String[] args) {
User user = new User("John");
user.addObserver(() -> System.out.println(
"Observable subject " + user + " has changed its state."));
user.setName("Jack");
}
每当User对象的状态通过setter方法进行修改时,观察者将被通知并向控制台打印出一条消息。到目前为止,给出了观察者模式的一个简单用例。不过,通过这个看似简单的用例,我们了解到在这种情况下控制是如何实现反转的。
观察者模式下,主题就是起到”框架层“的作用,它完全主导何时何地去触发谁的调用。观察者的主动权被外放,因为观察者无法主导自己何时被调用(只要它们已经被注册到某个主题中的话)。这意味着,实际上我们可以发现控制被反转的”事发地“—— 当观察者绑定到主题时:
user.addObserver(() -> System.out.println(
"Observable subject " + user + " has changed its state."));
上述用例,简要说明了为什么观察者模式是实现IoC的一种非常简单的方式。正是以这种分散式设计软件组件的形式,使得控制得以发生反转。
模板方法模式实现IOC
模板方法模式实现的思想是在一个基类中通过几个抽象方法来定义一个通用的算法,然后让子类提供具体的实现,这样保证算法结构不变。
我们可以应用这个思想,定义一个通用的算法来处理领域实体,看例子:
public abstract class EntityProcessor {
public final void processEntity() {
getEntityData();
createEntity();
validateEntity();
persistEntity();
}
protected abstract void getEntityData();
protected abstract void createEntity();
protected abstract void validateEntity();
protected abstract void persistEntity();
}
processEntity() 方法是个模板方法,它定义了处理实体的算法,而抽象方法代表了算法的步骤,它们必须在子类中实现。通过多次继承 EntityProcessor 并实现不同的抽象方法,可以实现若干算法版本。
虽然这说清楚了模板方法模式背后的动机,但人们可能想知道为什么这是 IOC 的模式。
典型的继承中,子类调用基类中定义的方法。而这种模式下,相对真实的情况是:子类实现的方法(算法步骤)被基类的模板方法调用。因此,控制实际是在基类中进行的,而不是在子类中。
总结:
依赖注入:从客户端获得依赖关系的控制不再存在于这些类中。它存由底层的注入器 / DI 框架来处理。
观察者模式:当主体发生变化时,控制从观察者传递到主体。
模板方法模式:控制发生在定义模板方法的基类中,而不是实现算法步骤的子类中。
【设计模式】不同设计模式体现IOC控制反转的更多相关文章
- DI依赖注入/IOC控制反转
DI依赖注入# 啥都不说,直接上代码 <?php class UserController { private $user; function __construct(UserModel $us ...
- Spring框架中IoC(控制反转)的原理(转)
原文链接:Spring框架中IoC(控制反转)的原理 一.IoC的基础知识以及原理: 1.IoC理论的背景:在采用面向对象方法设计的软件系统中,底层实现都是由N个对象组成的,所有的对象通过彼此的合作, ...
- Spring专题2: DI,IOC 控制反转和依赖注入
合集目录 Spring专题2: DI,IOC 控制反转和依赖注入 https://docs.spring.io/spring/docs/2.5.x/reference/aop.html https:/ ...
- .net 温故知新:【7】IOC控制反转,DI依赖注入
IOC控制反转 大部分应用程序都是这样编写的:编译时依赖关系顺着运行时执行的方向流动,从而生成一个直接依赖项关系图. 也就是说,如果类 A 调用类 B 的方法,类 B 调用 C 类的方法,则在编译时, ...
- 谈谈php里的IOC控制反转,DI依赖注入
理论 发现问题 在深入细节之前,需要确保我们理解"IOC控制反转"和"DI依赖注入"是什么,能够解决什么问题,这些在维基百科中有非常清晰的说明. 控制反转(In ...
- [转帖]什么是IOC(控制反转)、DI(依赖注入)
什么是IOC(控制反转).DI(依赖注入) 2018-08-22 21:29:13 Ming339456 阅读数 20642 原文地址(摘要了部分内容):https://blog.csdn.net ...
- 谈谈php里的IOC控制反转,DI依赖注入(转)
转自:http://www.cnblogs.com/qq120848369/p/6129483.html 发现问题 在深入细节之前,需要确保我们理解"IOC控制反转"和" ...
- Spring第一课:IOC控制反转,什么是反转,什么又是控制?
前言 学习Spring第一课,就是认识IOC控制反转,要了解它还真得花一些功夫.今天主要理解透彻它的真谛,而不仅限于表面. 上道小菜 public class BusinessService { pr ...
- Spring IOC(控制反转)思想笔记
Spring IOC(控制反转)思想笔记 IOC控制反转基本理念就是将程序控制权从程序员手中交给用户自定义,从而避免了因为用户一个小需求的变化使得程序员需要改动大量代码. 案例 如果按照之前javaw ...
随机推荐
- day_11函数的形参与实参
昨天讲了函数的定义和简单分类 1:什么是函数:具体特定功能的代码快 --特定功能代码作为一个整体,并给该整体命名,就是函数. 函数的优点: 1:减少代码的冗余 2:结构清晰,可读性强 3:具有复用性, ...
- Java基础知识提要
1. 简述Java的基本历史 java起源于SUN公司的一个GREEN的项目,其原先目的是:为家用消费电子产品发送一个信息的分布式代码系统,通过发送信息控制电视机.冰箱等 2. 简单写出Java特 ...
- 利用WindowsServiceWrapper(WinSW)将nginx包装为系统服务
1.WindowsServiceWrapper(WinSW) Github:https://github.com/kohsuke/winsw/ 下载地址:http://repo.jenkins-ci. ...
- JMagic 操作 ImageMagick 处理图片
项目描述 imagemagick是功能强大的图片处理库,以稳定及高效率著称,众多语言对该库进行封装处理.比如php.java.由于我们是java项目,直接使用java通过JNI技术调用ImageMag ...
- dubbo实用知识点总结(二)
1. 参数验证 2. 结果缓存 3. 泛化引用 客户端没有对应接口类的情况,可以直接调用 4. 泛化实现 5. 回声测试 用于检测服务是否可用 6. 上下文信息 7. 隐式传参(不常用) 8. 异步调 ...
- scws简单中文分词
demo如下: /** * 中文分词 * @param $keyword * @param $getTop * @param $limit * @return array */ function sp ...
- springboot2.0jar包启动异常
今天碰到一个异常: 08:44:07.214 [main] ERROR org.springframework.boot.SpringApplication - Application run fai ...
- 搭建gitlab本地服务
一本正经的搞事情 改变懒惰,成就心中的自己,并非难事 博客园 首页 新随笔 联系 管理 订阅 随笔- 40 文章- 0 评论- 5 gitlab服务部署及使用 目录 一.什么是gitlib ...
- IT老人,给后辈的十一点建议
我已经在IT业打拼9年了,从完全自学成为技术团队leader到PM也确实总结了不少的经验,自己也经常跟学弟学妹聊天,分享职场经验,当老家有人报考计算机或者从事相关工作时也会咨询我的意见,我很明白IT人 ...
- MacOS下命令行安装神器brew
1.安装brew:/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/mast ...