使用过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. Python之旅Day5 列表生成式 生成器 迭代器 装饰器

    装饰器 器即函数,装饰即修饰,意指为其他函数添加新功能 装饰器定义:本质就是函数,功能是为其他函数添加新功能 装饰器涉及的知识点= 高阶函数+函数嵌套+闭包 在遵循下面两个原则的前提下为被装饰者新功能 ...

  2. C语言向上、向下取整

    C语言有以下几种取整方法: 1.直接赋值给整数变量.如: int i = 2.5; 或 i = (int) 2.5; 这种方法采用的是舍去小数部分 2.C/C++中的整数除法运算符“/”本身就有取整功 ...

  3. QEMU Networking

    QEMU Networking QEMU has a number of really nice ways to set up networking for its guests. It can be ...

  4. Android MonkeyRunner

    MonkeyRunner 也是一款安卓sdk自有的测试工具 可以用来做功能测试 阅读目录 MonkeyRunner环境搭建 1. 安装JDK的安装与配置 2. 安装Python编译器     http ...

  5. Java中最常用的集合类框架之 HashMap

    一.HashMap的概述 HashMap可以说是Java中最常用的集合类框架之一,是Java语言中非常典型的数据结构.      HashMap是基于哈希表的Map接口实现的,此实现提供所有可选的映射 ...

  6. FFmpeg 学习(一):FFmpeg 简介

    一.FFmpeg 介绍 FFmpeg是一套可以用来记录.转换数字音频.视频,并能将其转化为流的开源计算机程序.采用LGPL或GPL许可证.它提供了录制.转换以及流化音视频的完整解决方案.它包含了非常先 ...

  7. Win10下python不同版本同时安装并解决pip共存问题

    特别说明,本文是在Windows64位系统下进行的,32位系统请下载相应版本的安装包,安装方法类似. 使用python开发,环境有Python2和 python3 两种,有时候需要两种环境切换使用,下 ...

  8. Kali学习笔记39:SQL手工注入(1)

    终于到了SQL注入 最大的.最经典的.最常见的Web漏洞就是SQL注入漏洞 SQL注入的原理这里就不说了,百度 打开DVWA,SQL注入测试模块 测试单引号,发现出错,于是想到测试语句: 1' or ...

  9. 将double或则float类型保留小数

    DecimalFormat df=new DecimalFormat("0.0");//“0.00” df.format(price);

  10. VueJs(6)---V-on指令

    V-on指令 一.概述 v-on是用来绑定事件监听器,用在普通元素上时,只能监听原生 DOM 事件.用在自定义元素组件上时,也可以监听子组件触发的自定义事件. 在监听原生 DOM 事件时,方法以事件为 ...