【问题标题】:Did I violate the LSP principle in this example?我在这个例子中是否违反了 LSP 原则?
【发布时间】:2018-10-06 17:47:18
【问题描述】:

我有实现 2 种门的代码。 一扇门有锁,另一扇没有。

界面很简单:

public interface Door {
    void open();
    void close();
}

然后我有实现:LockedDoorRegularDoor

public class LockedDoor implements Door {
    private Lock lock;
    private boolean isOpen;

    @Override
    public void open() {
        if(!lock.isLocked()) {
            this.isOpen = true;
        }
    }

    @Override
    public void close() {
        this.isOpen = false;
    }
}

public class RegularDoor implements Door {
    private boolean isOpen;

    @Override
    public void open() {
        isOpen = true;
    }

    @Override
    public void close() {
        isOpen = false;
    }
}

如您所见,LockedDoor 的打开功能只有在锁未解锁时才会打开门。
你可以通过从LockedDoor接收它并调用它的解锁函数来解锁。

这是否违反了里氏替换原则?
如果是,有什么好的选择?

【问题讨论】:

  • 你的问题在这里更合适:Software Engineering SE
  • 为什么你认为你需要两个不同的类?拥有一个 Door 类还不够吗?你只需要检查isOpen 状态是否锁住了门:)
  • @KarelG 在引用其他网站时,指出cross-posting is frowned upon 通常会有所帮助

标签: java oop design-patterns solid-principles liskov-substitution-principle


【解决方案1】:

回答这个问题有点困难,因为您的Door 界面似乎不完整,因为不清楚open()close() 应该做什么。让我们通过添加一个isOpen() 方法来清除它,并定义一旦调用open(),随后对isOpen() 的调用应该返回true(我故意忽略如果你尝试会发生什么的问题为简洁起见,打开且已经打开的门)。

在这种情况下,你肯定违反了 LSP 原则——如果你试图打开一扇上锁的门,你会失败,而门将保持关闭状态。

解决此问题的一种方法是向 open()close() 方法添加返回值,以便它们可以报告操作是否成功:

public interface Door {
    /**
     * Checks if the door is open.
     * @return {@code true} if the door is open, {@code false} if not.
    boolean isOpen();

    /**
     * Attempt to open the door.
     * @return {@code true} if the door was successfully opened, 
     * {@code false} if not.
     * In other words, if a call to {@code open} returns {@code true}, a
     * subsequent call to {@link #isOpen} will return {@code true}.
     */
    boolean open();

    /**
     * Attempt to close the door.
     * @return {@code true} if the door was successfully closed, 
     * {@code false} if not.
     * In other words, if a call to {@code close} returns {@code true}, a
     * subsequent call to {@link #isOpen} will return {@code false}.
     */
    void close();
}

public class RegularDoor implements Door {
    private boolean isOpen;

    @Override
    public boolean isOpen() {
        return isOpen;
    }

    @Override
    public boolean open() {
        return isOpen = true;
    }

    @Override
    public boolean close() {
        return isOpen = false;
    }
}


public class LockedDoor implements Door {
    private Lock lock;
    private boolean isOpen;

    @Override
    public boolean isOpen() {
        return isOpen;
    }

    @Override
    public boolean open() {
        if (!lock.isLocked()) {
            return isOpen = true;
        }
        return false;
    }

    @Override
    public boolean close() {
        return isOpen = false;
    }

    // Not shown here - methods to lock and unlock the door
}

【讨论】:

  • 我认为在无法打开时让 open() 抛出异常会更好。通过从命令方法获得返回值,您会破坏 CQS,尽管区分成功和失败的 open() 执行仍然有好处。异常清楚地表明,要么命令成功,要么我们应该转到不同的执行流程。
【解决方案2】:

,您(可能)没有违反 LSP。

更长的答案:当然取决于您在接口Door 中对open() 方法的“定义”。如果您将方法定义为“尽可能尝试开门”,那么您就没有问题了。

可能有人争辩说应该调用open() 方法tryOpen() 以向调用者阐明您在调用后门可能不会打开的意图。

如果您将open() 方法定义为始终开门,那么您当然违反了LockedDoor 中的合同(和LSP)。

另一个问题是,界面中缺少一些东西。就目前而言,打开/关闭状态对任何可用方法open()/close() 都没有影响。我假设您在 Door 中还有其他一些与门状态相关的方法,例如 walkThrough() 或类似的东西。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-02-15
    • 2010-11-29
    • 2020-02-03
    • 2015-01-01
    • 2017-03-17
    • 1970-01-01
    • 2017-07-04
    • 2021-12-15
    相关资源
    最近更新 更多