オブジェクトの機能は、実装するインターフェースによってのみ識別されるべきですか?


36

C#のis演算子とJavaのinstanceof演算子を使用すると、オブジェクトインスタンスが実装しているインターフェイス(または、より基本的にはその基本クラス)で分岐できます。

インターフェイスが提供する機能に基づいた高レベルの分岐にこの機能を使用することは適切ですか?

または、オブジェクトが持つ機能を記述するためのインターフェースを提供するために、基本クラスがブール変数を提供する必要がありますか?

例:

if (account is IResetsPassword)
       ((IResetsPassword)account).ResetPassword();
else
       Print("Not allowed to reset password with this account type!");

if (account.CanResetPassword)
       ((IResetsPassword)account).ResetPassword();
else
       Print("Not allowed to reset password with this account type!");

機能の識別にインターフェイス実装を使用することには落とし穴がありますか?


この例は単なる例でした。もっと一般的なアプリケーションについて考えています。


10
2つのコード例を見てください。どちらが読みやすいですか?
ロバートハーヴェイ

39
ただ私の意見ですが、適切な型に安全にキャストできるようにするためだけに、最初の方法を使用します。2つ目は、CanResetPassword何かが実装されてIResetsPasswordいる場合にのみtrueになることを前提としています。これで、型システムが制御する必要のある(および最終的にキャストを実行するときに)プロパティを決定します。
ベカズ


14
あなたの質問のタイトルと質問の本文は異なることを尋ねます。タイトルに答えるには:理想的には、はい。本文に対処するには:型を確認して手動でキャストすることに気付いた場合は、おそらく型システムを正しく活用していないでしょう。この状況にどのように気付いたのか、具体的に教えてください。
メタファイト

9
この質問は簡単に思えるかもしれませんが、考え始めるとかなり広範になります。私の頭に浮かぶ質問:既存のアカウントに新しい動作を追加するか、異なる動作を持つアカウントタイプを作成する予定ですか?すべてのアカウントタイプに同様の動作がありますか、それとも大きな違いがありますか?サンプルコードは、アカウントのすべてのタイプ、または基本的なIAccountインターフェイスについて知っていますか?
陶酔

回答:


7

可能であればダウンキャストを避けるのが最善であるという注意事項があるため、2つの理由から最初の形式が望ましいです。

  1. 型システムを機能させます。最初の例では、is火災が発生するとダウンキャストが確実に機能します。2番目の形式では、実装するオブジェクトのみIResetsPasswordがそのプロパティに対してtrueを返すことを手動で確認する必要があります
  2. 壊れやすい基本クラス。追加するすべてのインターフェイスの基本クラス/インターフェイスにプロパティを追加する必要があります。これは面倒でエラーが発生しやすくなります。

これは、これらの問題を多少改善できないと言っているわけではありません。たとえば、基本クラスに、インクルードをチェックできる一連の公開されたインターフェイスを持たせることができます。しかし、実際には、既存の型システムの冗長な部分を手動で実装しているだけです。

ところで私の自然な好みは継承よりも合成ですが、それは主に状態に関してです。通常、インターフェイスの継承はそれほど悪くはありません。また、貧乏人の型階層を実装していることに気付いた場合は、コンパイラが役立つので、すでに存在する階層を使用する方が良いでしょう。


また、合成タイピングが非常に好きです。この特定の例が適切に活用していないことを私に見せてくれて+1。ただし、(従来の継承のように)計算タイピングは、機能が大幅に異なる場合(実装方法とは対照的に)より制限されます。したがって、構成アプローチまたは標準継承とは異なる問題を解決するインターフェイスアプローチです。
TheCatWhisperer

次のようにチェックを簡素化できます:(account as IResettable)?.ResetPassword();またはvar resettable = account as IResettable; if(resettable != null) {resettable.ResetPassword()} else {....}
ベリンロリチュ

89

オブジェクトの機能は、実装するインターフェースによってのみ識別されるべきですか?

オブジェクトの機能をまったく特定しないでください。

オブジェクトを使用するクライアントは、それがどのように機能するかについて何も知る必要はありません。クライアントは、オブジェクトに実行するよう指示できることだけを知っている必要があります。オブジェクトに通知されると、クライアントの問題ではありません。

というよりも

if (account is IResetsPassword)
    ((IResetsPassword)account).ResetPassword();
else
    Print("Not allowed to reset password with this account type!");

または

