手動實現「低配版」IOC

引言

IOC,全稱Inversion of Control,控制反轉。也算是老生常談了。java

老生常談:原指老書生的平凡議論;今指常講的沒有新意的老話。

同時另外一個話題,依賴注入,用什麼就聲明什麼,直接就聲明,或者構造函數或者加註解,控制反轉是實現依賴注入的一種方式。程序員

經過依賴注入:咱們無需管理對象的建立,經過控制反轉:咱們能夠一鍵修改注入的對象。spring

最近在作Android實驗與小程序相關的開發,發現用慣了IOC的咱們再去手動new對象的時候總感受內心不舒服,之後改起來怎麼辦呢?既然沒有IOC,咱們就本身寫一個吧。小程序

實現

就像我在標題中描述的同樣,我先給你們講解一下標配IOC的原理,使你們更清晰明瞭,可是受Android與小程序相關的限制,我具體的實現,是低配版IOC服務器

我的扯淡

不論是Spring仍是Angular,要麼是開源你們,要麼是商業巨頭,具體的框架實現都是至關優秀。我還沒水平也沒精力去研讀源碼,只但願分享本身對IOC的理解,幫到更多的人。網絡

畢竟如今小學生都開始寫Python,之後的框架設計會愈來愈優秀,學習成本愈來愈低,開發效率愈來愈高。多是我的報個培訓班學個倆月也會設計微服務,也能成爲全棧工程師。因此咱們應該想的是如何設計框架,而不是僅停留在使用的層面,漸漸地被只會寫增刪改查每天搬磚的人取代。框架

996加班的工程師都開始擠時間寫框架擴大影響力,讓社會聽到程序員的吶喊,咱們還有什麼不努力的理由?ide

clipboard.png

「標配」IOC

不論是Spring仍是Angular,它們的核心是什麼呢?打上mvn spring-boot:run後臺就Started Application in xxx seconds了,它到底幹什麼了呢?函數

clipboard.png

容器

SpringAngular就是一個大的IOC容器,因此應用啓動的過程,其實就是構造容器的過程。spring-boot

clipboard.png

容器,確定是裝東西的啊?IOC容器裏裝的是什麼?

裝的是對象。控制器Controller,服務Service,自定義的組件Component,全部被Spring管理的對象都將被放進IOC容器裏。

clipboard.png

因此,你們應該能明白,爲何IOC是依賴注入的一種實現方式?

由於這個對象不是你本身new的,是從容器中拿的,容器初始化的時候,就已經把這個對象構造好了,該注的都注進來了。

思考

從上面你們能夠看到,依賴注入的前提是什麼?是要求這個對象必須是從容器中拿的,因此才能依賴注入成功。

Spring Boot中沒問題,Tomcat轉發的路由直接交給容器中相應的對象去處理,同理,Angular也同樣。

Android呢?

clipboard.png

手機調用的並非咱們構造的Activity,而是它本身實例化的,小程序也與之相似,Page的實例化不歸咱們管。

因此「標配」IOC在這裏不適用,因此我設計了「低配」IOC容器。

「低配」IOC

找點本身能管得了的對象放在IOC容器裏,這樣再須要對象就不用去new了,Service有變動直接修改注入就好了。

受咱們管理的只有Service,計劃設計一個管理全部ServiceIOC容器,而後ActivityPage裏用的時候,直接從容器中拿。(低配在這裏,不能依賴注入了,得本身拿)。

clipboard.png

Android

一個單例的Configuration負責註冊Bean和獲取Bean,存儲着一個context上下文。看着挺高級的,其實就是一個HashMap存儲着接口類型容器對象的映射。

/**
 * 全局配置類
 */
public class Configuration {

    private static Map<Class<?>, Object> context = new HashMap<>();

    private static final class Holder {
        private static final Configuration INSTANCE = new Configuration();
    }

    public static Configuration getInstance() {
        return Holder.INSTANCE;
    }

    public Configuration registerBean(Class<?> clazz, Object bean) {
        context.put(clazz, bean);
        return this;
    }

    public <T> T getBean(Class<?> clazz) {
        return (T) context.get(clazz);
    }
}

寫一個靜態方法,更加方便配置。

/**
 * 雲智,全局配置輔助類
 */
public class Yunzhi {

    ...

    public static <T> T getBean(Class<?> clazz) {
        return Configuration.getInstance().getBean(clazz);
    }
}

一個Application負責容器中全部對象的建立。

public class App extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        Yunzhi.init()
                .setApi("http://192.168.2.110:8888")
                .setTimeout(1L)
                .registerBean(AuthService.class, new AuthServiceImpl())
                .registerBean(LetterService.class, new LetterServiceImpl());
    }
}

使用方法和原來就同樣了,一個接口,一個實現類。這裏用到了RxJava,看上去卻是相似咱們的Angular了。

public interface AuthService {

    Observable<Auth> login(String username, String password);
}
public class AuthServiceImpl implements AuthService {

    private static final String TAG = "AuthServiceImpl";

    @Override
    public Observable<Auth> login(String username, String password) {
        Log.d(TAG, "BASIC 認證");
        String credentials = username + ":" + password;
        String basicAuth = "Basic " + Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);

        Log.d(TAG, "請求登陸");
        return HttpClient.request(AuthRequest.class)
                .login(basicAuth)
                .subscribeOn(Schedulers.io())                   // 在IO線程發起網絡請求
                .observeOn(AndroidSchedulers.mainThread());     // 在主線程處理
    }
}

由於Activity咱們管不着,因此在Activity裏用不了依賴注入,須要手動從容器裏拿。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    this.authService = Yunzhi.getBean(AuthService.class);
}

這裏有一處沒考慮到的問題,就是手機端的性能問題,手機和服務器的處理能力確定是比不了的,服務器在初始化的時候把全部對象都建立處理無可厚非,可是感受手機端這樣作仍是會對性能產生必定影響的。

應該是某些對象用到的時候再建立,實驗要求時間很緊,這個就先這樣吧,不改了。

小程序端

小程序是在Android以後寫的,想到了以前設計的部分缺陷,對容器使用了另外一種思想進行實現。

export class YunzhiService {

    private static context = new Map<string, object>();

    public static getBean(beanName: string): object {
        // 從context裏拿對象
        let bean = this.context.get(beanName);
        // 若是沒有,構造一個,並放進context
        if (!bean) {
            bean = this.createBeanByName(beanName);
            this.context.set(beanName, bean);
        }
        // 返回
        return bean;
    }

    public static createBeanByName(beanName: string): object {
        // 根據不一樣名稱構造不一樣的Bean
        switch (beanName) {
            case Bean.AUTH_SERVICE:
                return new AuthServiceImpl();
            case Bean.SCORE_SERVICE:
                return new ScoreServiceImpl();
            case Bean.SEMESTER_SERVICE:
                return new SemesterServiceImpl();
            default:
                throw '錯誤,未註冊的bean';
        }
    }
}

總結

Spring Boot真厲害,何時咱們也能寫出如此優秀的框架?

相關文章
相關標籤/搜索