Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

how to apply Decorator design pattern with rodi? #15

Open
Eldar1205 opened this issue Aug 29, 2021 · 4 comments
Open

how to apply Decorator design pattern with rodi? #15

Eldar1205 opened this issue Aug 29, 2021 · 4 comments

Comments

@Eldar1205
Copy link

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.

@RobertoPrevato
Copy link
Member

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?

@Eldar1205
Copy link
Author

Eldar1205 commented Sep 1, 2021 via email

@RobertoPrevato
Copy link
Member

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.

@Eldar1205
Copy link
Author

Eldar1205 commented Sep 1, 2021 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants