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

鍍金池/ 教程/ iOS/ 避免濫用單例
與四軸無(wú)人機(jī)的通訊
在沙盒中編寫腳本
結(jié)構(gòu)體和值類型
深入理解 CocoaPods
UICollectionView + UIKit 力學(xué)
NSString 與 Unicode
代碼簽名探析
測(cè)試
架構(gòu)
第二期-并發(fā)編程
Metal
自定義控件
iOS 中的行為
行為驅(qū)動(dòng)開發(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)畫
常見的后臺(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 開發(fā)者的 Android 第一課
iOS 上的相機(jī)捕捉
語(yǔ)言標(biāo)簽
同步案例學(xué)習(xí)
依賴注入和注解,為什么 Java 比你想象的要好
編譯器
基于 OpenCV 的人臉識(shí)別
玩轉(zhuǎn)字符串
相機(jī)工作原理
Build 過(guò)程

避免濫用單例

單例是整個(gè) Cocoa 中被廣泛使用的核心設(shè)計(jì)模式之一。事實(shí)上,蘋果開發(fā)者庫(kù)把單例作為 "Cocoa 核心競(jìng)爭(zhēng)力" 之一。作為一個(gè)iOS開發(fā)者,我們經(jīng)常和單例打交道,比如 UIApplicationNSFileManager 等等。我們?cè)陂_源項(xiàng)目、蘋果示例代碼和 StackOverflow 中見過(guò)了無(wú)數(shù)使用單例的例子。Xcode 甚至有一個(gè)默認(rèn)的 "Dispatch Once" 代碼片段,可以使我們非常簡(jiǎn)單地在代碼中添加一個(gè)單例:

+ (instancetype)sharedInstance
{
    static dispatch_once_t once;
    static id sharedInstance;
    dispatch_once(&once, ^{
        sharedInstance = [[self alloc] init];
    });
    return sharedInstance;
}

由于這些原因,單例在 iOS 開發(fā)中隨處可見。問(wèn)題是,它們很容易被濫用。

盡管有些人認(rèn)為單例是 '反模式', '魔鬼' 以及 ,我不會(huì)去完全否認(rèn)單例所帶來(lái)的的好處,而是會(huì)展示一些使用單例所帶來(lái)的問(wèn)題,這樣下一次在使用 dispatch_once 代碼片段的自動(dòng)補(bǔ)全功能時(shí),你可以對(duì)它的影響進(jìn)行評(píng)估,三思而行。

全局狀態(tài)

大多數(shù)的開發(fā)者都認(rèn)同使用全局可變的狀態(tài)是不好的行為。太多狀態(tài)使得程序難以理解,難以調(diào)試。我們這些面向?qū)ο蟮某绦騿T在最小化代碼的狀態(tài)復(fù)雜程度的方面,有很多需要向函數(shù)式編程學(xué)習(xí)的地方。

@implementation SPMath {
    NSUInteger _a;
    NSUInteger _b;
}

- (NSUInteger)computeSum
{
    return _a + _b;
}

在上面這個(gè)簡(jiǎn)單的數(shù)學(xué)庫(kù)的實(shí)現(xiàn)中,程序員需要在調(diào)用 computeSum 前正確的設(shè)置實(shí)例變量 _a_b。這樣有以下問(wèn)題:

  1. computeSum 沒(méi)有顯式地通過(guò)使用參數(shù)的形式聲明它依賴于 _a_b 的狀態(tài)。與僅僅通過(guò)查看函數(shù)聲明就可以知道這個(gè)函數(shù)的輸出依賴于哪些變量不同的是,另一個(gè)開發(fā)者必須查看這個(gè)函數(shù)的具體實(shí)現(xiàn)才能明白這個(gè)函數(shù)依賴那些變量。隱藏依賴是不好的。

  2. 當(dāng)為調(diào)用 computeSum 做準(zhǔn)備而修改 _a_b 的數(shù)值時(shí),程序員需要保證這些修改不會(huì)影響任何其他依賴于這兩個(gè)變量的代碼的正確性。而這在多線程的環(huán)境中是尤其困難的。

把下面的代碼和上面的例子做對(duì)比:

+ (NSUInteger)computeSumOf:(NSUInteger)a plus:(NSUInteger)b
{
    return a + b;
}

