I try to use dependency injection in a solution. I face a problem that in some cases the object(A) that is needed to be injected into an object(B) requires information(C) and I would like to encapsulate that information(C) into the object(B).
Some examples to make it more concrete:
Let's say I have a small application that fetches news headlines from a news site (like BBC,CNN) and then does stuff with them (e-mails it to certain users, etc.)
I had a NewsSite class(object B) that has the URL of the related news site(the information). I want to encapsulate the URL in the NewsSite class since due to the parsing and selectors the class only works with one site. I have an HttpResult class that is a wrapper around the http communication.
Original example:
class NewsSite:
url = '...'
def __init__(self):
self.http_result = HttpResult(url)
def get_news(self):
page = self.http_result.get_page()
# parsing the site
# fetching the news headlines
return news
In this case the NewsSite encapsulates the URL which is good. It also depends on the HttpResult class, a concrete implementation instead of an abstraction, which is not good.
Injected Http object:
class NewsSite:
url = '...'
def __init__(self, http_result):
self.http_result = http_result
self.http_result.set_url(url)
def get_news(self):
page = self.http_result.get_page()
# parsing the site
# fetching the news headlines
return news
Now in this case the Http result object is injected. The NewsSite doesn't depend on a concrete implementation, but only on the 'interface'(=any object that has the set_url and get_page methods). The URL is still encapsulated. However the http result object doesn't have all the information that is needed during its creation and I have to finish the initialization later with the set_url method, which is not too OO for me. (might be wrong)
Complete http result is injected:
class NewsSite:
def __init__(self, http_result):
self.http_result = http_result
def get_news(self):
page = self.http_result.get_page()
# parsing the site
# fetching the news headlines
return news
In this case I have a nice OO HttpResult created somewhere with all the information that's needed and it is injected into the NewsSite as it should be. However, I lost the URL. It's not encapsulated into the class anymore. It can come as a constant from some other class where it doesn't fit the context or a config file, while it shouldn't and can't change.
Lastly, with a factory:
class NewsSite:
url = '...'
def __init__(self, http_result_factory):
self.http_result = http_result_factory.create_http_result(url)
def get_news(self):
page = self.http_result.get_page()
# parsing the site
# fetching the news headlines
return news
Here, I have everything, but I am still not happy. I encapsulated the URL and I injected the dependency. However, I have to mock a whole factory object when I want to test the NewsSite object. Also, the NewsSite class having a 'Factory' as a constructor argument seems out of context and a bit alien. (Again, makes me feel less OO.)
This last code might seem nice now, but it's a really dummy example. What also bothers me is that with a real world example or with an object a bit higher up in the dependency chain might need 4-5 objects on creation which means 4-5 different factories. (Of course I can merge those factories in practice, but still it's semantically 4-5 factories for a news site)
Am I applying some bad design here or this is how it is? Is there any best practice or design pattern to solve these problems?
Back to the original and more general problem, I don't just want to solve this dummy problem. I would rather have some solution how I can encapsulate some information into a class and still being able to use dependency injection, even if the dependency needs that information.
Aucun commentaire:
Enregistrer un commentaire