【设计模式】不同设计模式体现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 ...
随机推荐
- Converting Recursive Traversal to Iterator
In this article, I'm going to introduce a general pattern named Lazy Iterator for converting recursi ...
- SpringMVC 使用 MultipartFile 实现文件上传
该代码实现了文件上传和文本字段同时传递到后台进行处理的功能. 直接贴代码,中间涉及到的实体类就不贴了,和功能没啥关系的. Controller /** * 添加活动 * * @param req * ...
- HTTP 协议服务器相关概念
1.HTTP/1.1规范允许一台HTTP服务器搭建多个Web站点,即物理层面只有一台服务器,使用虚拟主机功能,可就假想有多台服务器. 在相同IP地址下,由于虚拟主机可以寄存多个不同主机名和域名的网站, ...
- rabbitmq系统学习(二)
Rabbitmq高级整合应用 RabbitMq整合Spring AMQP实战 RabbitAdmin 使用RabbitTemplate的execute方法执行对应操作 rabbitAdmin.decl ...
- Oracle to_char(参数,'FM990.00')函数
遇到一个SQL,记录一下 select to_char(参数,'FM990.00') from 表格 刚看到FM990.00确实不知道什么意思,通过网上资料,知道了 0表示:如果参数(double或者 ...
- numpy中的norm用法
np.linalg.norm() computes the norm of a NumPy array according to an order, ord, which specifies the ...
- Ubuntu下录制和制作Gif图片--实战版
1.背景 ubuntu下,写文章的时候,经常用到Gif图片,这个自己怎么制作呢? 网上查了一下资料,大致的流程就是:安装 录屏软件(kazam) 和 视频 转 Jpeg 的工具(mplayer) ,使 ...
- VMware Workstation Pro网络配置(WiFi配置等)
常用技巧 连续按两下ctrl+alt,实现鼠标脱离 VMware Workstation Pro网络配置有几种模式: 桥接模式: 网络上的独立主机 占用路由器新IP资源 通过VMware Networ ...
- Python编程Day3—基本运算符、数据类型
一.基本运算符 1.算数运算 print(10/3) print(10//3) print(10%3) print(10**3) 2.比较运算 print(10==10) print(10!=10) ...
- 《CLR Via C#》读书笔记:24.运行时序列化
一.什么是运行时序列化 序列化的作用就是将对象图(特定时间点的对象连接图)转换为字节流,这样这些对象图就可以在文件系统/网络进行传输. 二.序列化/反序列化快速入门 一般来说我们通过 FCL 提供的 ...