传统的MVC

既然解释了依赖注入与控制反转,是java常用的高级设计思想
现在看看在这个思想出来之前,大家是怎么组织代码的,又遇到了什么问题

image.png

业界普遍按这种分层方式组织代码,其核心思想是职责分离。层次越低复用程度越高,比如一个 DAO 对象往往会被多个 Service 对象使用,一个 Service 对象往往也会被多个 Controller 对象使用

controller对依赖的service类是如何管理的?
controllerA需要调用serviceA、serviceB类的xx方法,就是在controller里实例化各个service

上层调用下一层时,必然会持有下一层的对象引用,即成员变量。
弊端是什么?
1、只是代码复用了,但是资源没有复用。
每一个链路都创建了同样的对象,造成了极大的资源浪费
image.png

本应多个 Controller 复用同一个 Service,多个 Service 复用同一个 DAO。现在变成了一个 Controller创建多个重复的 Service,多个 Service 又创建了多个重复的 DAO,从倒三角变成了正三角。

2、变化的代价太大
2.1 修改依赖的类
假设有 10 个 Controller 依赖了 UserService,最开始实例化的是 UserServiceImpl,后面需要换一个实现类 OtherUserServiceImpl,我就得逐个修改那 10 个 Controller,非常麻烦
2.2 创建类和配置类麻烦
配置可能会随着业务需求的变化经常更改,这时候你就需要修改每一个依赖该组件的地方,牵一发而动全身

  • 创建了许多重复对象,造成大量资源浪费;
  • 更换实现类需要改动多个地方;
  • 创建和配置组件工作繁杂,给组件调用方带来极大不便。

透过现象看本质,这些问题的出现都是同一个原因:组件的调用方参与了组件的创建和配置工作

before

一般将视图控制、业务逻辑和数据库操作分别抽离出来单独形成一个类,这样各个职责就非常清晰且易于复用和维护

  1. controller: UserServlet 依赖 UserServiceImpl
  2. @WebServlet("/user")
  3. public class UserServlet extends HttpServlet {
  4. // 用于执行业务逻辑的对象 【1】
  5. private UserService userService = new UserServiceImpl();
  6. @Override
  7. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  8. // ...省略其他代码
  9. // 执行业务逻辑
  10. userService.doService();
  11. // ...返回页面视图
  12. }
  13. }
  14. // 【2】UserServiceImpl类依赖UserDaoImpl类
  15. public class UserServiceImpl implements UserService{
  16. // 用于操作数据库的对象
  17. private UserDao userDao = new UserDaoImpl();
  18. @Override
  19. public void doService() {
  20. // ...省略业务逻辑代码
  21. // 执行数据库操作
  22. userDao.doUpdate();
  23. // ...省略业务逻辑代码
  24. }
  25. }
  26. public class UserDaoImpl implements UserDao{
  27. @Override
  28. public void doUpdate() {
  29. // ...省略JDBC代码
  30. }
  31. }

demo2

image.png

  1. class NetworkController {
  2. constructor(options: INetworkControllerOptions) {
  3. this.init();
  4. }
  5. init() {
  6. this.versionManager = new VersionManager(); // 版本管理
  7. this.connectLayer = new ConnectLayer(); // 连接层
  8. this.netWorkManager = new NetWorkManager(); // 网络状态管理
  9. this.taskListManager = new TaskListManager(this.versionManager); // 任务队列管理
  10. this.dataListManager = new DataListManager(); // 待提交数据队列
  11. this.sendDataController = new SendDataController(
  12. this.taskListManager,
  13. this.dataListManager
  14. ); // 发送数据控制器
  15. this.receiveDataController = new ReceiveDataController(
  16. this.taskListManager,
  17. this.dataListManager,
  18. this.netWorkManager
  19. ); // 接受数据控制器
  20. }
  21. }

状态变更这些也是通过注册回调的方式进行设计的
父组件NetworkController

  1. interface INetworkControllerOptions {
  2. // 其他参数
  3. onNetworkChange: (newStatus: NetWorkStatus) => void,
  4. onDataCommitSuccess: (data: LocalData) => void
  5. onDataCommitError: (data: LocalData) => void
  6. onNewData: (data: ServerData) => void
  7. }
  8. class NetworkController {
  9. constructor(options: INetworkControllerOptions) {
  10. // 需要将各个接口实现保存下来
  11. }
  12. }

下面需要实现:
子组件通知父组件,并通知父组件:比如发生了 onDataCommitSuccess
那么需要将这些接口实现保存下来,并传入到各个对象内部分别在恰当的时机进行调用

  1. interface ICallbackDependency {
  2. onDataCommitSuccess?: (data: LocalData) => void
  3. onDataCommitError?: (data: LocalData) => void
  4. }
  5. interface ITaskListManagerDependency {
  6. addTask: (task: BaseTask) => void;
  7. }
  8. interface IDataListManagerDependency {
  9. pushData: (data: LocalData) => void;
  10. shiftData: () => LocalData;
  11. }
  12. class SendDataController {
  13. constructor(
  14. taskListManagerDependency: ITaskListManagerDependency,
  15. dataListManagerDependency: IDataListManagerDependency,
  16. # 在初始化的时候需要通过注入的方式传进来
  17. callbackDependency: ICallbackDependency,
  18. ) {}
  19. handleDataCommitSuccess(data: LocalData) {
  20. try {
  21. // 该函数还可能为空
  22. # 子组件触发父组件的回调
  23. this.callbackDependency.onDataCommitSuccess?.(data);
  24. } catch (error) {
  25. // 使用的时候还需要注意异常问题
  26. }
  27. }
  28. }