if (account.CanResetPassword)
    ((IResetsPassword)account).ResetPassword();
else
    Print("Not allowed to reset password with this account type!");

考える

account.ResetPassword();

または

account.ResetPassword(authority);

accountこれが機能するかどうかはすでにわかっているからです。なぜ聞くのですか?あなたが望むことを伝えて、それが何をしようとしてもやらせてください。

これが他の問題であるためにクライアントがそれが機能するかどうか気にしないような方法で行われたと想像してください。クライアントの仕事は、試みをすることだけでした。それは今や完了しており、他に対処すべきことがあります。このスタイルには多くの名前がありますが、私が一番好きな名前は教えてください、尋ねないでください

クライアントを書くとき、あなたはすべてを追跡しなければならないと思うので、あなたが知る必要があると思うすべてをあなたに引き寄せることは非常に魅力的です。それを行うと、オブジェクトを裏返しにします。あなたの無知を大切に。詳細を押しのけ、オブジェクトにそれらを処理させます。知れば知るほど良くなります。


54
ポリモーフィズムは、自分が話していることを正確に知らないか気にしないときにのみ機能します。したがって、私がその状況に対処する方法は、それを慎重に回避することです。
candied_orange

7
クライアントが実際に試行が成功したかどうかを知る必要がある場合、メソッドはブール値を返す可能性があります。if (!account.AttemptPasswordReset()) /* attempt failed */;このようにして、クライアントは結果を認識しますが、問題の決定ロジックに関する問題のいくつかを回避します。試行が非同期の場合、コールバックを渡します。
Radiodef

5
@DavidZ私たちは両方とも英語を話します。したがって、このコメントに2回賛成票を投じることができます。それはあなたがそれをする能力があるということですか?私があなたにそうするように言う前に、あなたができるかどうか知る必要がありますか?
candied_orange

5
@QPaysTaxesは、エラーハンドラーが作成されたaccountときに渡されたことを知っています。この時点で、ポップアップ、ロギング、印刷、および音声アラートが発生する可能性があります。クライアントの仕事は「今すぐやる」と言うことです。それ以上のことをする必要はありません。すべてを1か所で行う必要はありません。
candied_orange

6
@QPaysTaxes他の場所でさまざまな方法で処理できますが、この会話には不必要であり、水を濁らせるだけです。
Tom.Bowen89

17

バックグラウンド

継承は、オブジェクト指向プログラミングの目的を果たす強力なツールです。ただし、すべての問題をエレガントに解決できるわけではありません。他のソリューションの方が優れている場合もあります。

初期のコンピューターサイエンスのクラス(CS学位を持っていると仮定)を振り返ると、顧客がソフトウェアに何をして欲しいかを述べた段落を与えた教授を覚えているかもしれません。あなたの仕事は、段落を読み、アクターとアクションを特定し、クラスとメソッドが何であるかの大まかな概要を理解することです。そこには、重要であるように見えるが重要ではないいくつかのバムリードがあります。要件を誤って解釈する可能性も非常にあります。

これは、経験豊富な私たちでさえ間違っている重要なスキルです。要件を適切に特定し、それらを機械語に翻訳します。

あなたの質問

あなたが書いたことに基づいて、クラスが実行できるオプションのアクションの一般的なケースを誤解していると思います。はい、あなたのコードは単なる例であり、一般的なケースに興味があることを知っています。ただし、オブジェクトの特定のサブタイプはアクションを実行できますが、他のサブタイプはできないという状況を処理する方法を知りたいようです。

などのオブジェクトにaccountアカウントタイプがあるからといって、それがオブジェクト指向言語のタイプに変換されることを意味するわけではありません。人間の言語の「タイプ」は、必ずしも「クラス」を意味するとは限りません。アカウントのコンテキストでは、「タイプ」は「許可セット」とより密接に相関する場合があります。ユーザーアカウントを使用してアクションを実行したいが、そのアクションはそのアカウントで実行できる場合とできない場合があります。継承を使用するのではなく、デリゲートまたはセキュリティトークンを使用します。

私のソリューション

実行できるオプションのアクションをいくつか持つアカウントクラスを考えます。継承を介して「アクションXを実行できます」を定義する代わりに、アカウントがデリゲートオブジェクト(パスワードリセッタ、フォーム送信者など)またはアクセストークンを返さないようにしましょう。

account.getPasswordResetter().doAction();
account.getFormSubmitter().doAction(view.getContents());

AccountManager.resetPassword(account, account.getAccessToken());

