我需要更改哪些内容才能正确实施 SOLID 设计?

Posted

技术标签:

【中文标题】我需要更改哪些内容才能正确实施 SOLID 设计?【英文标题】:What do I need to change to implement SOLID design correctly? 【发布时间】:2019-10-20 11:09:02 【问题描述】:

我正在尝试学习 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
    

【问题讨论】:

是什么让你认为你在这里破坏了 LSP? @canton7 如果我以后从 IItem 添加一个新的派生接口,我将不得不更改播放器,据我了解这意味着它是不可替代的? 这里看起来更像是打开/关闭违规(SOLID 的“O”),而不是 Liskov 替换问题。如果您需要进行更改,则意味着您的代码没有关闭进行修改。 @Corentin Pane,好点子!你能给我什么建议让我回到正轨吗?我还在努力学习:) 如果你有一个派生自例如的类,你会破坏 LSP。 IArmor(我们称它为Shield),如果您使用IArmor 引用它,则该类的行为与使用Shield 引用它时的行为不同。如果有的话,这违反了打开/关闭原则,因为如果您添加另一种类型的设备,您需要修改您的 Player 类以正确处理它(以及使用 EquipmentType 的所有其他地方)。但是我不会在这里担心 - 如果您添加一种新型设备,您的 Player 很可能需要一些升级来应对 【参考方案1】:

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

在您的示例中,这可能意味着定义不适合 SlotMagicalWholeBodyArmor,因此如果您尝试访问其 Slot 属性,则会引发异常。将MagicalWholeBodyArmor 视为IArmor 的人在尝试查看它适合哪个插槽时会大吃一惊。

您编写的代码确实违反了 SOLID 规则,即 开放/封闭原则。 Open/Closed 原则的一个很好的经验法则是“如果我更改这段代码,我还必须更改其他地方的多少其他代码位?”。如果答案是“很多”,那可能是因为您违反了开放/封闭原则。

在您的情况下,添加一个新的 EquipmentType 枚举成员意味着您必须在 Player 类中找到该 switch 语句并添加一个新的情况。

如果只有一个 switch 语句,那还不错。如果您添加了一种新型设备,那么您的Player 类可能需要升级无论如何,因此修改 switch 语句作为其中的一部分是可以的。尝试以自己的方式解决这个问题将意味着大量的抽象,而收益却很少。

但是,如果您在许多不同的地方有很多很多 switch 语句,它们都在查看 EquipmentType,(并根据它做出不同的决定),那么您需要找到它们并全部修复它们,那么这就更严重地违反了开放/封闭原则,这可能表明您需要重新架构事物以将所有这些独立的、不同的逻辑位集中到一个地方。

【讨论】:

以上是关于我需要更改哪些内容才能正确实施 SOLID 设计?的主要内容,如果未能解决你的问题,请参考以下文章

佰新视觉UI设计的实施需要注意哪些问题

我需要更改哪些内容才能从 SEDE 获取已删除、锁定的帖子的计数?

此 package.json 文件需要更改哪些内容才能使用 npm 0.3.0?

必须在客户端或服务器端更改哪些内容才能使 getJSON() 工作?

如何设计遵循 SOLID 的类而不在其他地方加载违反 SOLID 的内容?

我需要进行哪些更改才能执行上界的反转?