這里,對(duì)變量 ab 的依賴被顯式地聲明了。我們不需要為了調(diào)用這個(gè)方法而去改變實(shí)例變量的狀態(tài)。并且我們也不需要擔(dān)心調(diào)用這個(gè)函數(shù)會(huì)留下持久的副作用。我們甚至可以把這個(gè)方法聲明為類方法,這樣就告訴了代碼的閱讀者這個(gè)方法不會(huì)修改任何實(shí)例的狀態(tài)。

那么,這個(gè)例子和單例又有什么關(guān)系呢?用 Mi?ko Hevery 的話來(lái)說(shuō),"單例就是披著羊皮的全局狀態(tài)"。一個(gè)單例可以被使用在任何地方,而不需要顯式地聲明依賴。就像變量 _a_bcomputeSum 內(nèi)部被使用了,卻沒(méi)有被顯式聲明一樣,程序的任意模塊都可以調(diào)用 [SPMySingleton sharedInstance] 并且訪問(wèn)這個(gè)單例。這意味著任何和這個(gè)單例交互產(chǎn)生的副作用都會(huì)影響程序其他地方的任意代碼。

@interface SPSingleton : NSObject

+ (instancetype)sharedInstance;

- (NSUInteger)badMutableState;
- (void)setBadMutableState:(NSUInteger)badMutableState;

@end

@implementation SPConsumerA

- (void)someMethod
{
    if ([[SPSingleton sharedInstance] badMutableState]) {
        // ...
    }
}

@end

@implementation SPConsumerB

- (void)someOtherMethod
{
    [[SPSingleton sharedInstance] setBadMutableState:0];
}

@end

在上面的例子中,SPConsumerASPConsumerB 是兩個(gè)完全獨(dú)立的模塊。但是 SPConsumerB 可以通過(guò)使用單例提供的共享狀態(tài)來(lái)影響 SPConsumerA 的行為。這種情況應(yīng)該只能發(fā)生在 consumer B 顯式引用了 A,并表明了兩者之間的關(guān)系時(shí)。這里使用了單例,由于其具有全局和多狀態(tài)的特性,導(dǎo)致隱式地在兩個(gè)看起來(lái)完全不相關(guān)的模塊之間建立了耦合。

讓我們來(lái)看一個(gè)更具體的例子,并且暴露一個(gè)使用全局可變狀態(tài)的額外問(wèn)題。比如我們想要在我們的應(yīng)用中構(gòu)建一個(gè)網(wǎng)頁(yè)查看器。為了支持這個(gè)查看器,我們構(gòu)建了一個(gè)簡(jiǎn)單的 URL cache:

@interface SPURLCache

+ (SPCache *)sharedURLCache;

- (void)storeCachedResponse:(NSCachedURLResponse *)cachedResponse forRequest:(NSURLRequest *)request;

@end    

這個(gè)開發(fā)者開始寫一些單元測(cè)試來(lái)保證代碼在一些不同的情況下都能達(dá)到預(yù)期。首先,他寫了一個(gè)測(cè)試用例來(lái)保證網(wǎng)頁(yè)查看器在設(shè)備沒(méi)有連接時(shí)能夠展示出錯(cuò)誤信息。然后他寫了一個(gè)測(cè)試用例來(lái)保證網(wǎng)頁(yè)查看器能夠正確的處理服務(wù)器錯(cuò)誤。最后,他為成功情況時(shí)寫了一個(gè)測(cè)試用例,來(lái)保證返回的網(wǎng)絡(luò)內(nèi)容能夠被正確的顯示出來(lái)。這個(gè)開發(fā)者運(yùn)行了所有的測(cè)試用例,并且它們都如預(yù)期一樣正確。贊!

幾個(gè)月以后,這些測(cè)試用例開始出現(xiàn)失敗,盡管網(wǎng)頁(yè)查看器的代碼從它寫完后就從來(lái)沒(méi)有再改動(dòng)過(guò)!到底發(fā)生了什么?

原來(lái),有人改變了測(cè)試的順序。處理成功的那個(gè)測(cè)試用例首先被運(yùn)行,然后再運(yùn)行其他兩個(gè)。處理錯(cuò)誤的那兩個(gè)測(cè)試用例現(xiàn)在竟然成功了,和預(yù)期不一樣,因?yàn)?URL cache 這個(gè)單例把不同測(cè)試用例之間的 response 緩存起來(lái)了。