最後のオプションの利点はアカウントの資格情報を使用し他の人のパスワードをリセットしたい場合はどうなりますか?

AccountManager.resetPassword(otherAccount, adminAccount.getAccessToken());

システムがより柔軟になるだけでなく、型キャストを削除しただけでなく、デザインがより表現力豊かになりました。これを読んで、それが何をしていてどのように使用できるかを簡単に理解できます。


TL; DR:これはXY問題のようになります。一般に、次善の2つのオプションに直面した場合、一歩下がって「ここで本当に何を達成しようとしているのかを考えてみてください。タイプキャストを完全に削除しますか?」


1
フレーズを使用していなくても、デザインの匂いが+1されます。
ジャレッド・スミス

タイプによってパスワードをリセットする引数がまったく異なる場合はどうですか?ResetPassword(pswd)vs ResetPasswordToRandom()
TheCatWhisperer

1
@TheCatWhisperer最初の例は、異なるアクションである「パスワードの変更」です。2番目の例は、この回答で説明したものです。

なぜだaccount.getPasswordResetter().resetPassword();
AnoE

1
The benefit to the last option there is what if I want to use my account credentials to reset someone else's password?..簡単。他のアカウントのアカウントドメインオブジェクトを作成し、そのオブジェクトを使用します。静的クラスは単体テストに問題があります(定義上、メソッドは何らかのストレージにアクセスする必要があるため、そのクラスに触れるコードを簡単に単体テストすることはできなくなります)。他のインターフェイスを返すオプションは、多くの場合良いアイデアですが、根本的な問題を実際に処理しません(問題を他のインターフェイスに移動しただけです)。
Voo

1

ビル・ヴェナーは、あなたのアプローチは絶対に素晴らしいと言います。「instanceofを使用するタイミング」というタイトルのセクションにジャンプします。その特定の動作のみを実装する特定のタイプ/インターフェースにダウンキャストしているため、それを選択するのは絶対に正しいことです。

ただし、別のアプローチを使用する場合は、ヤクを剃る方法がたくさんあります。

ポリモーフィズムアプローチがあります。すべてのアカウントにパスワードがあるため、すべてのアカウントが少なくともパスワードのリセットを試みることができると主張できます。つまり、基本アカウントクラスにはresetPassword()、すべてのアカウントが独自の方法で実装するメソッドが必要です。問題は、機能が存在しない場合にそのメソッドがどのように動作するかです。

パスワードをリセットするかどうかに関係なく、voidを返し、静かに完了します。パスワードをリセットしない場合は、メッセージを内部的に出力する責任があります。最良のアイデアではありません。

リセットが成功したかどうかを示すブール値を返すことができました。そのブール値をオンにすると、パスワードのリセットに失敗したと関連付けることができます。

パスワードリセットの試行結果を示す文字列を返す可能性があります。文字列は、リセットが失敗した理由の詳細を提供し、出力される可能性があります。

それは、より詳細を伝え、以前のすべての戻り要素を結合するResetResultオブジェクトを返すことができます。

その機能を持たないアカウントをリセットしようとすると、voidを返し、代わりに例外をスローする可能性があります(通常のフロー制御に例外処理を使用することはさまざまな理由で悪い習慣です)。

canResetPassword()概念的には、それが記述されたときにクラスに組み込まれた静的な機能であるため、マッチングメソッドを持つことは、世界で最悪のものとは思えないかもしれません。ただし、機能が動的であり、canResetPassword()変更される可能性があることを示唆しているため、メソッドアプローチが悪い考えである理由の手がかりになります。他の場所で述べたように、許可を求めるのではなく伝えてください。

継承を介した合成はオプションになる可能性があります。最終passwordResetterフィールド(または同等のゲッター)と同等のクラスを使用して、呼び出す前にnullをチェックできます。許可を求めるように少し動作しますが、ファイナリティは、推測される動的な性質を回避する場合があります。

機能を独自のクラスに外部化すると考えられます。このクラスは、パラメーターとしてアカウントを取得し、それを実行することができます(例:resetter.reset(account)よくない習慣です)

この機能を備えた言語では、ミックスインまたはトレイトを使用する場合がありますが、それらの機能の存在を確認する可能性のある場所から始めます。


すべてのアカウントにパスワードがあるわけではありません(とにかくこの特定のシステムの観点から)。たとえば、アカウントはサードパーティである場合があります... google、facebook、ect。これは素晴らしい答えではないと言ってはいけません!
TheCatWhisperer

