• 依赖注入浅析


    一、名词解析。

    维基百科上的解释是依赖注入(Dependency Injection,简称DI)是实现控制反转(Inversion of Control,缩写为IoC)的一种用以降低代码耦合度的一种设计模式。

    二、进入主题。

    请先看下面的示例有什么问题没有

    @interface Service : NSObject
    - (void)doSomeThing;
    @end
    
    @implementation Service
    - (void)doSomeThing {
        // TODO:
    }
    @end
    
    @interface Client : NSObject {
        Service *_service;
    }
    - (void)doTheWork;
    @end
    
    @implementation Client
    
    - (instancetype)init {
        self = [super init];
        if (self) {
            _service = [Service new];
        }
        return self;
    }
    
    - (void)doTheWork {
        NSLog(@"I'm do the work, and ask service do some subwork");
        [_service doSomeThing];
    }
    
    @end
    

    咋一看,这样的代码看起来好像也没有什么问题:Client有一个Service成员,Client直接New一个Service成员,然后在工作时将一部分转包给Service去做。但是,在这种情况下,其实Client和Service就有一种hard-coded的依赖。

    依赖注入,就是让Client不去显示的通过构造函数(objc's all & init or new)初始化成员变量的技术。

    依赖注入有三种方式:

    1. 构造函数注入:通过构造函数去提供所依赖的对象 (objc的init)

    @implementation Client
    
    - (instancetype)initWithService:(Service *)sevice {
        self = [super init];
        if (self) {
            _service = sevice;
        }
        return self;
    }
    
    @end
    

    2. setter注入

    @implementation Client
    
    - (void)setService:(Service *)service {
        _service = service;
    }
    
    @end
    

      

    3. 接口注入

    @protocol ServiceProtocol <NSObject>
    
    - (void)doSomeThing;
    
    @end
    @interface Service : NSObject <ServiceProtocol>
    @end
    
    @implementation Service
    - (void)doSomeThing {
        // TODO:
    }
    @end
    
    @interface Client : NSObject {
        id<ServiceProtocol> _service;
    }
    - (void)doTheWork;
    @end
    
    @implementation Client
    
    - (void)setService:(id<ServiceProtocol>)service {
        _service = service;
    }
    
    @end
    

      

    我们可以看到,其实构造函数注入和setter注入也还是都依赖于具体的service类开。所以第三种接口注入其实才是比较好的方式。当然还应该加上一些对边界条件的检测。把三种注入方法接合起来。

    @implementation Client
    
    - (void)setService:(id<ServiceProtocol>)service {
        _service = service;
    }
    
    - (instancetype)initWithService:(id<ServiceProtocol>)sevice {
        self = [super init];
        if (self) {
            _service = sevice;
        }
        return self;
    }
    
    - (BOOL)validateState {
        if (!_service) {
            NSLog(@"Service cannot be nil");
            // TODO: maybe throw a exception
            return NO;
        }
        return YES;
    }
    
    - (BOOL)doTheWork {
        BOOL done = NO;
        if ([self validateState]) {
            NSLog(@"I'm do the work, and ask service do some subwork");
            [_service doSomeThing];
            done = YES;
        }
        return done;
    }
    
    @end
    

      

    有的人已经看不下去了:不就经常TM说的面向接口编程吗?yes, you are right.

    还有人要说了,我们用第三方的库,用系统的库,有太多地方都是直接依赖的了,而且如果所有地方都要这样不直接依赖,那还不搞死人?

    是的,面向对象也好,面向接口也好,都是有一个度的。做人做事何尝又不是呢。面向对象与面向过程不是互斥的。无论你怎么抽象、怎么面积对象,到具体的方法逻辑里面那一定是面向过程的。

    一样的,通过setter注入也好,接口注入也好,最后真正决定要使用哪一个实现了service protocol的类型的时候,总得有一个地方去初始化吧?那么好了,在那个地方一定就是和这个具体的Service类型强依赖的。我们要做的只是把一些今后可能会变的地方,一些粒度大一些的模块这样实现便好。

    关于依赖注入的库:Typhoon(objc, swift) Swinject 

    Reference: https://en.wikipedia.org/wiki/Dependency_injection

  • 相关阅读:
    一个简单的开源PHP爬虫框架『Phpfetcher』
    OpenNI1.5获取华硕XtionProLive深度图和彩色图并用OpenCV显示
    【数字图像处理之(三)】用图像增强谈灰度变换
    Android 实现登录界面和功能实例
    MFC通过button控制编辑框是否显示系统时间
    springmvc+mybatis整合
    iOS获取当前城市
    Android之Wifi学习(1)
    实测:Windows 8.1 (Windows Blue) 第三方桌面应用无法支持Retina屏,效果与Windows8.0似无差别。
    java 读取不同的配置文件
  • 原文地址:https://www.cnblogs.com/csutanyu/p/4749974.html
Copyright © 2020-2023  润新知