ในบทความบล็อกชุดนี้ Eric Lippert อธิบายถึงปัญหาในการออกแบบเชิงวัตถุโดยใช้พ่อมดและนักรบเป็นตัวอย่างโดยที่:
abstract class Weapon { }
sealed class Staff : Weapon { }
sealed class Sword : Weapon { }
abstract class Player
{
public Weapon Weapon { get; set; }
}
sealed class Wizard : Player { }
sealed class Warrior : Player { }
แล้วเพิ่มกฎสองสามข้อ:
- นักรบสามารถใช้ดาบได้เท่านั้น
- ตัวช่วยสร้างสามารถใช้พนักงานได้เท่านั้น
จากนั้นเขาก็จะแสดงให้เห็นถึงปัญหาที่คุณพบหากคุณพยายามบังคับใช้กฎเหล่านี้โดยใช้ระบบประเภท C # (เช่นทำให้Wizard
ชั้นเรียนมีความรับผิดชอบในการทำให้แน่ใจว่าตัวช่วยสร้างสามารถใช้พนักงานได้เท่านั้น) คุณละเมิดหลักการทดแทน Liskov, ความเสี่ยงข้อยกเว้นรันไทม์หรือจบลงด้วยรหัสที่ยากต่อการขยาย
วิธีแก้ปัญหาที่เขาเกิดขึ้นคือไม่มีการตรวจสอบความถูกต้องโดยคลาสผู้เล่น มันใช้เพื่อติดตามสถานะเท่านั้น จากนั้นแทนที่จะให้อาวุธแก่ผู้เล่นโดย:
player.Weapon = new Sword();
สถานะถูกแก้ไขโดยCommand
s และตามRule
s:
... เราทำให้
Command
วัตถุที่เรียกWield
ว่าใช้เวลาสองวัตถุเกมรัฐเป็นและPlayer
Weapon
เมื่อผู้ใช้ออกคำสั่งไปยังระบบ“ ตัวช่วยสร้างนี้ควรควงดาบนั้น” จากนั้นคำสั่งนั้นจะถูกประเมินในบริบทของชุดของRule
s ซึ่งสร้างลำดับของEffect
s เรามีสิ่งหนึ่งRule
ที่บอกว่าเมื่อผู้เล่นพยายามควงอาวุธผลที่ได้คืออาวุธที่มีอยู่หากมีหนึ่งถูกทิ้งและอาวุธใหม่กลายเป็นอาวุธของผู้เล่น เรามีกฎอีกข้อหนึ่งที่เสริมความแข็งแกร่งให้กับกฎข้อแรกซึ่งระบุว่าเอฟเฟกต์ของกฎข้อแรกจะไม่นำมาใช้เมื่อวิซาร์ดพยายามใช้ดาบ
ฉันชอบแนวคิดนี้ในหลักการ แต่มีข้อกังวลเกี่ยวกับวิธีการใช้ในทางปฏิบัติ
ไม่มีอะไรน่าจะป้องกันไม่ให้นักพัฒนาจากหลีกเลี่ยงCommands
และRule
s โดยเพียงแค่การตั้งค่าบนWeapon
คุณสมบัติความต้องการที่จะเข้าถึงได้โดยคำสั่งดังนั้นจึงไม่สามารถที่จะทำPlayer
Weapon
Wield
private set
ดังนั้นสิ่งที่ทำให้นักพัฒนาไม่สามารถทำสิ่งนี้ได้? พวกเขาแค่จำไม่ได้ใช่ไหม