为了正确实施SOLID设计,我需要更改什么?

问题描述 投票:2回答:1

我正在尝试学习SOLID设计,并认为自己犯了一个错误。我认为IItem类中的Player接口未遵循Liskov替换原理,但是,我无法解决该问题。如果我从IItem添加新的界面图形,则必须更改Player的方法以添加用于处理它的外壳。

我希望Player类仅需要一种装备方法,因此需要帮助来了解我做错了什么以及如何正确进行。

我的界面的简化版:

    interface IItem
    {
        string Name { get; set; }
        int Value { get; set; }
        Quality Quality { get; set; }
        EquipmentType Type { get; set; }
    }
    interface IWeapon : IItem
    {

    }
    interface IArmour : IItem
    {
        int Defence { get; set; }
        Slot Slot { get; set; }
    }

正在使用的Player类:

    class Player
    {
        private Dictionary<Slot, IArmour> armour = new Dictionary<Slot, IArmour>();
        private IWeapon weapon;

        public bool Equip(IItem item)
        {
            switch (item.Type)
            {
                case EquipmentType.Armour:
                    var armour = item as IArmour;
                    if (this.armour.ContainsKey(armour.Slot))
                    {
                        return false;
                    }
                    this.armour.Add(armour.Slot, armour);
                    return true;
                case EquipmentType.Weapon:
                    var weapon = item as IWeapon;
                    throw new NotImplementedException();
                default:
                    return false;
            }
        }
    }

枚举上下文:

    enum Slot
    {
        Head = 0,
        Soulders = 1,
        Gloves = 2,
        Neck = 3,
        RRing = 4,
        LRing = 5,
        Torso = 6,
        Legs = 7,
        Boots = 8,
        Bracers = 9,
        Belt = 10,
    }
    enum EquipmentType
    {
        Armour = 0,
        Weapon = 1
    }
c# .net-4.5
1个回答
2
投票

Liskov替换原理通常是关于如何定义类的。如果您编写一个派生自其他某个类的类(或实现某个接口),则想法是某人应该能够像使用该父类的实例一样使用您的类。您的子类可能具有父类所没有的其他行为(当然,使用您的类的人就像是父类的实例一样,将无法访问它),但是所有的行为父类应保留在子类中。

在您的示例中,这可能意味着定义不适合MagicalWholeBodyArmorSlot,因此,如果尝试访问其Slot属性,则会引发异常。试图将MagicalWholeBodyArmor视为IArmor的人会惊讶地发现他们适合插入哪个插槽。

您编写的SOLID规则确实违反了一点[。打开/关闭原则的一个好的经验法则是:“如果我更改了这部分代码,那么我还必须在其他地方更改多少其他代码?”。如果答案是“很多”,那可能是因为您违反了开放/封闭原则。

在您的情况下,添加一个新的EquipmentType枚举成员意味着您将不得不在Player类中找到该switch语句并添加一个新的大小写。

如果只有一个switch语句,那还不错。如果添加了新类型的设备,则您的Player类可能需要升级

anyway,因此可以修改switch语句作为其中的一部分。尝试以这种方式设计您的方法将意味着大量的抽象而几乎没有收益。

但是,如果您在许多不同的地方有很多switch语句,它们都看EquipmentType,(并据此做出不同的决定),那么您需要全部找到并修复它们,那就更大程度地违反了开放/封闭原则,这可能表明您需要重新架构事物以将所有这些单独的,完全不同的逻辑放在一个地方。
© www.soinside.com 2019 - 2024. All rights reserved.