Pour cet exemple spécifique du " peut réinitialiser un mot de passe ", je vous recommande d'utiliser la composition plutôt que l'héritage (dans ce cas, l'héritage d'une interface / contrat). Parce que, en faisant cela:
class Foo : IResetsPassword {
//...
}
Vous spécifiez immédiatement (au moment de la compilation) que votre classe " peut réinitialiser un mot de passe ". Mais, si dans votre scénario, la présence de la capacité est conditionnelle et dépend d'autres éléments, vous ne pouvez plus spécifier d'éléments au moment de la compilation. Ensuite, je suggère de faire ceci:
class Foo {
PasswordResetter passwordResetter;
}
Maintenant, au moment de l'exécution, vous pouvez vérifier si myFoo.passwordResetter != null
avant de faire cette opération. Si vous souhaitez découpler encore plus de choses (et que vous envisagez d'ajouter beaucoup plus de fonctionnalités), vous pouvez:
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...
MISE À JOUR
Après avoir lu d’autres réponses et commentaires de OP, j’ai bien compris que la question ne concernait pas la solution compositionnelle. Je pense donc que la question est de savoir comment mieux identifier les capacités des objets en général, dans un scénario comme celui-ci:
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, ...);
}
Maintenant, je vais essayer d'analyser toutes les options mentionnées:
Deuxième exemple d'OP:
if (account.CanResetPassword)
((IResetsPassword)account).ResetPassword();
else
Print("Not allowed to reset password with this account type!");
Disons que ce code reçoit une classe de compte de base (par exemple: BaseAccount
dans mon exemple); c'est dommage car cela insère des booléens dans la classe de base, la polluant avec un code qui n'a aucun sens d'être là.
OP premier exemple:
if (account is IResetsPassword)
((IResetsPassword)account).ResetPassword();
else
Print("Not allowed to reset password with this account type!");
Pour répondre à la question, cette option est plus appropriée que l’option précédente, mais en fonction de l’implémentation, elle enfreindra le principe de solide, et des vérifications telles que celles-ci seraient réparties dans le code et rendraient la maintenance ultérieure plus difficile.
Ansie de CandiedOrange:
account.ResetPassword(authority);
Si cette ResetPassword
méthode est insérée en BaseAccount
classe, alors elle pollue également la classe de base avec un code inapproprié, comme dans le deuxième exemple de OP
Réponse de bonhomme de neige:
AccountManager.resetPassword(otherAccount, adminAccount.getAccessToken());
C'est une bonne solution, mais elle considère que les fonctionnalités sont dynamiques (et peuvent évoluer dans le temps). Cependant, après avoir lu plusieurs commentaires de OP, je suppose que nous parlons ici de polymorphisme et de classes statiquement définies (bien que l’exemple de comptes indique intuitivement le scénario dynamique). EG: dans cet AccountManager
exemple, la vérification de l'autorisation serait une requête à la base de données; dans la question OP, les vérifications sont des tentatives de lancer les objets.
Une autre suggestion de moi:
Utilisez le modèle de méthode de modèle pour les ramifications de haut niveau. La hiérarchie de classe mentionnée est conservée telle quelle; nous ne créons que des gestionnaires plus appropriés pour les objets, afin d'éviter les conversions et les propriétés / méthodes inappropriées qui polluent la classe de base.
//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(...);
}
}
Vous pouvez lier l'opération à la classe de compte approprié à l'aide d' un dictionnaire / Hashtable, ou effectuer des opérations en temps exécuter à l' aide des méthodes d'extension, l' utilisation de dynamic
mots clés, ou comme la dernière utilisation de l' option une seule coulée afin de passer l'objet de compte à l'opération (en dans ce cas, le nombre de lancers est un seul, au début de l’opération).