在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ iOS/ 依賴注入和注解,為什么 Java 比你想象的要好
與四軸無(wú)人機(jī)的通訊
在沙盒中編寫腳本
結(jié)構(gòu)體和值類型
深入理解 CocoaPods
UICollectionView + UIKit 力學(xué)
NSString 與 Unicode
代碼簽名探析
測(cè)試
架構(gòu)
第二期-并發(fā)編程
Metal
自定義控件
iOS 中的行為
行為驅(qū)動(dòng)開(kāi)發(fā)
Collection View 動(dòng)畫
截圖測(cè)試
MVVM 介紹
使 Mac 應(yīng)用數(shù)據(jù)腳本化
一個(gè)完整的 Core Data 應(yīng)用
插件
字符串
為 iOS 建立 Travis CI
先進(jìn)的自動(dòng)布局工具箱
動(dòng)畫
為 iOS 7 重新設(shè)計(jì) App
XPC
從 NSURLConnection 到 NSURLSession
Core Data 網(wǎng)絡(luò)應(yīng)用實(shí)例
GPU 加速下的圖像處理
自定義 Core Data 遷移
子類
與調(diào)試器共舞 - LLDB 的華爾茲
圖片格式
并發(fā)編程:API 及挑戰(zhàn)
IP,TCP 和 HTTP
動(dòng)畫解釋
響應(yīng)式 Android 應(yīng)用
初識(shí) TextKit
客戶端
View-Layer 協(xié)作
回到 Mac
Android
Core Image 介紹
自定義 Formatters
Scene Kit
調(diào)試
項(xiàng)目介紹
Swift 的強(qiáng)大之處
測(cè)試并發(fā)程序
Android 通知中心
調(diào)試:案例學(xué)習(xí)
從 UIKit 到 AppKit
iOS 7 : 隱藏技巧和變通之道
安全
底層并發(fā) API
消息傳遞機(jī)制
更輕量的 View Controllers
用 SQLite 和 FMDB 替代 Core Data
字符串解析
終身學(xué)習(xí)的一代人
視頻
Playground 快速原型制作
Omni 內(nèi)部
同步數(shù)據(jù)
設(shè)計(jì)優(yōu)雅的移動(dòng)游戲
繪制像素到屏幕上
相機(jī)與照片
音頻 API 一覽
交互式動(dòng)畫
常見(jiàn)的后臺(tái)實(shí)踐
糟糕的測(cè)試
避免濫用單例
數(shù)據(jù)模型和模型對(duì)象
Core Data
字符串本地化
View Controller 轉(zhuǎn)場(chǎng)
照片框架
響應(yīng)式視圖
Square Register 中的擴(kuò)張
DTrace
基礎(chǔ)集合類
視頻工具箱和硬件加速
字符串渲染
讓東西變得不那么糟
游戲中的多點(diǎn)互聯(lián)
iCloud 和 Core Data
Views
虛擬音域 - 聲音設(shè)計(jì)的藝術(shù)
導(dǎo)航應(yīng)用
線程安全類的設(shè)計(jì)
置換測(cè)試: Mock, Stub 和其他
Build 工具
KVC 和 KVO
Core Image 和視頻
Android Intents
在 iOS 上捕獲視頻
四軸無(wú)人機(jī)項(xiàng)目
Mach-O 可執(zhí)行文件
UI 測(cè)試
值對(duì)象
活動(dòng)追蹤
依賴注入
Swift
項(xiàng)目管理
整潔的 Table View 代碼
Swift 方法的多面性
為什么今天安全仍然重要
Core Data 概述
Foundation
Swift 的函數(shù)式 API
iOS 7 的多任務(wù)
自定義 Collection View 布局
測(cè)試 View Controllers
訪談
收據(jù)驗(yàn)證
數(shù)據(jù)同步
自定義 ViewController 容器轉(zhuǎn)場(chǎng)
游戲
調(diào)試核對(duì)清單
View Controller 容器
學(xué)無(wú)止境
XCTest 測(cè)試實(shí)戰(zhàn)
iOS 7
Layer 中自定義屬性的動(dòng)畫
第一期-更輕量的 View Controllers
精通 iCloud 文檔存儲(chǔ)
代碼審查的藝術(shù):Dropbox 的故事
GPU 加速下的圖像視覺(jué)
Artsy
照片擴(kuò)展
理解 Scroll Views
使用 VIPER 構(gòu)建 iOS 應(yīng)用
Android 中的 SQLite 數(shù)據(jù)庫(kù)支持
Fetch 請(qǐng)求
導(dǎo)入大數(shù)據(jù)集
iOS 開(kāi)發(fā)者的 Android 第一課
iOS 上的相機(jī)捕捉
語(yǔ)言標(biāo)簽
同步案例學(xué)習(xí)
依賴注入和注解,為什么 Java 比你想象的要好
編譯器
基于 OpenCV 的人臉識(shí)別
玩轉(zhuǎn)字符串
相機(jī)工作原理
Build 過(guò)程

