最近、依存性注入を使用することが「今日のソフトウェア開発の世界でシングルトンを使用する唯一の社会的に受け入れられる方法」であると聞きました。この声明はほとんど意見に基づいているため、現時点でこの声明の正確性について必ずしも議論したくはありません。今の私の目標は、シングルトンパターンで依存性注入をどのように正確に使用できるかを理解することです。
たとえば、最新のiOSアプリには、URLSessionコードを保持するサービスレイヤーがあります。このレイヤーをシングルトンとして作成しました。
struct ServiceSingleton {
private init()
static let shared = ServiceSingleton()
func fetchJSON() {
// URLSession code
}
}
次に、以下のようshared
に、ViewControllerで使用します。
class ViewController: UIViewController() {
override viewDidLoad() {
super.viewDidLoad()
fetchData()
}
fileprivate func fetchData() {
ServiceSingleton.shared.fetchJSON()
}
}
もちろん、上記のコードはシングルトンを使用していますが、依存性注入は使用していません。一般に依存性注入を使用したい場合は、ViewControllerに次のようなものを追加することを認識しています。
// Dependency Injection Constructor
override init(someProperty: SomePropertyType) {
self.someProperty = someProperty
super.init()
}
TL; DR:
(1)Swiftでシングルトンパターンを使用して依存性注入を適切に使用する方法を教えてください。
(2)これが何を達成するのか説明してもらえますか?
(3)今後iOSプロジェクトでシングルトンパターンを使用する場合、常にDIを使用する必要がありますか?
Swiftのシングルトンパターンで依存性注入を適切に使用する方法を教えてください。
ServiceSingleton.shared
直接アクセスするのではなく、オブジェクトに挿入されるインスタンス変数にアクセスします。通常は、可能であれば初期化子で、そうでない場合は初期化後に設定可能なプロパティとしてアクセスします。
protocol FooService {
func doFooStuff()
}
class ProductionFooService: FooService {
private init() {}
static let shared = ProductionFooService()
func doFooStuff() {
print("real URLSession code goes here")
}
}
struct MockFooService: FooService {
func doFooStuff() {
print("Doing fake foo stuff!")
}
}
class FooUser {
let fooService: FooService
init(fooService: FooService) { // "initializer based" injection
self.fooService = fooService
}
func useFoo() {
fooService.doFooStuff() // Doesn't directly call ProductionFooService.shared.doFooStuff
}
}
let isRunningInAUnitTest = false
let fooUser: FooUser
if !isRunningInAUnitTest {
fooUser = FooUser(fooService: ProductionFooService.shared) // In a release build, this is used.
}
else {
fooUser = FooUser(fooService: MockFooService()) // In a unit test, this is used.
}
fooUser.useFoo()
通常、ViewControllerの初期化はストーリーボードによって行われるため、初期化パラメーターを介して依存関係を検出することはできず、代わりにオブジェクトの初期化後に設定される保存済みプロパティを使用する必要があります。
これが何を達成するのか説明してもらえますか?
コードはに結合されなくなりましたProductionFooService.shared
。この結果FooService
、ベータ環境用の実装、単体テスト用のモック実装など、さまざまな実装を導入できます。
すべてのコードが直接製品の依存関係を広く使用している場合は、...
テスト環境でオブジェクトをインスタンス化することは不可能であることがわかります。単体テスト、CIテスト環境、ベータ環境などを製品データベース、サービス、APIに接続する必要はありません。
真の「ユニット」テストはありません。すべてのテストは、コードの単位に加えて、推移的に依存するすべての一般的な依存関係をテストします。これらの依存関係の1つにコード変更を加えると、システム内のほとんどの単体テストが失敗し、失敗したものを正確に特定することが困難になります。依存関係を分離することで、単体テストをサポートするために最低限必要なモックオブジェクトを使用し、各テストが依存する推移的な依存関係ではなく、特定のコード単位のみをテストするようにすることができます。
今後iOSプロジェクトでシングルトンパターンを使用する場合、常にDIを使用する必要がありますか?
拾うのは良い習慣です。もちろん、速く移動したいだけであまり気にしないqucik-and-dirty-projectsもありますが、これらの想定されるqucik-and-dirty-projectsの多くが実際に離陸することに驚かれることでしょう。将来的に費用を支払う。あなたは自分の良識を切り離すために余分な時間をかけないことによって自分自身を妨げているときを認識する必要があります。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加