0

この具体的な例については、「パスワードをリセットすることができます」、私は相続オーバー構図使用してお勧めします(この場合は、インタフェース/契約の継承を)。なぜなら、これを行うことにより:

class Foo : IResetsPassword {
    //...
}

クラスがパスワードをリセットできることを(コンパイル時に)すぐに指定しています。ただし、シナリオで機能の存在が条件付きであり、他のものに依存する場合、コンパイル時にそれを指定することはできません。次に、これを行うことをお勧めします:

class Foo {

    PasswordResetter passwordResetter;

}

これで、実行時に、myFoo.passwordResetter != nullこの操作を行う前に確認できます。さらに多くのものを分離したい場合(さらに多くの機能を追加する予定の場合)、次のことができます。

class Foo {
    //... foo stuff
}

class PasswordResetOperation {
    bool Execute(Foo foo) { ... }
}

class SendMailOperation {
    bool Execute(Foo foo) { ... }
}

//...and you follow this pattern for each new capability...

更新

OPから他のいくつかの回答とコメントを読んだ後、私は質問が構成的なソリューションに関するものではないことを理解しました。だから私は、以下のようなシナリオで、一般的にオブジェクトの機能をよりよく識別する方法についての質問だと思います:

class BaseAccount {
    //...
}
class GuestAccount : BaseAccount {
    //...
}
class UserAccount : BaseAccount, IMyPasswordReset, IEditPosts {
    //...
}
class AdminAccount : BaseAccount, IPasswordReset, IEditPosts, ISendMail {
    //...
}

//Capabilities

interface IMyPasswordReset {
    bool ResetPassword();
}

interface IPasswordReset {
    bool ResetPassword(UserAccount userAcc);
}

interface IEditPosts {
    bool EditPost(long postId, ...);
}

interface ISendMail {
    bool SendMail(string from, string to, ...);
}

ここで、言及したすべてのオプションを分析してみます。

OPの2番目の例:

if (account.CanResetPassword)
       ((IResetsPassword)account).ResetPassword();
else
       Print("Not allowed to reset password with this account type!");

このコードがベースアカウントクラス(例:BaseAccount私の例では)を受け取っているとしましょう。これは、基底クラスにブール値を挿入し、そこに存在する意味がまったくないコードで汚染しているため、悪いです。

OPの最初の例:

if (account is IResetsPassword)
       ((IResetsPassword)account).ResetPassword();
else
       Print("Not allowed to reset password with this account type!");

質問に答えるには、これは前のオプションよりも適切ですが、実装によってはLの強固な原則を破り、おそらくこのようなチェックがコード全体に広がり、さらなるメンテナンスが難しくなります。

CandiedOrangeのanser:

account.ResetPassword(authority);

このResetPasswordメソッドがBaseAccountクラスに挿入されると、OPの2番目の例のように、不適切なコードで基本クラスが汚染されます

雪だるまの答え:

AccountManager.resetPassword(otherAccount, adminAccount.getAccessToken());

これは良い解決策ですが、機能は動的であると見なされます(時間とともに変化する可能性があります)。ただし、OPからいくつかのコメントを読んだ後、ここでの話はポリモーフィズムと静的に定義されたクラスに関するものだと思います(ただし、アカウントの例は動的なシナリオを直感的に指し示しています)。EG:このAccountManager例では、権限のチェックはDBへのクエリです。OPの質問では、チェックはオブジェクトのキャストの試みです。

私からの別の提案:

高レベルの分岐にはテンプレートメソッドパターンを使用します。上記のクラス階層はそのまま保持されます。基本クラスを汚染するキャストや不適切なプロパティ/メソッドを避けるために、オブジェクトに対してより適切なハンドラーのみを作成します。

//Template method
class BaseAccountOperation {

    BaseAccount account;

    void Execute() {

        //... some processing

        TryResetPassword();

        //... some processing

        TrySendMail();

        //... some processing
    }

    void TryResetPassword() {
        Print("Not allowed to reset password with this account type!");
    }

    void TrySendMail() {
        Print("Not allowed to reset password with this account type!");
    }
}

class UserAccountOperation : BaseAccountOperation {

    UserAccount userAccount;

    void TryResetPassword() {
        account.ResetPassword(...);
    }

}

class AdminAccountOperation : BaseAccountOperation {

    AdminAccount adminAccount;

