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