rodi icon indicating copy to clipboard operation
rodi copied to clipboard

how to apply Decorator design pattern with rodi?

Open Eldar1205 opened this issue 4 years ago • 4 comments

Hi, How to apply the Decorator design pattern with rodi, on single instance registrations and/or collection registrations?

Example how I used to do it with a DI container from C# .Net called SimpleInjector (if same syntax were to be in lagom):

class HttpClient(ABC):
    async def send(self, request: Request) -> Response:
        pass

class ActualHttpClient(HttpClient):
    async def send(self, request: Request) -> Response:
        # Implement actual request send, e.g. with aiohttp

class LoggingHttpClientDecorator(HttpClient):
    def __init__(self, decoratee: HttpClient):
        self.__decoratee = decoratee
    async def send(self, request: Request) -> Response:
        print("Sending request")
        response = await self.__decoratee.send(request);
        print("Received response")
        return response;

container = Container()
container[HttpClient] = Singleton(ActualHttpClient)
container.RegisterSingletonDecorator[HttpClient, LoggingHttpClientDecorator](); # mimics the SimpleInjector syntax

With this code, resolving HttpClient would return LoggingHttpClientDecorator decorating ActualHttpClient. SimpleInjector also supports collection injections, e.g. injecting something like Iterable[EventHandler], and apply a decorator to all of the EventHandler instances registered to the collection.

Eldar1205 avatar Aug 29 '21 16:08 Eldar1205

Hi, I apologize for taking so long to reply. The feature you describe looks interesting, it is not supported by rodi. It could be a nice addition to the library.

The closest you could get at the moment, is to use a factory:

from abc import ABC

from rodi import Container


class Base(ABC):
    pass


class ActualImplementation(Base):
    pass


class LoggingImplementation(Base):
    def __init__(self, decoratee: Base) -> None:
        super().__init__()
        self._decoratee = decoratee


container = Container()


def example_factory() -> Base:
    return LoggingImplementation(ActualImplementation())


container.add_singleton_by_factory(example_factory)

provider = container.build_provider()

example = provider.get(Base)

assert isinstance(example, LoggingImplementation)
assert isinstance(example._decoratee, ActualImplementation)

But I see the difference, since the decorator feature you are describing enables changing services that might be registered in a different package. In fact, it seems that the decorator might bypass completely the base class?

RobertoPrevato avatar Sep 01 '21 20:09 RobertoPrevato

The decorator must have a constructor parameter implementing the same ABC as it's registered for, and can ignore it in the implementation of the abstract methods. The useful thing about such a decorator feature is the decorator may have other injectables other than the decoratee, e.g. LoggingImplementation can have a Logger dependency besides the decoratee, and the DI container would handle injection of additional dependencies for the user.

‫בתאריך יום ד׳, 1 בספט׳ 2021 ב-23:47 מאת ‪Roberto Prevato‬‏ <‪ @.***‬‏>:‬

Hi, I apologize for taking so long to reply. The feature you describe looks interesting, it is not supported by rodi. It could be a nice addition to the library.

The closest you could get at the moment, is to use a factory:

from abc import ABC from rodi import Container

class Base(ABC): pass

class ActualImplementation(Base): pass

class LoggingImplementation(Base): def init(self, decoratee: Base) -> None: super().init() self._decoratee = decoratee

container = Container()

def example_factory() -> Base: return LoggingImplementation(ActualImplementation())

container.add_singleton_by_factory(example_factory) provider = container.build_provider() example = provider.get(Base) assert isinstance(example, LoggingImplementation)assert isinstance(example._decoratee, ActualImplementation)

But I see the difference, since the decorator feature you are describing enables changing services that might be registered in a different package. In fact, it seems that the decorator might bypass completely the base class?

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/Neoteroi/rodi/issues/15#issuecomment-910733610, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABIFRN2USOQZROSMVYOQB73T72GPVANCNFSM5DAMYBDA . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

Eldar1205 avatar Sep 01 '21 20:09 Eldar1205

Thanks for opening this issue, I will keep it open and try to add this feature to the library (eventual contributions are welcome). This month I won't have much time for my open source projects, and I have pending things on the web framework, but I won't forget about this.

RobertoPrevato avatar Sep 01 '21 21:09 RobertoPrevato

Thank you!

‫בתאריך יום ה׳, 2 בספט׳ 2021 ב-0:22 מאת ‪Roberto Prevato‬‏ <‪ @.***‬‏>:‬

Thanks for opening this issue, I will keep it open and try to add this feature to the library (eventual contributions are welcome). This month I won't have much time for my open source projects, and I have pending things on the web framework, but I won't forget about this.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/Neoteroi/rodi/issues/15#issuecomment-910772089, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABIFRN7PSAEU7EVQEKULCF3T72KRFANCNFSM5DAMYBDA . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

Eldar1205 avatar Sep 01 '21 21:09 Eldar1205