业务组件怎么使用?
也要初始化接口

  1. const netWorkLayer = new NetworkController({
  2. // 其他参数
  3. otherOptions: {},
  4. onNetworkChange: () {
  5. // 网络状态变更处理
  6. },
  7. onDataCommitSuccess: () {
  8. // 提交数据成功处理
  9. },
  10. onDataCommitError: () {
  11. // 提交数据失败处理
  12. },
  13. onNewData: () {
  14. // 服务端新数据处理
  15. },
  16. })

所以问题两个:
1、controller层:需要在constructor的时候实例化全部对象,进行统一管控
适合自上而下的
没有一个 适配层,比如上层改个配置下面很容易就切换了

2、基于总管理器的问题,所以组件之间通信方式:底层一步步往上传
具体实现:外部开放个回调接口,子组件去触发这个异步接口,并把函数传到外面

after

依赖注入原理,重要的设计思想是: 面向接口编程
(比面向测试更细粒度,比面向对象编程也更松散化

控制反转,是指对象的创建和配置的控制权从调用方转移给容器。
有了 IoC 容器,我们可以将对象交由容器管理,交由容器管理后的对象称之为 Bean。调用方不再负责组件的创建,要使用组件时直接获取 Bean 即可:

使用依赖倒置进行依赖解耦
依赖倒置原则有两个,其中包括了:

  1. 高层次的模块不应该依赖于低层次的模块,两者都应该依赖于抽象接口
  2. 抽象接口不应该依赖于具体实现,而具体实现则应该依赖于抽象接口

以SendDataController为例:
依赖TaskListManager其实主要是依赖的添加任务的接口addTask()
依赖DataListManager则是依赖添加数据pushData()、取出数据shiftData()

转换为代码:

  1. interface ITaskListManagerDependency {
  2. addTask: (task: BaseTask) => void;
  3. }
  4. interface IDataListManagerDependency {
  5. pushData: (data: LocalData) => void;
  6. shiftData: () => LocalData;
  7. }
  8. class SendDataController {
  9. constructor(
  10. taskListManagerDependency: ITaskListManagerDependency,
  11. dataListManagerDependency: IDataListManagerDependency
  12. ) {
  13. // 相关依赖可以保存起来,在需要的时候使用
  14. }
  15. }

如果项目中有完善的依赖注入框架,则可以使用项目中的依赖注入体系。
实际上,我们可以给每个对象提供自身的接口描述
这样其他对象中可以直接import同一份接口也是可以的,管理和调整会比较方便。

所以before的demo2改造为:
如果项目中有完善的依赖注入框架,则可以使用项目中的依赖注入体系。
在我们这个例子里,总控制器充当了依赖注入的控制角色
而具体其中的各个对象之间,实现了基于抽象接口的依赖,成功了进行了解耦。
依赖注入在大型项目中比较常见,对于各个模块间的依赖关系管理很实用。

除了初始化相关,总控制器的职责还包括对业务层提供接口和事件监听
其中接口中会依赖具体职责对象的协作

使用事件驱动进行依赖解耦

子组件,向上传递_onDataCommitSuccess事件

  1. class SendDataController {
  2. private readonly _onDataCommitSuccess = new Emitter<LocalData>();
  3. readonly onDataCommitSuccess: Event<LocalData> = this._onDataCommitSuccess.event;
  4. constructor(
  5. taskListManagerDependency: ITaskListManagerDependency,
  6. dataListManagerDependency: IDataListManagerDependency,
  7. // 在初始化的时候需要通过注入的方式传进来
  8. callbackDependency: ICallbackDependency,
  9. ) {}
  10. handleDataCommitSuccess(data: LocalData) {
  11. this._onDataCommitSuccess.fire(data);
  12. }
  13. }
  1. class NetworkController {
  2. // 提供的事件
  3. private readonly _onNetworkChange = new Emitter<NetWorkStatus>();
  4. readonly onNetworkChange: Event<NetWorkStatus> = this._onNetworkChange.event;
  5. private readonly _onDataCommitSuccess = new Emitter<LocalData>();
  6. readonly onDataCommitSuccess: Event<LocalData> = this._onDataCommitSuccess.event;
  7. private readonly _onNewData = new Emitter<ServerData>();
  8. readonly onNewData: Event<ServerData> = this._onNewData.event;
  9. constructor(options: INetworkControllerOptions) {
  10. this.init();
  11. this.initEvent();
  12. }
  13. initEvent() {
  14. // 监听 SendDataController 的事件,并触发自己的事件
  15. # 父组件是怎么拿到 this.sendDataController 这个实例的?
  16. // 也是init里?
  17. this.sendDataController.onDataCommitSuccess(data => {
  18. this._onDataCommitSuccess.fire(data);
  19. });
  20. }
  21. }