持久化狀態(tài)是單元測(cè)試的敵人,因?yàn)閱卧獪y(cè)試在各個(gè)測(cè)試用例相互獨(dú)立的情況下才有效。如果狀態(tài)從一個(gè)測(cè)試用例傳遞到了另外一個(gè),這樣就和測(cè)試用例的執(zhí)行順序就有關(guān)系了。有 bug 的測(cè)試用例,尤其是那些本來(lái)不應(yīng)該通過(guò)的測(cè)試用例,是非常糟糕的事情。

對(duì)象的生命周期

另外一個(gè)關(guān)鍵問(wèn)題就是單例的生命周期。當(dāng)你在程序中添加一個(gè)單例時(shí),很容易會(huì)認(rèn)為 “永遠(yuǎn)只會(huì)有一個(gè)實(shí)例”。但是在很多我看到過(guò)的 iOS 代碼中,這種假定都可能被打破。

比如,假設(shè)我們正在構(gòu)建一個(gè)應(yīng)用,在這個(gè)應(yīng)用里用戶可以看到他們的好友列表。他們的每個(gè)朋友都有一張個(gè)人信息的圖片,并且我們想使我們的應(yīng)用能夠下載并且在設(shè)備上緩存這些圖片。 使用 dispatch_once 代碼片段,我們可以寫一個(gè) SPThumbnailCache 單例:

@interface SPThumbnailCache : NSObject

+ (instancetype)sharedThumbnailCache;

- (void)cacheProfileImage:(NSData *)imageData forUserId:(NSString *)userId;
- (NSData *)cachedProfileImageForUserId:(NSString *)userId;

@end

我們繼續(xù)構(gòu)建我們的應(yīng)用,一切看起來(lái)都很正常,直到有一天,我們決定去實(shí)現(xiàn)‘注銷’功能,這樣用戶可以在應(yīng)用中進(jìn)行賬號(hào)切換。突然我們發(fā)現(xiàn)我們將要面臨一個(gè)討厭的問(wèn)題:用戶相關(guān)的狀態(tài)存儲(chǔ)在全局單例中。當(dāng)用戶注銷后,我們希望能夠清理掉所有的硬盤上的持久化狀態(tài)。否則,我們將會(huì)把這些被遺棄的數(shù)據(jù)殘留在用戶的設(shè)備上,浪費(fèi)寶貴的硬盤空間。對(duì)于用戶登出又登錄了一個(gè)新的賬號(hào)這種情況,我們也想能夠?qū)@個(gè)新用戶使用一個(gè)全新的 SPThumbnailCache 實(shí)例。

問(wèn)題在于按照定義單例被認(rèn)為是“創(chuàng)建一次,永久有效”的實(shí)例。你可以想到一些對(duì)于上述問(wèn)題的解決方案。或許我們可以在用戶登出時(shí)移除這個(gè)單例:

static SPThumbnailCache *sharedThumbnailCache;

+ (instancetype)sharedThumbnailCache
{
    if (!sharedThumbnailCache) {
        sharedThumbnailCache = [[self alloc] init];
    }
    return sharedThumbnailCache;
}

+ (void)tearDown
{
    // The SPThumbnailCache will clean up persistent states when deallocated
    sharedThumbnailCache = nil;
}

這是一個(gè)明顯的對(duì)單例模式的濫用,但是它可以工作,對(duì)吧?

我們當(dāng)然可以使用這種方式去解決,但是代價(jià)實(shí)在是太大了。我們不能使用簡(jiǎn)單的的 dispatch_once 方案了,而這個(gè)方案能夠保證線程安全以及所有調(diào)用 [SPThumbnailCache sharedThumbnailCache] 的地方都能訪問(wèn)到同一個(gè)實(shí)例?,F(xiàn)在我們需要對(duì)使用縮略圖 cache 的代碼的執(zhí)行順序非常小心。假設(shè)當(dāng)用戶正在執(zhí)行登出操作時(shí),有一些后臺(tái)任務(wù)正在執(zhí)行把圖片保存到緩存中的操作:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [[SPThumbnailCache sharedThumbnailCache] cacheProfileImage:newImage forUserId:userId];
});

我們需要保證在所有的后臺(tái)任務(wù)完成前, tearDown 一定不能被執(zhí)行。這確保了 newImage 數(shù)據(jù)可以被正確的清理掉?;蛘?,我們需要保證在縮略圖 cache 被移除時(shí),后臺(tái)緩存任務(wù)一定要被取消掉。否則,一個(gè)新的縮略圖 cache 的實(shí)例將會(huì)被延遲創(chuàng)建,并且之前用戶的數(shù)據(jù) (newImage 對(duì)象) 會(huì)被存儲(chǔ)在它里面。

