Đối với ví dụ cụ thể này về " có thể đặt lại mật khẩu ", tôi khuyên bạn nên sử dụng thành phần trên kế thừa (trong trường hợp này là kế thừa giao diện / hợp đồng). Bởi vì, bằng cách này:
class Foo : IResetsPassword {
//...
}
Bạn ngay lập tức xác định (tại thời gian biên dịch) rằng lớp của bạn ' có thể đặt lại mật khẩu '. Nhưng, nếu trong kịch bản của bạn, sự hiện diện của khả năng là có điều kiện và phụ thuộc vào những thứ khác, thì bạn không thể chỉ định những thứ đó vào thời gian biên dịch nữa. Sau đó, tôi đề nghị làm điều này:
class Foo {
PasswordResetter passwordResetter;
}
Bây giờ, tại thời gian chạy, bạn có thể kiểm tra nếu myFoo.passwordResetter != null
trước khi thực hiện thao tác này. Nếu bạn muốn tách rời nhiều thứ hơn nữa (và bạn dự định thêm nhiều khả năng khác), bạn có thể:
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...
CẬP NHẬT
Sau khi tôi đọc một số câu trả lời và nhận xét khác từ OP, tôi hiểu câu hỏi không phải là về giải pháp sáng tác. Vì vậy, tôi nghĩ rằng câu hỏi là về cách xác định tốt hơn khả năng của các đối tượng nói chung, trong một kịch bản như dưới đây:
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, ...);
}
Bây giờ, tôi sẽ cố gắng phân tích tất cả các tùy chọn được đề cập:
Ví dụ thứ hai của OP:
if (account.CanResetPassword)
((IResetsPassword)account).ResetPassword();
else
Print("Not allowed to reset password with this account type!");
Giả sử mã này đang nhận một số lớp tài khoản cơ sở (ví dụ: BaseAccount
trong ví dụ của tôi); Điều này thật tệ vì nó chèn booleans vào lớp cơ sở, làm ô nhiễm nó với mã không có ý nghĩa gì cả.
Ví dụ đầu tiên của OP:
if (account is IResetsPassword)
((IResetsPassword)account).ResetPassword();
else
Print("Not allowed to reset password with this account type!");
Để trả lời câu hỏi, điều này phù hợp hơn so với tùy chọn trước đó, nhưng tùy thuộc vào việc thực hiện, nó sẽ phá vỡ nguyên tắc rắn L và có thể các kiểm tra như thế này sẽ được truyền qua mã và khiến việc bảo trì thêm khó khăn hơn.
Anieder của CandiedOrange:
account.ResetPassword(authority);
Nếu ResetPassword
phương thức này được chèn vào BaseAccount
lớp, thì nó cũng gây ô nhiễm lớp cơ sở với mã không phù hợp, như trong ví dụ thứ hai của OP
Câu trả lời của Người tuyết:
AccountManager.resetPassword(otherAccount, adminAccount.getAccessToken());
Đây là một giải pháp tốt, nhưng nó cho rằng các khả năng là động (và có thể thay đổi theo thời gian). Tuy nhiên, sau khi tôi đọc một số bình luận từ OP, tôi đoán cuộc nói chuyện ở đây liên quan đến tính đa hình và các lớp được xác định tĩnh (mặc dù ví dụ về các tài khoản trực giác chỉ ra kịch bản động). EG: trong AccountManager
ví dụ này , kiểm tra cho phép sẽ là truy vấn DB; trong câu hỏi OP, kiểm tra là cố gắng đúc các đối tượng.
Một đề nghị khác từ tôi:
Sử dụng mẫu Phương thức mẫu để phân nhánh cấp cao. Hệ thống phân cấp lớp được đề cập được giữ nguyên như vậy; chúng ta chỉ tạo các trình xử lý thích hợp hơn cho các đối tượng, để tránh các phôi và các thuộc tính / phương thức không phù hợp làm biến đổi lớp cơ sở.
//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(...);
}
}
Bạn có thể liên kết hoạt động với lớp tài khoản phù hợp bằng cách sử dụng từ điển / hashtable hoặc thực hiện các hoạt động trong thời gian chạy bằng phương pháp tiện ích mở rộng, sử dụng dynamic
từ khóa hoặc tùy chọn cuối cùng chỉ sử dụng một lần truyền để chuyển đối tượng tài khoản sang hoạt động (trong trường hợp này số lượng phôi chỉ là một, khi bắt đầu hoạt động).