【问题标题】:Liskov substitution principe and promise objectsLiskov 替换原则和承诺对象
【发布时间】:2015-02-20 15:35:27
【问题描述】:

我有一个基类报告:

class Report(object):
    def build():
        # ... sync report build
        return build_path  # str

还有一个在 celery 中构建报告的子类:

class AsyncReport(Report):
    def build():
        return task.delay(...)  # celery.result.EagerResult

这是否违反了 Liskov 替换原则?我认为是的,它违反了 LSP。

当一个逻辑操作 (build()) 有不同的实现和可能不同的返回类型时,如何设计这样的类?

例如:

  • 创建基础抽象类 Report 和 2 个具体子类:SyncReport 和 AsyncReport(但我不确定它是否违反 LSP)

    class Report(object):
        def build():
            # Raises NotImplementedError or has @abstractmethod decora
            pass
    
    class SyncReport(Report):
        def build():
            # ...
            return build_path  # str
    
    class AsyncReport(Report):
        def build():
            return task.delay(...)  # celery.result.EagerResult    
    
  • 不要覆盖 AsyncReport 类中的 build() 方法(它会降低继承的收益)

    class Report(object):
        def build():
            # ...
            return build_path #  str
    
    class AsyncReport(Report):
        def build_async():
            return task.delay(...)
    

【问题讨论】:

  • 不task.delay 返回AsyncResult 而不是EagerResult?
  • 这个问题纯属学术性的吗?否则我只会使用鸭子类型,因为它是 Python 中非常强大的功能。只需检查该方法是否返回适合您需要的正确类型,并且您很好......

标签: python solid-principles


【解决方案1】:

在 Python 中,继承在技术上并没有像例如由于鸭子类型在Java中。但是,从设计的角度来看,您可以考虑两种报表类型都希望实现的一些接口。虽然build() 的具体返回类型可能因实现而异,但它们仍然可能(并且应该)共享它们履行的一些共同合约,以便调用者可以统一使用它而不必怀疑类型。例如:

class SyncReport(object):
    def build():
        return EagerResult(...)  # Contains build_path

class AsyncReport(object):
    def build():
        return task.delay()  # AsyncResult

EagerResult 和 AsyncResult 是 ResultBase 的子类,但在 Python 中真正重要的是它们履行相同的约定,并且可以在不知道确切结果类型的情况下互换使用。这使得SyncReport 和AsyncReport 可以互换,即使没有公共基类,它们也遵循 LSP(允许针对接口进行编程)的意图。

【讨论】:

    猜你喜欢
    • 2014-06-05
    • 1970-01-01
    • 2010-12-03
    • 2019-10-29
    • 2016-08-20
    • 1970-01-01
    • 1970-01-01
    • 2016-03-12
    相关资源
    最近更新 更多