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