Swift でコンパス可能なオブジェクトを構築する方法を作成しようとしています。私は自分が持っているものでほとんどそこにいるように感じますが、それでも100%正しいわけではありません.
私が目指しているのは、FlowControllerオブジェクトを作成UIViewControllersして必要な依存関係を与えることができるオブジェクトを持つことです。
私がやりたいことは、この作業をできるだけゆるくすることです。
ここに、機能するが理想的ではない小さな例があります。説明します...
コンポーネントとして使用できる 2 つのオブジェクトを次に示します...WalletとUser.
class Wallet {
func topUp(amount: Int) {
print("Top up wallet with £\(amount)")
}
}
class User {
func sayHello() {
Print("Hello, world!")
}
}
Component次に、これらのそれぞれのケースを持つ列挙型を定義します...
enum Component {
case Wallet
case User
}
requiresComponents...そして、の配列を返すメソッドを定義するプロトコルComponents。
ここで問題が発生します。「ファクトリ オブジェクト」がコンポーネントをオブジェクトに入れるためには、プロトコルでもプロパティComposableを定義する必要がありますuser。wallet
protocol Composable {
var user: User? {get set}
var wallet: Wallet? {get set}
func requiresComponents() -> [Component]
}
これらのプロパティを「オプション」(オプションではない) にしようとして、Composableこれらの変数を nil として定義するプロトコルの拡張を定義しました。
extension Composable {
var user: User? {
get {return nil}
set {}
}
var wallet: Wallet? {
get {return nil}
set {}
}
}
次に、作成したいクラスを宣言しますComposable。ご覧のとおり、Userコンポーネントが必要で、変数を宣言しています。
class SomeComposableClass: Composable {
var user: User?
func requiresComponents() -> [Component] {
return [.User]
}
}
これで、FlowControllerこれらが作成され、コンポーネントが追加されます。varここで、オブジェクトを取得し、そのローカルバージョンを作成してから、更新されたオブジェクトを返す必要があることがわかります。これは、プロトコルに準拠するオブジェクトのタイプがわからないため、パラメーターを変更できないためだと思います。
class FlowController {
func addComponents<T: Composable>(toComposableObject object: T) -> T {
var localObject = object
for component in object.requiresComponents() {
switch component {
case .Wallet:
localObject.wallet = Wallet()
print("Wallet")
case .User:
localObject.user = User()
print("User")
}
}
return localObject
}
}
ここでオブジェクトを作成します。
let flowController = FlowController()
let composable = SomeComposableClass()
ここで、コンポーネントを追加します。本番環境では、これはすべて 内で行われますFlowController。
flowController.addComponents(toComposableObject: composable) // prints "User" when adding the user component
compassable.user?.sayHello() // prints "Hello, world!"
ご覧のとおり、ここで機能します。ユーザー オブジェクトが追加されます。
ただし、ご覧のとおり。プロトコルで vars を宣言したので、composableオブジェクトもwalletコンポーネントへの参照を持っています (ただし、常に nil になります)。
composable.wallet // nil
私はこれで約 95% の道のりを進んでいるように感じますが、私ができるようにしたいのは、プロパティの宣言方法を改善することです。私が望むのは、その最後の行が...composable.walletコンパイルエラーになることです。
プロパティの宣言をプロトコルの外に移動することでこれを行うことができますが、プロトコルに準拠するオブジェクトにプロパティを追加できないという問題がありComposableます。
ファクトリ オブジェクトが宣言に依存せずにプロパティを追加できることは素晴らしいことです。または、「このオブジェクトにユーザーを呼び出すプロパティがある場合、それにユーザーコンポーネントを追加する」というある種のガードがあります。またはそのようなもの。
この作業の残りの 5% を取得する方法を誰かが知っていれば、それは素晴らしいことです。私が言ったように、これはうまくいきますが、理想的な方法ではありません.
ありがとう
ハッキー編集
うーん... ばかげた、恐ろしい、「誰もこれをすべきではない」編集として。プロトコル拡張を次のように変更しました...
extension Composable {
var user: User? {
get {fatalError("Access user")}
set {fatalError("Set user")}
}
var wallet: Wallet? {
get {fatalError("Access wallet")}
set {fatalError("Set waller")}
}
}
少なくとも、定義していない変数にアクセスしようとすると、プログラムがクラッシュします。しかし、それはまだ理想的ではありません。
ダニエルのブログを読んだ後に編集
OK、やりたいことはできたと思います。それが正確にSwiftyであるかどうかはわかりません。とはいえ、そうかもしれないとも思います。セカンドオピニオンを探しています:)
それで、私のコンポーネントとプロトコルはこのようになりました...
// these are unchanged
class Wallet {
func topUp(amount: Int) {
print("Top up wallet with £\(amount)")
}
}
// each component gets a protocol
protocol WalletComposing {
var wallet: Wallet? {get set}
}
class User {
func sayHello() {
print("Hello, world!")
}
}
protocol UserComposing {
var user: User? {get set}
}
現在、ファクトリメソッドが変更されています...
// this is the bit I'm unsure about.
// I now have to check for conformance to each protocol
// and add the components accordingly.
// does this look OK?
func addComponents(toComposableObject object: AnyObject) {
if var localObject = object as? UserComposing {
localObject.user = User()
print("User")
}
if var localObject = object as? WalletComposing {
localObject.wallet = Wallet()
print("Wallet")
}
}
これにより、私はこれを行うことができます...
class SomeComposableClass: UserComposing {
var user: User?
}
class OtherClass: UserComposing, WalletComposing {
var user: User?
var wallet: Wallet?
}
let flowController = FlowController()
let composable = SomeComposableClass()
flowController.addComponents(toComposableObject: composable)
composable.user?.sayHello()
composable.wallet?.topUp(amount: 20) // this is now a compile time error which is what I wanted :D
let other = OtherClass()
flowController.addComponents(toComposableObject: other)
other.user?.sayHello()
other.wallet?.topUp(amount: 10)