依賴注入和注解,為什么 Java 比你想象的要好

我坦白\: 我喜歡 Java。

我真的喜歡!

也許這并不會(huì)讓你感到吃驚,因?yàn)槲耶吘勾_實(shí)參與編著過(guò)一本滿是 Java 代碼的書。但是事實(shí)上,當(dāng)我開(kāi)始編寫 Android 應(yīng)用的時(shí)候我并不是一個(gè)喜歡 Java 的人,而當(dāng)我開(kāi)始編寫書蟲編程指南的時(shí)候,我也很難稱得上是粉絲,甚至當(dāng)我們完成編寫的時(shí)候,我也始終不能算是一名超級(jí)粉絲。這個(gè)事實(shí)其實(shí)讓我自己都很吃驚!

我原本并非想抱怨什么,也并非要深刻反思一番。但是下面列出的這些內(nèi)容卻是一直困擾我的問(wèn)題:

  • Java 很冗長(zhǎng)。沒(méi)有任何簡(jiǎn)短的類似 Blocks 或者 Lambda 表達(dá)式的語(yǔ)法來(lái)執(zhí)行回調(diào)(當(dāng)然,Java8已經(jīng)開(kāi)始支持這一特性),所以你必須編寫非常多的模板代碼來(lái)實(shí)現(xiàn),有時(shí)甚至只是一個(gè)簡(jiǎn)單的接口。如果你需要一個(gè)對(duì)象來(lái)保存四個(gè)屬性,你必須創(chuàng)建一個(gè)擁有四個(gè)命名字段的類。

  • Java 很死板。要編寫清楚的Java程序, 你通常要正確的指定需要捕獲的異常類型,以及要接受的參數(shù)類型,還有仔細(xì)檢查并確保你的引用非空,甚至還要導(dǎo)入你所使用的每一個(gè)類。另外在運(yùn)行時(shí)雖然有一定的靈活性,但是和 Objective-C 的 runtime 沒(méi)有任何相似的地方,更不用說(shuō)和 Ruby 或者 Python 相比了。

這是我眼中的Java,它的代碼就像這樣:

public class NumberStack {
    List<Integer> mNumbers = new ArrayList<Integer>();

    public void pushNumber(int number) {
        mNumbers.add(number);
    }

    public Integer popNumber() {
        if (mNumber.size() == 0) {
            return null;
        } else {
            return mNumber.remove(mNumber.size() - 1);
        }
    }
}

我學(xué)習(xí)過(guò)并且會(huì)在工作中混合使用一些內(nèi)部類和接口。雖然編寫Java程序這并不是世界上最糟糕的事情,但是我還是希望Java能夠擁有其他語(yǔ)言的特點(diǎn)和靈活性。類似 “天啊,我多么希望這能更像 Java” 的感嘆從沒(méi)有出現(xiàn)過(guò)。

但是,我的想法改變了。

Java 獨(dú)有的特性

說(shuō)來(lái)也奇怪,改變我想法的恰恰是 Java 獨(dú)有的特性。請(qǐng)思考下面的代碼:

public class Payroll {
    ...

    public long getWithholding(long payInDollars) {
        ...
        return withholding;
   }

    public long getAfterTaxPay(Employee employee) {
        long basePay = EmployeeDatabase.getInstance()
           .getBasePay(employee);
        long withholding = getWithholding(basePay);

        return basePay - withholding;
    }
}

