Reputation: 1736
I have a base class Report:
class Report(object):
def build():
# ... sync report build
return build_path # str
And a child class that build reports in celery:
class AsyncReport(Report):
def build():
return task.delay(...) # celery.result.EagerResult
Is it violates a Liskov substitution principle? I assume that yes, it violates LSP.
How to design such classes, when one logical operation (build()
) has different implementations and possible different return types?
For example:
Make base abstract class Report and 2 concrete child classes: SyncReport and AsyncReport (but I'm not sure that its doesn't violates 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
Don't override a build() method in AsyncReport class (it reduces a profit from inheritance)
class Report(object):
def build():
# ...
return build_path # str
class AsyncReport(Report):
def build_async():
return task.delay(...)
Upvotes: 3
Views: 1075
Reputation: 34290
In Python inheritance technically does not play as big role as e.g. in Java due to duck-typing. However, from the design point of view you can think of some interface that both report types are expected to implement. While the concrete return type of build()
may be different for the implementations, they still may (and should) be sharing some common contract they fulfill, so that the caller may use it uniformly without wondering about the types. For example:
class SyncReport(object):
def build():
return EagerResult(...) # Contains build_path
class AsyncReport(object):
def build():
return task.delay() # AsyncResult
EagerResult
and AsyncResult
are sub-classes of ResultBase
, but what actually matters in Python, is that they fulfill the same contract and may be used interchangeably without knowing the exact result type. That makes SyncReport
and AsyncReport
interchangeable and they follow the intention of LSP (allow programming against the interface) even without a common base class.
Upvotes: 1