สำหรับตัวอย่างที่เฉพาะเจาะจงของ " สามารถรีเซ็ตรหัสผ่าน " ฉันขอแนะนำให้ใช้การแต่งเพลงมากกว่าการสืบทอด (ในกรณีนี้การสืบทอดของอินเทอร์เฟซ / สัญญา) เพราะโดยทำสิ่งนี้:
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...
UPDATE
หลังจากที่ฉันอ่านคำตอบและความคิดเห็นอื่น ๆ จาก 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 ของของแข็งและอาจตรวจสอบเช่นนี้จะแพร่กระจายผ่านรหัสและทำให้การบำรุงรักษาเพิ่มเติมยากขึ้น
แอนเซอร์ของ CandiedOrange:
account.ResetPassword(authority);
หากResetPassword
วิธีนี้ถูกแทรกในBaseAccount
ชั้นเรียนแล้วมันยังก่อมลพิษชั้นฐานด้วยรหัสที่ไม่เหมาะสมเช่นในตัวอย่างที่สองของ OP
คำตอบของ Snowman:
AccountManager.resetPassword(otherAccount, adminAccount.getAccessToken());
นี่เป็นวิธีแก้ปัญหาที่ดี แต่ก็ถือว่าความสามารถนั้นเป็นแบบไดนามิก (และอาจเปลี่ยนแปลงตลอดเวลา) อย่างไรก็ตามหลังจากที่ฉันอ่านความคิดเห็นต่าง ๆ จาก OP ฉันเดาว่าการพูดคุยที่นี่เกี่ยวข้องกับ polymorphism และคลาสที่กำหนดไว้แบบสแตติก 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(...);
}
}
คุณสามารถผูกการดำเนินการกับคลาสบัญชีที่เหมาะสมโดยใช้พจนานุกรม / hashtable หรือทำการดำเนินการแบบรันไทม์โดยใช้วิธีการขยายใช้dynamic
คำสำคัญหรือเป็นตัวเลือกสุดท้ายใช้เพี้ยนเพียงครั้งเดียวเพื่อส่งวัตถุบัญชีไปยังการดำเนินการ (ใน กรณีนี้จำนวนการปลดเปลื้องจะมีเพียงครั้งเดียวเท่านั้นในตอนเริ่มต้นของการปฏิบัติการ)