Für dieses spezielle Beispiel für " kann ein Kennwort zurückgesetzt werden " würde ich die Verwendung der Komposition anstelle der Vererbung empfehlen (in diesem Fall die Vererbung einer Schnittstelle / eines Vertrags). Denn auf diese Weise:
class Foo : IResetsPassword {
//...
}
Sie geben sofort (zur Kompilierungszeit) an, dass Ihre Klasse ein Kennwort zurücksetzen kann . Wenn jedoch in Ihrem Szenario die Anwesenheit der Funktion von anderen Faktoren abhängt, können Sie diese beim Kompilieren nicht mehr angeben. Dann schlage ich vor:
class Foo {
PasswordResetter passwordResetter;
}
Jetzt können Sie zur Laufzeit prüfen, ob myFoo.passwordResetter != null
dies der Fall ist, bevor Sie diesen Vorgang ausführen. Wenn Sie noch mehr Dinge entkoppeln möchten (und viel mehr Funktionen hinzufügen möchten), können Sie:
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...
AKTUALISIEREN
Nachdem ich einige andere Antworten und Kommentare von OP gelesen hatte, verstand ich, dass es sich bei der Frage nicht um die kompositorische Lösung handelt. Ich denke, die Frage ist, wie die Fähigkeiten von Objekten im Allgemeinen in einem Szenario wie dem Folgenden besser identifiziert werden können:
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, ...);
}
Nun werde ich versuchen, alle genannten Optionen zu analysieren:
OP zweites Beispiel:
if (account.CanResetPassword)
((IResetsPassword)account).ResetPassword();
else
Print("Not allowed to reset password with this account type!");
Angenommen, dieser Code erhält eine Basis-Account-Klasse (z. B .: BaseAccount
in meinem Beispiel). Das ist schlecht, da es Boolesche Werte in die Basisklasse einfügt und sie mit Code verschmutzt, der überhaupt keinen Sinn ergibt, dort zu sein.
OP erstes Beispiel:
if (account is IResetsPassword)
((IResetsPassword)account).ResetPassword();
else
Print("Not allowed to reset password with this account type!");
Zur Beantwortung der Frage ist dies angemessener als die vorherige Option, aber je nach Implementierung wird das L-Prinzip des Solid gebrochen, und Überprüfungen wie diese würden wahrscheinlich über den Code verbreitet und die weitere Wartung erschweren.
CandiedOrange's Antwort:
account.ResetPassword(authority);
Wenn diese ResetPassword
Methode in eine BaseAccount
Klasse eingefügt wird , wird auch die Basisklasse mit unangemessenem Code belastet, wie im zweiten Beispiel von OP
Schneemanns Antwort:
AccountManager.resetPassword(otherAccount, adminAccount.getAccessToken());
Dies ist eine gute Lösung, berücksichtigt jedoch, dass die Funktionen dynamisch sind (und sich im Laufe der Zeit ändern können). Nachdem ich jedoch einige Kommentare von OP gelesen habe, denke ich, dass es hier um Polymorphismus und statisch definierte Klassen geht (obwohl das Beispiel von Konten intuitiv auf das dynamische Szenario verweist). EG: in diesem AccountManager
Beispiel wäre die Prüfung auf Erlaubnis eine Abfrage an DB; In der OP-Frage handelt es sich bei den Prüfungen um Versuche, die Objekte zu werfen.
Ein weiterer Vorschlag von mir:
Verwenden Sie das Muster der Vorlagenmethode für Verzweigungen auf hoher Ebene. Die erwähnte Klassenhierarchie bleibt unverändert; Wir erstellen nur geeignetere Handler für die Objekte, um Umwandlungen und unangemessene Eigenschaften / Methoden zu vermeiden, die die Basisklasse belasten.
//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(...);
}
}
Sie können den Vorgang mithilfe eines Wörterbuchs / einer Hashtabelle an die entsprechende Kontoklasse binden oder Laufzeitvorgänge mithilfe von Erweiterungsmethoden ausführen, ein dynamic
Schlüsselwort verwenden oder als letzte Option nur eine Besetzung verwenden, um das Kontoobjekt an den Vorgang (in In diesem Fall ist die Anzahl der Würfe zu Beginn der Operation nur eins.