2つのクラスDog
とCat
が両方ともAnimal
プロトコルに準拠していると考えてください(Swiftプログラミング言語に関して)。これはJavaのインターフェースです。/C#)。
犬と猫の混合リストを表示する画面があります。舞台裏のロジックを処理するInteractor
クラスがあります。
ここで、ユーザーが猫を削除するときに確認アラートを表示したいとします。ただし、犬はアラートなしですぐに削除する必要があります。条件付きのメソッドは次のようになります。
func tryToDeleteModel(model: Animal) {
if let model = model as? Cat {
tellSceneToShowConfirmationAlert()
} else if let model = model as? Dog {
deleteModel(model: model)
}
}
このコードはどのようにリファクタリングできますか?それは明らかににおいがする
プロトコルタイプitselfに動作を決定させます。プログラム全体ですべてのプロトコルを同じように扱いたいexcept実装クラス自体で。このようにすることは、Cat
またはDog
(または最終的にAnimal
の下にある可能性のある他のプロトコル)を渡すことができるはずであると述べているLiskovの置換原則を尊重しています。 、無関心に動作させます。
そのため、isCritical
とAnimal
の両方によって実装されるDog
にCat
funcを追加するとします。 Dog
を実装するものはすべてfalseを返し、Cat
を実装するものはすべてtrueを返します。
その時点で、必要な作業は次のとおりです(構文が正しくない場合の謝罪。Swiftのユーザーではありません)。
func tryToDeleteModel(model: Animal) {
if model.isCritical() {
tellSceneToShowConfirmationAlert()
} else {
deleteModel(model: model)
}
}
これには小さな問題しかありません。つまり、Dog
とCat
はプロトコルであるため、それら自体ではisCritical
が返すものを決定せず、これをすべての自分で決めるクラスを実装する。実装が多数ある場合は、Cat
またはDog
の拡張可能なクラスを作成して、isCritical
を正しく実装し、すべての実装クラスを効率的にクリアすることは、おそらく時間の価値があります。 isCritical
をオーバーライドする必要性から。
これがあなたの質問に答えない場合は、コメントに書き込んでください。それに応じて私の答えを広げます!
あなたが示している条件付きアプローチは、 "ask"と呼びます。これは、消費側のクライアントasks「あなたはどのような人ですか?」それに応じて、オブジェクトの動作とオブジェクトとの相互作用をカスタマイズします。
これは、 "tell"と呼ぶ別の方法とは対照的です。 tellを使用すると、より多くの作業をポリモーフィック実装にプッシュできるため、使用するクライアントコードは、条件なしでよりシンプルになり、実装に関係なく共通になります。
確認アラートを使用したいので、それをインターフェースの明示的な機能にすることができます。したがって、オプションでユーザーに確認し、確認ブール値を返すブールメソッドがあるとします。確認したくないクラスでは、単にreturn true;
でオーバーライドします。他の実装では、確認を使用するかどうかを動的に決定する場合があります。
使用しているクライアントは、使用している特定のサブクラスに関係なく、常に確認メソッドを使用するため、askの代わりにtellが相互作用します。
(別のアプローチは、確認を削除にプッシュすることですが、削除操作が成功することを期待する消費クライアントを驚かせます。)
確認が必要かどうかを判断するのはCat
クラスの責任なので、そのアクションを実行できるようにします。 Kotlinは知らないので、C#で表現します。うまくいけば、アイデアはKotlinにも転送できます。
interface Animal
{
bool IsOkToDelete();
}
class Cat : Animal
{
private readonly Func<bool> _confirmation;
public Cat (Func<bool> confirmation) => _confirmation = confirmation;
public bool IsOkToDelete() => _confirmation();
}
class Dog : Animal
{
public bool IsOkToDelete() => true;
}
次に、Cat
インスタンスを作成するときに、TellSceneToShowConfirmationAlert
を指定します。削除してもよければ、true
を返す必要があります。
var model = new Cat(TellSceneToShowConfirmationAlert);
そして、あなたの関数は次のようになります:
void TryToDeleteModel(Animal model)
{
if (model.IsOKToDelete())
{
DeleteModel(model)
}
}
Visitorパターンに行くことをお勧めします。 Javaで小さな実装を行いました。私はSwiftに慣れていませんが、簡単に適応できます。
訪問者
public interface AnimalVisitor<R>{
R visitCat();
R visitDog();
}
あなたのモデル
abstract class Animal { // can also be an interface like VisitableAnimal
abstract <R> R accept(AnimalVisitor<R> visitor);
}
class Cat extends Animal {
public <R> R accept(AnimalVisitor<R> visitor) {
return visitor.visitCat();
}
}
class Dog extends Animal {
public <R> R accept(AnimalVisitor<R> visitor) {
return visitor.visitDog();
}
}
訪問者に電話する
public void tryToDelete(Animal animal) {
animal.accept( new AnimalVisitor<Void>() {
public Void visitCat() {
tellSceneToShowConfirmation();
return null;
}
public Void visitDog() {
deleteModel(animal);
return null;
}
});
}
AnimalVisitorの実装は必要なだけ持つことができます。
例:
public void isColorValid(Color color) {
animal.accept( new AnimalVisitor<Boolean>() {
public Boolean visitCat() {
return Color.BLUE.equals(color);
}
public Boolean visitDog() {
return true;
}
});
}