OOD和主题 - 对象混淆

时间:2009-07-27 16:17:57

标签: c# oop

假设我有一个门的定义:

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

这似乎对我有意义,至少在一段时间内。但现在,我不太确定。如果我有一个想要锁门的Person对象,他会调用aDoor.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 个答案:

答案 0 :(得分:9)

虽然该人“锁定”了门,但实际上该人正在切换(或擦拭)门的元件(锁定手柄)并且该操纵导致锁锁定门。你可以想到这一点,虽然这个人正在移动锁舌,但是锁舌是锁门的 - 而不是人。因此,一个更好的表示可能是门有一个锁,并且该人调用lock.lock(),然后设置锁被关闭(锁定)。

这里的基本前提是,虽然这个人正在操纵锁,但这是外部的(函数调用)。锁的内部更改(函数内部的代码)实际上导致门锁定。这个人每次都没有脱下手柄并操纵内部来锁门 - 他们只是在外面切换一个状态并期望机器内部处理它。

答案 1 :(得分:6)

OOP并不是关于在“现实世界”中如何工作的建模。它更多的是管理复杂性。考虑到这一点,门完全锁定是完全可以接受的。即使在现实世界中,除了转动旋钮或钥匙之外,锁门的人也不需要了解锁的工作原理。

隐藏抽象背后的复杂想法的细节是OOP如此有用的原因。您使用的抽象与问题域不同。在你给出的例子中,除了如何操作之外,人们不应该知道关于门的任何事情:

class Door
{
    public bool Open(){}
    public bool Close(){}
    public void Lock(){}
    public void Unlock(){}
}

答案 2 :(得分:0)

这里最有趣的设计问题是如何处理储物柜和储物柜之间的连接,因为必须满足锁定/解锁的要求。我看一下这个问题,想象一个游戏,玩家有时候可能是人类,但其他时候是猫(根据给出的例子),也许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!"

但是,您可能不希望公开公开所涉及的字段,或者混淆不需要了解锁定/解锁要求的对象所看到的Player界面。

我不是C#程序员,自从我使用Java以来​​已经有一段时间了,但我认为Java中可能也适用于C#的方法是让Player对象传递一个{{1}的实例}内部类作为Door对象的lock_unlock_credentials / get_locked方法的参数(或已建议的Lock对象。)get_unlocked对象将具有回调方法,凭借它是Player的内部类,可以访问Player对象的相关字段,但这些字段不会暴露在Player之外。然后,Lockable对象可以使用这些回调方法来检查是否满足它所关心的要求。您无法避免因需求而产生的耦合,但这种方式将详细信息保留在Player和Lockable对象之间的交互中。

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