内容:
パート0。シングルトン・ロナー
パート1.戦略
パート2.オブザーバー
この一連の記事では、EricとElizabeth Freemanによる書籍Design Patternsを分解していることを思い出させてください。 そして、今日は「戦略」パターンを研究します。 行こう
脚(および翼)の成長元
この本の著者は、SimUDuckアプリケーションの作成に関するストーリーを教えてくれます。 アプリケーションの初期状態を実装することから始めましょう。抽象Duck
クラスとその2つの子孫MallardDuck
とRedheadDuck
ます。 ここで最初の難題に直面しています。Objective-CとSwiftには抽象クラスがありません。
Objective-Cの場合、通常のDuck
クラスを宣言し(デフォルトの実装を持つ)、 AbstractDuck
プロトコルを追加します(子孫に実装する必要のある抽象メソッドが含まれます)。 次のようになります。
// Objective-C @protocol AbstractDuck <NSObject> - (void)display; @end @interface Duck : NSObject - (void)quack; - (void)swim; @end
したがって、相続人は次のようになります。
// Objective-C @interface MallardDuck : Duck <AbstractDuck> @end @implementation MallardDuck - (void)display { } @end @interface RedheadDuck : Duck <AbstractDuck> @end @implementation RedheadDuck - (void)display { } @end
Swiftでは、これは少し簡単です。プロトコルとその拡張機能で十分です(拡張機能では、デフォルトでいくつかのプロトコルメソッドを実装できます)。
// Swift protocol Duck { func quack() func swim() func display() } extension Duck { func quack() { } func swim() { } }
そして相続人:
// Swift class MallardDuck: Duck { func display() { } } class RedheadDuck: Duck { func display() { } }
アプリケーションは開発中であり、アヒルは飛ぶ機会があります
これを行うには、対応するメソッドが親クラスDuck
表示されます。 そしてその後すぐに、別の相続人、 RubberDuck
がいることがRubberDuck
。 ゴム製のアヒルは飛ぶことはありません。このメソッドは親クラスに追加されるため、ゴム製のアヒルで使用できます。 一般に、状況は単純ではありませんでした。 アプリケーションがさらに拡張されると、飛行機能のサポート(およびそれだけでなく、同じストーリーを偽造する機能)および他の種類のアヒル(たとえば木製)のサポートで問題が発生します。
最初に、この本の著者は、 Quackable
とQuackable
別のインターフェイス(Objective-cおよびSwiftプロトコル用)にフライト機能とクワック機能を配置することで問題を解決することを提案します。 しかし、このオプションは、一見すると思われるほどではありません。 飛行機能のわずかな変更は、すべての飛行アヒルに適用する必要がありますが、プログラムの多くの場所に同じコードを導入する必要があります。 したがって、このようなソリューションは間違いなく適切ではありません。
(このオプションの不適当と言えば、著者はインターフェース(私たちのプロトコルの場合)にはデフォルトの実装はないという事実に言及していますが、これはObjective-Cにのみ当てはまりますが、Swiftでは、フライトとクッキングのデフォルトの実装はこれらのプロトコルの拡張およびこれらの機能の再定義は必要な場合にのみ行われ、どこでも行われません)
それに、パターンの主な目標の1つは、実行時の動作の代替実装です。したがって、著者は、 Duck
クラスから動作の実装を削除することを提案しています。
これを行うには、 FlyBehavior
およびQuackBehavior
作成しQuackBehavior
。
// Objective-C @protocol FlyBehavior <NSObject> - (void)fly; @end @protocol QuackBehavior <NSObject> - (void)quack; @end
// Swift protocol FlyBehavior { func fly() } protocol QuackBehavior { func quack() }
そして、これらのプロトコルを実装する特定のクラス: FlyWithWings
とFlyNoWay
、およびFlyNoWay
Quack
、 Squeak
、 QuackBehavior
( QuackBehavior
の例をFlyWithWings
が、残りは同様の方法で実装されます):
// Objective-C @interface FlyWithWings : NSObject <FlyBehavior> @end @implementation FlyWithWings - (void)fly { // fly implementation } @end
// Swift class FlyWithWings: FlyBehavior { func fly() { // fly implementation } }
私たちの代表団がすべてです
実際、対応するインターフェイス(プロトコル)を実装する他のクラスに動作を委任します。 飛行中および鳴き声での行動をアヒルに伝える方法は? 非常に簡単です。クラスに2つのプロパティを追加します(Swift-プロトコル) Duck
:
// Objective-C @property (strong, nonatomic) id<FlyBehavior> flyBehavior; @property (strong, nonatomic) id<QuackBehavior> quackBehavior;
// Swift var flyBehavior: FlyBehavior { get set } var quackBehavior: QuackBehavior { get set }
ご覧のように、それらには特定のタイプが定義されておらず、適切なプロトコルに署名されたクラスであると判断されるだけです。
親クラス(またはプロトコル) Duck
のfly
quack
とquack
、同様のquack
置き換えられます。
// Objective-C - (void)performFly { [self.flyBehavior fly]; } - (void)performQuack { [self.quackBehavior quack]; }
// Swift func performFly() { flyBehavior.fly() } func performQuack() { quackBehavior.quack() }
これで、アヒルは単にその動作を対応する動作オブジェクトに委任します。 各アヒルの行動をどのように確立しますか? たとえば、初期化中( MallardDuck
例):
// Objective-C - (instancetype)init { self = [super init]; if (self) { self.flyBehavior = [[FlyWithWings alloc] init]; self.quackBehavior = [[Quack alloc] init]; } return self; }
// Swift init() { self.flyBehavior = FlyWithWings() self.quackBehavior = Quack() }
パターンの準備ができました:)
おわりに
iOS開発では、たとえば、 MVPアーキテクチャで「戦略」パターンを見つけることができます。その中で、プレゼンターはView Controllerの動作オブジェクトにすぎません(ご存知のように、View Controllerはプレゼンターにユーザーのアクションのみを通知しますが、データ処理はプレゼンターによって決定されます)、およびその逆:View Controllerはプレゼンターの動作オブジェクトです(プレゼンターは「ユーザーデータを表示する」とだけ言いますが、View Controllerは表示方法を決定します)。 もちろん、このパターンをアプリケーションで使用することにした場合、このパターンはVIPERでも見つかります。 :)