    override void TryResetPassword() {
        account.ResetPassword(...);
    }

    void TrySendMail() {
        account.SendMail(...);
    }
}

辞書/ハッシュテーブルを使用して操作を適切なアカウントクラスにバインドしたり、拡張メソッドを使用して実行時操作を実行したり、dynamicキーワードを使用したり、最後のオプションとしてアカウントオブジェクトを操作に渡すために1つのキャストのみを使用したりできます(この場合、キャストの数は1つだけです(操作の開始時)。


1
常に「Execute()」メソッドを実装するだけで機能しますが、何もしない場合がありますか?それはブール値を返すので、私はそれがそれをしたかどうかを意味していると推測しています。これは、クライアントに戻ってそれをスロー:「...今私がすべき、(何らかの理由で)私がパスワードをリセットしようとしたが、それは実現しなかった」

4
「canX()」および「doX()」メソッドを持つことはアンチパターンです。インターフェースが「doX()」メソッドを公開する場合、Xが何もしないことを意味している場合でもXを実行できます)。インターフェースのユーザーが一時的なカップリング(メソッドBの前にメソッドAを呼び出す)に関与することは絶対に避けてください。

1
インターフェイスを持つことは、継承よりも合成を使用することに関係ありません。それらは利用可能な機能を表しているだけです。その機能がどのように機能するかは、インターフェースの使用とは無関係です。ここで行ったことは、利点のないインターフェイスのアドホック実装を使用することです。
宮本明

興味深いことに、トップ投票の答えは、基本的に上記の私のコメントが言っていることを示しています。幸運を感じる日もあります。

@nocomprende-はい、いくつかのコメントに従って回答を更新しました。フィードバックをありがとう:)
エマーソンCardoso

0

あなたが提示したオプションはどちらも良いオブジェクト指向ではありません。オブジェクトの型に関するif文を書いている場合、OOを間違っている可能性が高いです(例外があり、これは1つではありません)。

interface IAccount {
  bool CanResetPassword();

  void ResetPassword();

  // Other Account operations as needed
}

public class Resetable : IAccount {
  public bool CanResetPassword() {
    return true;
  }

  public void ResetPassword() {
    /* RESET PASSWORD */
  }
}

public class NotResetable : IAccount {
  public bool CanResetPassword() {
    return false;
  }

  public void ResetPassword() {
    Print("Not allowed to reset password with this account type!");}
  }

元のコードが行っていたことと一致するように、この例を修正しました。いくつかのコメントに基づいて、これがここでの「正しい」特定のコードであるかどうかに人々が夢中になっているようです。これはこの例のポイントではありません。ポリモーフィックのオーバーロード全体は、オブジェクトのタイプに基づいてロジックのさまざまな実装を条件付きで実行することです。両方の例であなたがしていることは、あなたの言語が機能としてあなたに与えるものを手で妨害することです。簡単に言えば、サブタイプを削除し、アカウントタイプのブールプロパティとしてリセットする機能を追加できます(サブタイプの他の機能は無視されます)。

設計のより広い視野がなければ、これが特定のシステムに適したソリューションであるかどうかを判断することは不可能です。それは簡単で、あなたがやっていることに対して機能するなら、誰かがResetPassword()を呼び出す前にCanResetPassword()をチェックし損ねない限り、あなたはおそらくそれについて再び考える必要はないでしょう。また、ブール値を返すか、サイレントに失敗することもあります(推奨されません)。それは本当に設計の詳細に依存します。


すべてのアカウントをリセットできるわけではありません。また、アカウントにはリセット可能な機能よりも多くの機能があるのでしょうか?
TheCatWhisperer

2
「すべてのアカウントをリセットできるわけではありません。」これが編集前に書かれたかどうかはわかりません。2番目のクラスはNotResetableです。「アカウントにはリセット可能な機能よりも多くの機能がある可能性があります」と私は予想しますが、手元の質問にとって重要ではないようです。
JimmyJames

1
これは、OO内で素晴らしい解決策を提供するため、正しい方向に進む解決策だと思います。インターフェースを分離するために、おそらくインターフェース名をIPasswordReseter(またはその効果のあるもの)に変更します。IAccountは、他の複数のインターフェイスをサポートするものとして宣言できます。これにより、ドメインオブジェクトの委任によって構成および使用される実装を持つクラスを使用するオプションが提供されます。@JimmyJames、マイナーニックピック、C#では、両方のクラスがIAccountを実装することを指定する必要があります。それ以外の場合、コードは少し奇妙に見えます;
宮本明

