使用过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控制反转的更多相关文章

  1. DI依赖注入/IOC控制反转

    DI依赖注入# 啥都不说,直接上代码 <?php class UserController { private $user; function __construct(UserModel $us ...

  2. Spring框架中IoC(控制反转)的原理(转)

    原文链接:Spring框架中IoC(控制反转)的原理 一.IoC的基础知识以及原理: 1.IoC理论的背景:在采用面向对象方法设计的软件系统中,底层实现都是由N个对象组成的,所有的对象通过彼此的合作, ...

  3. Spring专题2: DI,IOC 控制反转和依赖注入

    合集目录 Spring专题2: DI,IOC 控制反转和依赖注入 https://docs.spring.io/spring/docs/2.5.x/reference/aop.html https:/ ...

  4. .net 温故知新:【7】IOC控制反转,DI依赖注入

    IOC控制反转 大部分应用程序都是这样编写的:编译时依赖关系顺着运行时执行的方向流动,从而生成一个直接依赖项关系图. 也就是说,如果类 A 调用类 B 的方法,类 B 调用 C 类的方法,则在编译时, ...

  5. 谈谈php里的IOC控制反转,DI依赖注入

    理论 发现问题 在深入细节之前,需要确保我们理解"IOC控制反转"和"DI依赖注入"是什么,能够解决什么问题,这些在维基百科中有非常清晰的说明. 控制反转(In ...

  6. [转帖]什么是IOC(控制反转)、DI(依赖注入)

    什么是IOC(控制反转).DI(依赖注入) 2018-08-22 21:29:13 Ming339456 阅读数 20642   原文地址(摘要了部分内容):https://blog.csdn.net ...

  7. 谈谈php里的IOC控制反转,DI依赖注入(转)

    转自:http://www.cnblogs.com/qq120848369/p/6129483.html 发现问题 在深入细节之前,需要确保我们理解"IOC控制反转"和" ...

  8. Spring第一课:IOC控制反转,什么是反转,什么又是控制?

    前言 学习Spring第一课,就是认识IOC控制反转,要了解它还真得花一些功夫.今天主要理解透彻它的真谛,而不仅限于表面. 上道小菜 public class BusinessService { pr ...

  9. Spring IOC(控制反转)思想笔记

    Spring IOC(控制反转)思想笔记 IOC控制反转基本理念就是将程序控制权从程序员手中交给用户自定义,从而避免了因为用户一个小需求的变化使得程序员需要改动大量代码. 案例 如果按照之前javaw ...

随机推荐

  1. 《HTTP权威指南》4-连接管理

    TCP连接 TCP/IP是全球计算机及网络设备都在使用的一种常见的分组交换网络分层协议集客户端应用程序可以打开一条TCP/IP连接.连接到可能运行在世界任何地方的服务器应用程序 TCP的可靠数据管道 ...

  2. [转]Examining Open vSwitch Traffic Patterns

    In this post, I want to provide some additional insight on how the use of Open vSwitch (OVS) affects ...

  3. 吴恩达机器学习笔记39-误差分析与类偏斜的误差度量(Error Analysis and Error Metrics for Skewed Classes)

    如果你准备研究机器学习的东西,或者构造机器学习应用程序,最好的实践方法不是建立一个非常复杂的系统,拥有多么复杂的变量:而是构建一个简单的算法,这样你可以很快地实现它. 构建一个学习算法的推荐方法为:1 ...

  4. 第68节:Java中的MYSQL运用从小白到大牛

    第68节:Java中的MYSQL运用从小白到大牛 前言 学习java必备要求,学会运用!!! 常见关系化数据库 BootStrap是轻量级开发响应式页面的框架,全局css组件,js插件.栅格系统是将页 ...

  5. Kali学习笔记40:SQL手工注入(2)

    上一篇讲到可以通过注入得到数据库中所有的表信息 而SQL注入能不能做数据库之外的事情呢? 读取文件: ' union select null,load_file('/etc/passwd') -- 为 ...

  6. 两步验证杀手锏:Java 接入 Google 身份验证器实战

    两步验证 大家应该对两步验证都熟悉吧?如苹果有自带的两步验证策略,防止用户账号密码被盗而锁定手机进行敲诈,这种例子屡见不鲜,所以苹果都建议大家开启两步验证的. Google 的身份验证器一般也是用于登 ...

  7. MySQL基础整理(一)之SQL基础(未完成)

    大家好,我是浅墨竹染,以下是MySQL基础整理(一)之SQL基础 1.SQL简介 SQL(Structure Query Language)是一种结构化查询语言,是使用关系模型的数据库应用语言. 2. ...

  8. pyengine介绍及使用

    一个可以通过HTTP请求动态执行Python 代码的HTTP服务器,还自带一个装饰器来执行时间较长的任务. 使用方法 1  pip install pyengine 2 pyengine run -d ...

  9. Java高阶回调,回调函数的另一种玩法

    工具类package com.sctek; import java.lang.reflect.Field; import android.os.CountDownTimer;import androi ...

  10. parseInt和map方法使用案例分析

    ["1","2","3"].map(parseInt) //[1,NaN,NaN] 先看map()方法 定义和用法 map() 方法返回一个 ...