代码之家  ›  专栏  ›  技术社区  ›  xofz

对象与对象混淆

  •  3
  • xofz  · 技术社区  · 17 年前

    假设我对门有一个定义:

    class Door
    {
        public void Lock()
        {
            // lock the door
        }
    }
    

    这对我来说似乎很有意义,至少有一段时间是这样。但现在,我不太确定。如果我有个人对象想要锁门,他会调用a door.lock()。但在现实生活中,我们并不是通过告诉门自己上锁来锁门的。

    如果一个人有足够的能力锁门,他就可以直接修改阿多的状态,这似乎是一个更准确的情况模型。例如,acat不应该能够设置adoor.islocked=true。如果属性支持以下参数,我可以了解如何使用这些属性:

    class Person
    {
        public void LockDoor(Door door)
        {
            door.IsLocked(this) = true;
        }
    }
    
    class Door
    {
        bool isLocked;
    
        public bool IsLocked(Person person)
        {
            set
            {
                if(person != null) // ensure there is a real person trying to lock the door
                {
                    this.isLocked = value;
                }
            }
        }
    }
    
    static void Main()
    {
        Person personFromThinAir = new Person();
        Door doorFromThinAir = new Door();
        personFromThinAir.LockDoor(doorFromThinAir);
    }
    

    相反,我们可以做的是:

    class Person
    {
        public void LockDoor(Door door)
        {
            door.SetLocked(this, true);
        }
    }
    
    class Door
    {
        bool isLocked;
    
        public void SetLocked(Person person, bool locked)
        {
            if(person != null)
            {
                this.isLocked = locked;
            }
        }
    }
    

    显然,这两个类是强耦合的,两个类都可能在实际的代码中提取接口,但这不是我想要的。我的问题是,这是模拟两个对象之间关系的更好方法吗?还有比这更好的方法吗?我想得越多,我对adoor.lock()的理解就越少;它似乎违反了面向对象的设计。

    3 回复  |  直到 17 年前
        1
  •  9
  •   aperkins    17 年前

    尽管这个人“锁”了门,但实际上,这个人是在门的一个部件(锁把手)上切换(或起泡),这种操作会导致锁锁住门。你可以想到这里,尽管有人在移动门闩,但门闩是锁上门的东西,而不是锁上门的人。所以更好的表示可能是一扇门有一个锁,而这个人调用lock.lock(),然后将锁设置为关闭(锁定)。

    这里的基本前提是,尽管用户正在操作锁,但这是外部的(函数调用)。锁的内部变化(功能内部的代码)实际上是导致门锁定的原因。这个人不是每次都拿下门把手,操纵门的内部来锁门——他们只是简单地在外部切换一个状态,并期望内部的机器来处理它。

        2
  •  6
  •   Kenneth Cochran    17 年前

    OOP并不是在模拟“现实世界”中事物的工作方式。更重要的是管理复杂性。考虑到这一点,门锁本身是完全可以接受的。即使在现实世界中,锁门的人也不需要知道锁门是如何工作的,除了转动门把手或钥匙。

    把复杂概念的细节隐藏在抽象的背后是OOP如此有用的原因。您使用的抽象与问题域不同。在您给出的示例中,除了如何操作门之外,该人员不需要了解任何有关门的信息:

    class Door
    {
        public bool Open(){}
        public bool Close(){}
        public void Lock(){}
        public void Unlock(){}
    }
    
        3
  •  0
  •   Anon    17 年前

    对我来说,最有趣的设计问题是如何处理储物柜和受锁人之间的耦合,因为必须满足允许锁定/解锁的要求。我看着这个问题,想象一个游戏,玩家有时是人类,但有时是猫(根据给出的例子),也许 is_human 是锁定/解锁的唯一要求。但你也可能想要有门,需要匹配的钥匙在玩家的位置,以便锁定/解锁。如果是这样,您必须将其添加到标准中。也许有些门只能从一侧锁定,而不能从另一侧锁定,所以必须将玩家的位置添加到标准中。你可以进一步增加一些玩家可能拥有的开锁技能(毫无疑问,是猫贼),让他们有机会打开(但不是锁)一扇门,即使他们没有钥匙。等。

    人们可以想象在对象之间的对话,比如:

    Player: "I am trying to unlock you."
    Lock: "Do you meet requirement A?"
    Player: "Yes"
    Lock: "Do you meet requirement B?"  // Only some doors would ask this.
    Player: "Yes"
    Lock: "OK, you succeed.  I am unlocked!"
    

    但是,您可能不想公开所涉及的字段,也不想让不需要知道锁定/解锁要求的对象看到的播放器接口混乱。

    我不是一个C程序员,自从我做Java以来已经有一段时间了,但是我认为Java中的一种方法也可以应用在C语言中,让玩家对象通过一个 lock_unlock_credentials 作为参数的内部类 get_locked / get_unlocked 门对象的方法(或建议的锁定对象)。 锁定解锁凭证 对象将具有回调方法,由于该方法是播放机的内部类,因此可以访问播放机对象的相关字段,但否则这些字段将不会在播放机外部公开。然后,可锁定对象可以使用这些回调方法来检查它关心的需求是否得到满足。您不能避免由需求引起的耦合,但是这样可以将细节保持在播放器和可锁定对象之间交互的内部。

    不确定相同的内部类方法是否适用于C,但将其表示为需要考虑的内容。