這個(gè)類在 getAfterTaxPay() 方法中需要依賴一個(gè) EmployeeDatabase 對(duì)象。有很多種方式可以創(chuàng)建該對(duì)象,但在這個(gè)例子中, 我使用了單例模式,調(diào)用一個(gè)靜態(tài)的 getInstance 方法。

Java 中的依賴關(guān)系是非常嚴(yán)格的。所以任何時(shí)間我都像這樣編寫代碼:

        long basePay = EmployeeDatabase.getInstance()
           .getBasePay(employee);

EmployeeDatabase 類中我創(chuàng)建了一個(gè)嚴(yán)格依賴。不僅如此,我是利用EmployeeDatabase類的特定方法 getInstance() 創(chuàng)建的嚴(yán)格依賴。而在其他語(yǔ)言里,我也許可以使用 swizzle 或者 monkey patch 的方式來(lái)處理這樣的事情.當(dāng)然并不是說(shuō)這樣的方法有什么好處,但它至少存在實(shí)現(xiàn)的可能。但是在 Java 里是不可能的。

而創(chuàng)建依賴的其他方式比這更加嚴(yán)格。就讓我們來(lái)看看下面這行:

        long basePay = new EmployeeDatabase()
           .getBasePay(employee);

當(dāng)使用關(guān)鍵字 new 時(shí),我會(huì)采用與調(diào)用靜態(tài)方法相同的方式,但有一點(diǎn)不同:調(diào)用 new EmployeeDatabase() 方法一定會(huì)返回給我們一個(gè) EmployeeDatabase 類的實(shí)例。無(wú)論你如何努力,你都沒(méi)有辦法重寫這個(gè)構(gòu)造函數(shù)來(lái)讓它返回一個(gè) mock 的子類對(duì)象。

依賴注入

我們解決此類問(wèn)題通常采用依賴注入技術(shù)。它并非 Java 獨(dú)有的特性,但對(duì)于上述提到的問(wèn)題,Java 尤其需要這個(gè)特性。

依賴注入簡(jiǎn)單的說(shuō),就是接受合作對(duì)象作為構(gòu)造方法的參數(shù)而不是直接獲取它們自身。所以 Payroll 類的實(shí)現(xiàn)會(huì)相應(yīng)地變成這樣:

public class Payroll {
    ...

    EmployeeDatabase mEmployeeDatabase;

    public Payroll(EmployeeDatabase employeeDatabase) {
        mEmployeeDatabase = employeeDatabase;
    }

    public long getWithholding(long payInDollars) {
        ...
        return withholding;
   }

    public long getAfterTaxPay(Employee employee) {
        long basePay = mEmployeeDatabase.getBasePay(employee);
        long withholding = getWithholding(basePay);

        return basePay - withholding;
    }
}

EmployeeDatabase 是一個(gè)單例?一個(gè)模擬出來(lái)的子類?還是一個(gè)上下文相關(guān)的實(shí)現(xiàn)? Payroll 類不再需要知道這些。

用聲明依賴進(jìn)行編程

上述這些僅僅介紹了我真正要講的內(nèi)容——依賴注入器。

(旁白:我知道在真正開(kāi)始討論前將這兩個(gè)問(wèn)題講的比較深入是很奇怪的,但是我希望你們能夠容忍我這么做。正確的理解 Java 比起其他語(yǔ)言要花費(fèi)更多地時(shí)間。困難的事物往往都是這樣。)

現(xiàn)在我們通過(guò)構(gòu)造函數(shù)傳遞依賴,會(huì)導(dǎo)致我們的對(duì)象更加難以使用,同時(shí)也很難作出更改。在我使用依賴注入之前,我會(huì)像這樣使用 Payroll 類:

    new Payroll().getAfterTaxPay(employee);

但是,現(xiàn)在我必須這樣寫:

    new Payroll(EmployeeDatabase.getInstance())
        .getAfterTaxPay(employee);

還有,任何時(shí)候如何我改變了 Payroll 的依賴, 我都不得不修改使用了 new Payroll 的每一個(gè)地方。

而依賴注入器允許我不再編寫用來(lái)明確提供依賴的代碼。相反,我可以直接聲明我的依賴對(duì)象,讓工具來(lái)自動(dòng)處理相應(yīng)操作。有很多依賴注入的工具,下面我將用 RoboGuice 來(lái)舉個(gè)例子。

