Untuk contoh khusus " dapat mengatur ulang kata sandi " ini, saya sarankan menggunakan komposisi di atas warisan (dalam hal ini, warisan antarmuka / kontrak). Karena, dengan melakukan ini:
class Foo : IResetsPassword {
//...
}
Anda segera menentukan (pada waktu kompilasi) bahwa kelas Anda ' dapat mengatur ulang kata sandi '. Tetapi, jika dalam skenario Anda, keberadaan kapabilitas bersyarat dan tergantung pada hal-hal lain, maka Anda tidak dapat menentukan hal-hal pada waktu kompilasi lagi. Kemudian, saya sarankan melakukan ini:
class Foo {
PasswordResetter passwordResetter;
}
Sekarang, saat runtime, Anda dapat memeriksa apakah myFoo.passwordResetter != nullsebelum melakukan operasi ini. Jika Anda ingin memisahkan barang lebih banyak (dan Anda berencana untuk menambahkan lebih banyak kemampuan), Anda dapat:
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...
MEMPERBARUI
Setelah saya membaca beberapa jawaban dan komentar dari OP saya mengerti pertanyaannya bukan tentang solusi komposisional. Jadi saya pikir pertanyaannya adalah tentang bagaimana mengidentifikasi kemampuan objek secara lebih baik, dalam skenario seperti di bawah ini:
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, ...);
}
Sekarang, saya akan mencoba menganalisis semua opsi yang disebutkan:
OP contoh kedua:
if (account.CanResetPassword)
((IResetsPassword)account).ResetPassword();
else
Print("Not allowed to reset password with this account type!");
Katakanlah kode ini menerima beberapa kelas akun dasar (misalnya: BaseAccountdalam contoh saya); ini buruk karena memasukkan boolean di kelas dasar, mencemarinya dengan kode yang tidak masuk akal untuk berada di sana.
OP contoh pertama:
if (account is IResetsPassword)
((IResetsPassword)account).ResetPassword();
else
Print("Not allowed to reset password with this account type!");
Untuk menjawab pertanyaan, ini lebih tepat daripada opsi sebelumnya, tetapi tergantung pada implementasinya akan melanggar prinsip L solid, dan mungkin memeriksa seperti ini akan menyebar melalui kode dan membuat pemeliharaan lebih lanjut lebih sulit.
Pelamar CandiedOrange:
account.ResetPassword(authority);
Jika ResetPasswordmetode ini dimasukkan dalam BaseAccountkelas, maka itu juga mencemari kelas dasar dengan kode yang tidak sesuai, seperti pada contoh kedua OP
Jawaban Snowman:
AccountManager.resetPassword(otherAccount, adminAccount.getAccessToken());
Ini adalah solusi yang baik, tetapi mempertimbangkan bahwa kemampuannya dinamis (dan mungkin berubah seiring waktu). Namun, setelah saya membaca beberapa komentar dari OP, saya kira pembicaraan di sini adalah mengenai polimorfisme dan kelas-kelas yang ditentukan secara statis (walaupun contoh akun secara intuitif menunjukkan skenario dinamis). EG: dalam AccountManagercontoh ini pemeriksaan untuk izin adalah permintaan ke DB; dalam pertanyaan OP pemeriksaan adalah upaya casting benda.
Saran lain dari saya:
Gunakan pola Metode Templat untuk percabangan tingkat tinggi. Hirarki kelas yang disebutkan tetap seperti itu; kami hanya membuat penangan yang lebih tepat untuk objek, untuk menghindari gips dan properti / metode yang tidak sesuai, membuat polusi pada kelas dasar.
//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(...);
}
}
Anda dapat mengikat operasi ke kelas akun yang sesuai dengan menggunakan kamus / hashtable, atau melakukan operasi run-time menggunakan metode ekstensi, menggunakan dynamickata kunci, atau sebagai opsi terakhir gunakan hanya satu pemain untuk meneruskan objek akun ke operasi (di kasus ini jumlah gips hanya satu, di awal operasi).