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

Binding multiple interfaces to same implementation #206

Open
qb-fredrikomstedt opened this issue Sep 28, 2022 · 2 comments
Open

Binding multiple interfaces to same implementation #206

qb-fredrikomstedt opened this issue Sep 28, 2022 · 2 comments

Comments

@qb-fredrikomstedt
Copy link

qb-fredrikomstedt commented Sep 28, 2022

Hi!

This issue is somewhat related to #181. Let's say I have the following code structure:

from abc import ABC, abstractmethod


class FileReader(ABC):
    @abstractmethod
    def read(self, file_path: str) -> str:
        pass


class FileWriter(ABC):
    @abstractmethod
    def write(self, file_path: str) -> None:
        pass


class FileHandler(FileReader, FileWriter):
    def read(self, file_path: str) -> str:
        # Do file reading stuff

    def write(self, file_path: str) -> None:
        # Do file writing stuff

Essentially a class that implements two interfaces, where only one interface may be needed elsewhere at a given time (for instance, many classes may need to read the files but only a few may need to write to them).

The FileHandler class could be instantiated as a Singleton, which in turn makes it reasonable that both FileReader and FileWriter bind to the same object. As is mentioned in #181, the following does not work (it creates two instances of FileHandler):

injector = Injector()

injector.binder.bind(FileReader, to=FileHandler, scope=singleton)
injector.binder.bind(FileWriter, to=FileHandler, scope=singleton)

Instead, it is suggested to create a Module doing the following:

class FileModule(injector.Module):
    def configure(self, binder: injector.Binder) -> None:
        binder.bind(FileHandler, scope=singleton)

    @provider
    def provide_reader(self, implementation: FileHandler) -> FileReader:
        return implementation

    @provider
    def provide_writer(self, implementation: FileHandler) -> FileWriter:
        return implementation

This seems like a lot of code for a binding of several interfaces to one instance. In other DI frameworks, I've seen syntax similar to:

injector.binder.bind([FileReader, FileWriter], to=FileHandler, scope=singleton)

I don't know if I've missed something, but is this possible to do in injector? If not, I think it would be a nice addition to the library.

Thanks!

@qb-fredrikomstedt
Copy link
Author

I've managed to solve this on my end by adding a bind_several function to the Binder class. I don't know if it covers all use cases of how injector can be used, but it does cover mine, and allows me to bind multiple interfaces to one implementation (and one instance of that implementation if the request_scope is singleton). It basically uses the same functionality as the Module classes do, just wrapped into a function.

Someone more experienced with this library could potentially point out if this is a good addition to this repository, if it requires some modification, or if it doesn't belong here at all. Regardless, the problem is solved on my end. :)

def bind_several(
    self,
    interfaces: List[Type[T]],
    implementation_type: Type[T],
    scope: Union[None, Type[Scope], ScopeDecorator] = None,
):
    def get_implementation(implementation: implementation_type):  # type: ignore
        return implementation

    self.bind(implementation_type, scope=scope)
    for interface in interfaces:
        self.bind(interface, to=inject(get_implementation), scope=scope)

As an example, it can be used like this:

class C(A, B):
    ...

injector.binder.bind_several([A, B], C, scope=singleton)

@jstasiak
Copy link
Collaborator

jstasiak commented Dec 2, 2022

This would be nice to have I think

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