由于對(duì)于單例實(shí)例來(lái)說(shuō)它沒(méi)有明確的所有者,(因?yàn)閱卫约汗芾碜约旱纳芷?,“關(guān)閉”一個(gè)單例變得非常的困難。

分析到這里,我希望你能夠意識(shí)到,“這個(gè)縮略圖 cache 從來(lái)就不應(yīng)該作為一個(gè)單例!”。問(wèn)題在于一個(gè)對(duì)象得生命周期可能在項(xiàng)目的最初階段沒(méi)有被很好得考慮清楚。舉一個(gè)具體的例子,Dropbox 的 iOS 客戶端曾經(jīng)只支持一個(gè)賬號(hào)登錄。它以這樣的狀態(tài)存在了數(shù)年,直到有一天我們希望能夠同時(shí)支持多個(gè)用戶賬號(hào)登錄 (同時(shí)登陸私人賬號(hào)和工作賬號(hào))。突然之間,我們以前的的假設(shè)“只能夠同時(shí)有一個(gè)用戶處于登錄狀態(tài)”就不成立了。如果假定了一個(gè)對(duì)象的生命周期和應(yīng)用的生命周期一致,那你的代碼的靈活擴(kuò)展就受到了限制,早晚有一天當(dāng)產(chǎn)品的需求產(chǎn)生變化時(shí),你會(huì)為當(dāng)初的這個(gè)假定付出代價(jià)的。

這里我們得到的教訓(xùn)是,單例應(yīng)該只用來(lái)保存全局的狀態(tài),并且不能和任何作用域綁定。如果這些狀態(tài)的作用域比一個(gè)完整的應(yīng)用程序的生命周期要短,那么這個(gè)狀態(tài)就不應(yīng)該使用單例來(lái)管理。用一個(gè)單例來(lái)管理用戶綁定的狀態(tài),是代碼的壞味道,你應(yīng)該認(rèn)真的重新評(píng)估你的對(duì)象圖的設(shè)計(jì)。

避免使用單例

既然單例對(duì)局部作用域的狀態(tài)有這么多的壞處,那么我們應(yīng)該怎樣避免使用它們呢?

讓我們來(lái)重溫一下上面的例子。既然我們的縮略圖 cache 的緩存狀態(tài)是和具體的用戶綁定的,那么讓我們來(lái)定義一個(gè)user對(duì)象吧:

@interface SPUser : NSObject

@property (nonatomic, readonly) SPThumbnailCache *thumbnailCache;

@end

@implementation SPUser

- (instancetype)init
{
    if ((self = [super init])) {
        _thumbnailCache = [[SPThumbnailCache alloc] init];

        // Initialize other user-specific state...
    }
    return self;
}

@end

我們現(xiàn)在用一個(gè)對(duì)象來(lái)作為一個(gè)經(jīng)過(guò)認(rèn)證的用戶會(huì)話的模型類,并且我們可以把所有和用戶相關(guān)的狀態(tài)存儲(chǔ)在這個(gè)對(duì)象中?,F(xiàn)在假設(shè)我們有一個(gè)view controller來(lái)展現(xiàn)好友列表:

@interface SPFriendListViewController : UIViewController

- (instancetype)initWithUser:(SPUser *)user;

@end

我們可以顯式地把經(jīng)過(guò)認(rèn)證的 user 對(duì)象作為參數(shù)傳遞給這個(gè) view controller。這種把依賴性傳遞給依賴對(duì)象的技術(shù)正式的叫法是依賴注入,它有很多優(yōu)點(diǎn):

  1. 對(duì)于閱讀這個(gè) SPFriendListViewController 頭文件的讀者來(lái)說(shuō),可以很清楚的知道它只有在有登錄用戶的情況下才會(huì)被展示。
  2. 這個(gè) SPFriendListViewController 只要還在使用中,就可以強(qiáng)引用 user 對(duì)象。舉例來(lái)說(shuō),對(duì)于前面的例子,我們可以像下面這樣在后臺(tái)任務(wù)中保存一個(gè)圖片到縮略圖 cache 中:

      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
          [_user.thumbnailCache cacheProfileImage:newImage forUserId:userId];
      });

    就算后臺(tái)任務(wù)還沒(méi)有完成,應(yīng)用其他地方的代碼也可以創(chuàng)建和使用一個(gè)全新的 SPUser 對(duì)象,而不會(huì)在清理第一個(gè)實(shí)例時(shí)阻塞用戶交互。

