对于“ 可以重设密码 ”的此特定示例,我建议使用继承而非继承(在这种情况下,接口/合同的继承)。因为,通过这样做:
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第二个例子:
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的solid原理,并且可能像这样的检查会在代码中传播,并使进一步的维护更加困难。
CandiedOrange的分析服务:
account.ResetPassword(authority);
如果将此ResetPassword
方法插入到BaseAccount
类中,那么它还会使用不适当的代码污染基类,如OP的第二个示例
雪人的答案:
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)。