1
また、CanX()およびDoX()に関する他の回答の1つでSnowmanからのコメントを確認してください。常にではないアンチパターン、しかし、それは(UIの行動に、たとえば)を使用しているとして
宮本明

1
この答えはうまく始まります:これは悪いOOPです。残念ながら、ソリューションは型システム破壊するが例外をスローするため、同じくらい悪いです。良い解決策は異なって見えます:型に実装されていないメソッドを持たないでください。.NETフレームワークは、実際にはいくつかのタイプに対してあなたのアプローチを使用しますが、それは間違いなく間違いでした。
コンラッドルドルフ

0

回答

あなたの質問に対する私の少し意見を述べた答えは:

オブジェクトの機能は、インターフェースを実装することではなく、それ自体で識別する必要があります。ただし、静的に強く型付けされた言語は、この可能性を制限します(設計上)。

補遺:

オブジェクトタイプの分岐は悪です。

とりとめのない

c#タグを追加しなかったが、一般的なobject-orientedコンテキストで尋ねていることがわかります。

あなたが説明しているのは、静的/強い型付けされた言語の技術性であり、「OO」全体に特化したものではありません。あなたの問題は、とりわけ、コンパイル時に十分に狭い型を持たずにそれらの言語でメソッドを呼び出すことができないという事実に起因します(変数定義、メソッドパラメーター/戻り値定義、または明示的なキャストのいずれかを使用)。過去にこれらの種類の言語で両方のバリアントを作成しましたが、最近はどちらもいように見えます。

動的に型指定されたオブジェクト指向言語では、「ダックタイピング」と呼ばれる概念のため、この問題は発生しません。要するに、これは基本的にオブジェクトの型をどこでも宣言しないことを意味します(作成時を除く)。オブジェクトにメッセージを送信すると、オブジェクトはそれらのメッセージを処理します。オブジェクトが実際にその名前のメソッドを持っているかどうかは気にしません。一部の言語には、method_missingこれをさらに柔軟にする汎用の包括的メソッドさえあります。

したがって、そのような言語(Rubyなど)では、コードは次のようになります。

account.reset_password

期間。

accountどちらか、パスワードをリセット「拒否」例外をスロー、または「『reset_password』を処理する方法がわからない」例外がスローされます。

何らかの理由で明示的に分岐する必要がある場合、クラスではなく機能で分岐します。

account.reset_password  if account.can? :reset_password

can?オブジェクトを呼び出さずに、オブジェクトが何らかのメソッドを実行できるかどうかをチェックするメソッドだけがあります。)

私の個人的な好みは現在、動的に型付けされた言語を使用していますが、これは単なる個人的な好みです。静的に型付けされた言語にも同様のことがありますので、静的に型付けされた言語に対するバッシングとしてこのとりとめを受け取らないでください...


ここにあなたの視点があるのは素晴らしいことです!java / C#側では、OOPの別のブランチが存在することを忘れがちです。同僚に話すと、JS(すべての問題について)は、多くの点でC#よりもオブジェクト指向であり、彼らは私を笑っています!
TheCatWhisperer

私はC#が大好きで、それは優れた汎用言語だと思いますが、OOの観点からはかなり貧弱だという私の個人的な意見です。機能と実践の両方で、手続き型プログラミングを奨励する傾向があります。
TheCatWhisperer

2
おそらく、アヒル型言語でのメソッドの存在のテストは、静的型言語でのインターフェイスの実装のテストと同じです。オブジェクトに特定の機能があるかどうかの実行時チェックを実行しています。すべてのメソッド宣言は事実上、メソッド名によってのみ識別される独自のインターフェイスであり、その存在を使用してオブジェクトに関する他の情報を推測することはできません。
IMSoP

0

あなたのオプションは異なる動機付けの力から派生しているようです。1つ目は、オブジェクトモデリングの質が低いために採用されたハックのようです。ポリモーフィズムを壊すため、抽象化が間違っていることが実証されました。おそらくそれは、オープンな閉原理を破り、より緊密な結合をもたらします。

2番目のオプションはOOPの観点からは問題ないかもしれませんが、型キャストは私を混乱させます。アカウントでパスワードをリセットできる場合、なぜ型キャストが必要なのですか?したがって、CanResetPassword条項がアカウントの抽象化の一部である基本的なビジネス要件を表していると仮定すると、2番目のオプションは問題ありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.