為了更詳細(xì)的說(shuō)明一下第二點(diǎn),讓我們畫一下在使用依賴注入之前和之后的對(duì)象圖。

假設(shè)我們的 SPFriendListViewController 是當(dāng)前 window 的 root view controller。使用單例時(shí),我們的對(duì)象圖看起來(lái)如下所示:

http://wiki.jikexueyuan.com/project/objc/images/13-4.png" alt="" />

view controller 自己,以及自定義的 image view 的列表,都會(huì)和 sharedThumbnailCache 產(chǎn)生交互。當(dāng)用戶登出后,我們想要清理 root view controller 并且退出到登錄頁(yè)面:

http://wiki.jikexueyuan.com/project/objc/images/13-5.png" alt="" />

這里的問(wèn)題在于這個(gè)好友列表的 view controller 可能仍然在執(zhí)行代碼 (由于后臺(tái)操作的原因),并且可能因此仍然有一些沒(méi)有執(zhí)行的涉及到 sharedThumbnailCache 的調(diào)用。

和使用依賴注入的解決方案對(duì)比一下:

http://wiki.jikexueyuan.com/project/objc/images/13-6.png" alt="" />

簡(jiǎn)單起見,假設(shè) SPApplicationDelegate 管理 SPUser 的實(shí)例 (在實(shí)踐中,你可能會(huì)把這些用戶狀態(tài)的管理工作交給另外一個(gè)對(duì)象來(lái)做,這樣可以使你的 application delegate 簡(jiǎn)化)。當(dāng)展現(xiàn)好友列表 view controller 時(shí),會(huì)傳遞進(jìn)去一個(gè) user 的引用。這個(gè)引用也會(huì)向下傳遞給 profile image views。現(xiàn)在,當(dāng)用戶登出時(shí),我們的對(duì)象圖如下所示:

http://wiki.jikexueyuan.com/project/objc/images/13-7.png" alt="" />

這個(gè)對(duì)象圖看起來(lái)和使用單例時(shí)很像。那么,區(qū)別是什么呢?

關(guān)鍵問(wèn)題是作用域。在單例那種情況中,sharedThumbnailCache 仍然可以被程序的任意模塊訪問(wèn)。假如用戶快速的登錄了一個(gè)新的賬號(hào)。該用戶也想看看他的好友列表,這也就意味著需要再一次的和縮略圖 cache 產(chǎn)生交互:

http://wiki.jikexueyuan.com/project/objc/images/13-8.png" alt="" />

當(dāng)用戶登錄一個(gè)新賬號(hào),我們應(yīng)該能夠構(gòu)建并且與全新的 SPThumbnailCache 交互,而不需要再在銷毀老的縮略圖 cache 上花費(fèi)精力?;趯?duì)象管理的典型規(guī)則,老的 view controllers 和老的縮略圖 cache 應(yīng)該能夠自己在后臺(tái)延遲被清理掉。簡(jiǎn)而言之,我們應(yīng)該隔離用戶 A 相關(guān)聯(lián)的狀態(tài)和用戶 B 相關(guān)聯(lián)的狀態(tài):

http://wiki.jikexueyuan.com/project/objc/images/13-9.png" alt="" />

結(jié)論

希望這篇文章中的內(nèi)容讀起來(lái)不像奇幻小說(shuō)那樣難以理解。人們已經(jīng)對(duì)單例的濫用抱怨了很多年了,并且我們也都知道全局狀態(tài)是很不好的事情。但是在 iOS 開發(fā)的世界中,單例的使用是如此的普遍以至于我們有時(shí)候忘記了我們多年來(lái)在其他面向?qū)ο缶幊讨袑W(xué)到的教訓(xùn)。

這一切的關(guān)鍵點(diǎn)是,在面向?qū)ο缶幊讨形覀兿胍钚』勺儬顟B(tài)的作用域。但是單例卻因?yàn)槭箍勺兊臓顟B(tài)可以被程序中的任何地方訪問(wèn),而站在了對(duì)立面。下一次你想使用單例時(shí),我希望你能夠好好考慮一下使用依賴注入作為替代方案。