Source code for domainlab.compos.pcr.p_chain_handler
"""Chain of Responsibility"""
__author__ = "Xudong Sun"
import abc
from domainlab.utils.logger import Logger
[docs]
class Request4Chain(metaclass=abc.ABCMeta):
"""
define all available fields of request to ensure operation safety
"""
[docs]
@abc.abstractmethod
def convert(self, obj):
"""
Convert an heavy weight object into request object with pre-defined behavior
"""
raise NotImplementedError
[docs]
class AbstractChainNodeHandler(metaclass=abc.ABCMeta):
"""Chain of Responsibility:
1. Make sure the chain can be constructed successfully even one node fails to
initialize its designated service/business object so services from other nodes will still
be available.
2. To ensure this decoupling, avoid overriding self.__init__() (the initializer/constructor)
of the handler by using multiple inheritance.
e.g. inherit both AbstractChainNodeHandler and Factory Interface since doing so couples the
Factory initializer with the AbstractChainNodeHandler():
if the initializer/constructor of the Factory does not work, it will
affect the whole chain.
3. Instead, return service/business object in a member function of AbstractChainNodeHandler
self.init_business(), this can result in redundant code but is safest.
4. A sub-optimal but still acceptable solution is to use Multiple Inheritance (inherit
AbstractChainNodeHandler and Factory interface) but **only** override the
self.init_business(*kargs, **kwargs) method (with concrete input arguments)
of the Chain Handler so the initializer/constructor of the Chain Handler
will always work. Factory can be returned by calling
ChainNode.init_business(*kargs, **kwargs).
This can still be coupling since there might be some interface methods in Factory,
once you change the parent class, some concrete factories has not implemented that,
which will break the initalization of the chain.
"""
def __init__(self, success_node=None):
"""__init__.
:param success_node: successor chain node which implement the AbstractChainNodeHandler
interface
"""
self._task = None
self._success_node = success_node
self._parent_node = None
self.next_model = None
if success_node is not None:
success_node.set_parent(self)
[docs]
def set_parent(self, parent):
self._parent_node = parent
parent._success_node = self
[docs]
@abc.abstractmethod
def is_myjob(self, request):
"""is_myjob.
:param request: subclass can override request object to be string or function
:return True/False
"""
raise NotImplementedError
[docs]
@abc.abstractmethod
def init_business(self, *kargs):
"""init_business: initialize **and** return the heavy weight business object for
doing the real job
:param request: subclass can override request object to be string or function
:return: the constructed service object
"""
raise NotImplementedError
[docs]
def handle(self, request):
"""This method invoke self.is_myjob() to check which node in the chain should handle the
request
:param request: subclass can override request object to be string or function
:return: light weight AbstractChainNodeHandler
"""
if self.is_myjob(request):
return self
if self._success_node is not None:
return self._success_node.handle(request)
err_msg = "option " + str(request) + " does not exist"
logger = Logger.get_logger()
logger.info(err_msg)
logger.info("available options are")
self.print_options()
raise NotImplementedError(err_msg)
[docs]
def print_options(self):
logger = Logger.get_logger()
logger.info(str(self.__class__.__name__))
if self._parent_node is not None:
self._parent_node.print_options()
[docs]
class DummyBusiness:
message = "dummy business"
[docs]
class DummyChainNodeHandlerBeaver(AbstractChainNodeHandler):
"""Dummy class to show how to inherit from Chain of Responsibility"""
[docs]
def init_business(self, *kargs, **kwargs):
return DummyBusiness()
[docs]
def is_myjob(self, request):
"""is_myjob.
:param request: subclass can override request object to be string or function
:return True/False
"""
return True
[docs]
class DummyChainNodeHandlerLazy(AbstractChainNodeHandler):
"""Dummy class to show how to inherit from Chain of Responsibility"""
[docs]
def init_business(self, *kargs, **kwargs):
return DummyBusiness()
[docs]
def is_myjob(self, request):
"""is_myjob.
:param request: subclass can override request object to be string or function
:return True/False
"""
return False