為了這樣做,我使用“注解“這一 Java 工具來(lái)描述代碼。我們通過(guò)為構(gòu)造函數(shù)添加簡(jiǎn)單的注解聲明:

    @Inject
    public Payroll(EmployeeDatabase employeeDatabase) {
        mEmployeeDatabase = employeeDatabase;
    }

注解 @Inject 的含義是“創(chuàng)建一個(gè) Payroll 類的實(shí)例,執(zhí)行它的構(gòu)造方法,傳遞所有的參數(shù)值?!倍螽?dāng)我真的需要一個(gè) Payroll 實(shí)例的時(shí)候,我會(huì)利用依賴注入器來(lái)幫我創(chuàng)建,就像這樣:

    Payroll payroll = RoboGuice.getInjector(getContext())
        .getInstance(Payroll.class);

    long afterTaxPay = payroll.getAfterTaxPay(employee);

一旦我采用這種方式創(chuàng)建實(shí)例,就能使用注入器來(lái)設(shè)置足夠令人滿意的依賴。是否需要 EmployeeDatabase 是一個(gè)單例?是否需要一個(gè)可自定義的子類?所有這些都可以在同一個(gè)地方指定。

聲明式 Java 的廣闊世界

這是一種很容易使用的描述工具,但是很難比較在 Java 中是否使用依賴注入的根本差距。如果沒(méi)有依賴注入器,重構(gòu)和測(cè)試驅(qū)動(dòng)開(kāi)發(fā)會(huì)是一項(xiàng)艱苦的勞動(dòng)。而使用它,這些工作則會(huì)毫不費(fèi)力。對(duì)于一名 Java 開(kāi)發(fā)者來(lái)說(shuō),唯一比依賴注入器更重要的就是一個(gè)優(yōu)秀的 IDE 了。

不過(guò),這只是廣泛可能性中的第一點(diǎn)。 對(duì)于 Google 之外的 Android 開(kāi)發(fā)者來(lái)說(shuō),最令人興奮的就是基于注解的 API 了。

舉個(gè)例子,我們可以使用 ButtreKnife。通常情況下,我們會(huì)花費(fèi)大量的時(shí)間為 Android 的視圖對(duì)象編寫監(jiān)聽(tīng)器,就像這樣:

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_content);

    View okButton = findViewById(R.id.ok_button);
    okButton.setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
            onOkButtonClicked();
        }
    });
}

public void onOkButtonClicked() {
    // 處理按鈕點(diǎn)擊
}

ButterKnife 允許我們只提供很少的代碼來(lái)描述“在 ID 為 R.id.ok_button 的視圖控件被點(diǎn)擊時(shí)調(diào)用 onOkButtonClicked 方法”這件事情,就像這樣:

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_content);

    ButterKnife.inject(this);
}

@OnClick(R.id.ok_button);
public void onOkButtonClicked() {
    // 處理按鈕點(diǎn)擊
}

我能繼續(xù)寫很多這樣的例子。有很多庫(kù)可以通過(guò)注解來(lái)實(shí)現(xiàn)序列化與反序列化 Json,在 savedInstanceState 方法內(nèi)部存儲(chǔ)字段,或者是生成 REST 網(wǎng)絡(luò)服務(wù)的接口代碼等操作。

編譯時(shí)和運(yùn)行時(shí)注解處理對(duì)比

盡管有些使用注解的工具會(huì)產(chǎn)生相似的效果,不過(guò) Java 允許使用不同的方式實(shí)現(xiàn)。下面我用 RoboGuice 和 Dagger 來(lái)舉個(gè)例子。它們都是依賴注入器,也同樣都使用 @Inject 注解。但是 RoboGuice 會(huì)在運(yùn)行時(shí)讀取你的代碼注解,而 Dragger 則是在編譯時(shí)生成對(duì)應(yīng)的代碼。

這樣會(huì)有一些重要的好處。它能在更早的時(shí)間發(fā)現(xiàn)注解中的語(yǔ)義錯(cuò)誤。Dagger 能夠在編譯時(shí)提醒你可能存在的循環(huán)依賴,但是 RoboGuice 不能。

而且這對(duì)提高性能也很有幫助。使用預(yù)先生成的代碼可以減少啟動(dòng)時(shí)間,并在運(yùn)行時(shí)避免讀取注解。因?yàn)樽x取注解需要使用 Java 反射相關(guān)的 API,這在 Android 設(shè)備上是很耗時(shí)的。

運(yùn)行時(shí)進(jìn)行注解處理的例子

我會(huì)通過(guò)展示一個(gè)如何定義和處理運(yùn)行時(shí)注解的簡(jiǎn)單例子,來(lái)結(jié)束今天的內(nèi)容。 假設(shè)你是一個(gè)很沒(méi)有耐心地人,并且厭倦了在你的 Android 程序中打出一個(gè)完整的靜態(tài)限定常量,比如:

public class CrimeActivity {
    public static final String ACTION_VIEW_CRIME = 
        “com.bignerdranch.android.criminalintent.CrimeActivity.ACTION_VIEW_CRIME”;
}

你可以使用一個(gè)運(yùn)行時(shí)注解來(lái)幫你做這些事情。首先,你要?jiǎng)?chuàng)建一個(gè)注解類:

@Retention(RetentionPolicy.RUNTIME)
@Target( { ElementType.FIELD })
public @interface ServiceConstant { }

這段代碼聲明了一個(gè)名為 ServiceConstant 的注解。 而代碼本身被 @Retention@Target 注解。@Retention 表示注解將會(huì)停留的時(shí)間。在這里我們將它設(shè)置為運(yùn)行時(shí)觸發(fā)。如果我們想僅僅在編譯時(shí)處理注解,可以將其設(shè)置為 RetentionPolicy.SOURCE。

另一個(gè)注解 @Target,表示你放置注解的位置。當(dāng)然有很多的數(shù)據(jù)類型可以選擇。因?yàn)槲覀兊淖⒔鈨H需要對(duì)字段有效,所以只需要提供 ElementType.FIELD 的聲明。

一旦定義了注解,我們接著就要寫些代碼來(lái)尋找并自動(dòng)填充帶注解的字段:

public static void populateConstants(Class<?> klass) {
    String packageName = klass.getPackage().getName();
    for (Field field : klass.getDeclaredFields()) {
        if (Modifier.isStatic(field.getModifiers()) && 
                field.isAnnotationPresent(ServiceConstant.class)) {
            String value = packageName + "." + field.getName();
            try {
                field.set(null, value);
                Log.i(TAG, "Setup service constant: " + value + "");
            } catch (IllegalAccessException iae) {
                Log.e(TAG, "Unable to setup constant for field " + 
                        field.getName() +
                        " in class " + klass.getName());
            }
        }
    }
}

最后,我們?yōu)榇a增加注解,然后調(diào)用我們充滿魔力的方法:

public class CrimeActivity {
    @ServiceConstant
    public static final String ACTION_VIEW_CRIME;

    static {
        ServiceUtils.populateConstants(CrimeActivity.class);
}

總結(jié)

這些就是我了解的全部?jī)?nèi)容。有太多與 Java 注解相關(guān)的部分。我不能保證所有這些能夠立刻讓你對(duì) Java 的感受變得和我一樣,但是我希望你能確實(shí)看到很多有趣的東西。雖然通常 Java 在表達(dá)性上還欠缺一些,但是在 Java 的工具包中有一些基本的構(gòu)建模塊,能夠讓高級(jí)開(kāi)發(fā)人員可以構(gòu)建更強(qiáng)大的工具,從而擴(kuò)大整個(gè)社區(qū)的生產(chǎn)力。

如果你對(duì)此很感興趣,并且打算深入了解這些,你會(huì)發(fā)現(xiàn)通過(guò)注解驅(qū)動(dòng)代碼生成的過(guò)程非常有趣。有時(shí)候并不一定要真的閱讀或者寫出漂亮的代碼,但是人們可以利用這些工具創(chuàng)造出漂亮的代碼。假如你對(duì)于實(shí)際場(chǎng)景如何應(yīng)用依賴注入的原理很感興趣的話,ButterKnife 的源碼還是相當(dāng)簡(jiǎn)單的。