1

For instance I have this bit of code

public class ProductService{
private IProductDataSource _dataSource = DependencyManager.Get<IProductDataSource>();
public Product Get(int id){
return _dataSource.Select(id);
}
}

I have 2 different data source:

  • XML file which contains the informations only in 1 language,
  • a SQL data base which contains the informations in many languages.

So I created 2 implementation for IProductDataSource, for for each kind of datasource. But how do I send the required language to the SQL data source ?

  • I add the parameter "language" to the method "IProductDataSource.Select" even if I won't use it in the case of the XML implementation.
  • Inside the SQL implementation I get the language from a global state ?
  • I add the language to the constructor of my SQL implementation, but then I won't use my DependencyManager and handle my self the dependency injection.

Maybe my first solution is not good.

remi bourgarel
  • 9,231
  • 4
  • 40
  • 73

3 Answers3

5

The third option is the way to go. Inject the language configuration to your SQL implementation. Also get rid of your DependencyManager ServiceLocator and use constructor injection instead.

Remo Gloor
  • 32,665
  • 4
  • 68
  • 98
  • I also agree with the first part of this response http://stackoverflow.com/questions/871405/why-do-i-need-an-ioc-container-as-opposed-to-straightforward-di-code/1532254#1532254 – remi bourgarel Nov 09 '11 at 09:34
  • the problem with your solution is that the entry point will initiate all the services, so I can end with a very complex initialization process. how to avoid it ? – remi bourgarel Nov 09 '11 at 16:00
  • 1
    Therefore IoC containers were invented. Use one of your choice to simplify the construction of your system at startup. – Remo Gloor Nov 09 '11 at 21:49
  • I used this solution, even if the initialization process is a bit tough, the program can run with or without a Service Locator. Do you have any other good article about it ? thanks – remi bourgarel Nov 15 '11 at 08:21
1

If your application needs to work with multiple languages in a single instance I think point one is a sensible approach. If the underlying data does not provide translations for a request language then return null. There is another solution in this scenario. I'm assuming that what you have is a list of products and language translations for each product. Can you refactor your model so that you do not need to specify or asertain the langauge until you reference language specific text? The point being a product is a product regardless of the language you choose to describe it. i.e. one product instance per product, only the product id on the Datasource.Select(..) method and some other abstraction mechanism to deal with accessing the correct text translation.

If however each instance of your application is only concerned with one language set I second Mr Gloor.

Myles McDonnell
  • 12,943
  • 17
  • 66
  • 116
  • I need to work with multiple languages, but what if I need to add another parameter (let say "currency" : my 1 db is always in euro but my second one has product in euro / dollars, I'll have to convert from $ to €), I'll add another useless parameter and so on ... And what if someone try to make is own implementation of IProductDataSource, he won't understand these currency, languages parameters ! – remi bourgarel Nov 09 '11 at 17:25
  • Product is a product, regardless of the currency in which you quote the price, filter products etc. Does a Money (http://martinfowler.com/eaaCatalog/money.html) class help here? – Myles McDonnell Nov 10 '11 at 10:37
  • A product is something you sell (a car is not a product, it's an object, a car for 200€ is a product), so something you sell as a price, and a price as a currency. And the product, money languages are just examples here to illustrate my problem : what if newly created implementation needs more parameters ? – remi bourgarel Nov 10 '11 at 11:45
0

First of all I need to point out that you are NOT injecting any dependencies with your example - you are depending on a service locator (DependencyManager) to get them for you. Dependency injection, simply put, is when your classes are unaware of who provides the dependencies, e.g. using a constructor, a setter, a method. As it was already mentioned in the other answers, Service locator is an anti-pattern and should be avoided. The reasons are described in this great article.

Another thing is that the settings you are mentioning, such as language or currency, seem to be localization related and would probably be better dealt with using the built-in mechanisms of your language of choice (e.g. resource files, etc).

Now, having said that, depending on how the rest of your code is structured you have several options to solve this while still using Service locator:

  • You could have SqlDataSource depend on some ILanguageProvider which pulls the current language from somewhere. However, with more settings like these (or if it is difficult to get current language in an isolated way) this can get messy very fast.
  • You could depend on IProductDataSourceFactory instead (or, if you are using C#, Func<IProductDataSource>) which would return the concrete implementation with the correct settings. Again, you need to be able to get the current language in an isolated way in order to use this.
  • You could go with option 1 in your question. This would be a leaky abstraction but would be the simplest to implement.

However, if you decide to get rid of service locator and start using some DI container, the best solution would be using option 3 (as it was already stated) and configuring container accordingly to provide the correct value. Some good ideas of how to do this in an elegant way can be found in the answer to this question

Community
  • 1
  • 1
  • SQLDataSource : this looks like the first answer, I add the language to my datasource's constructor. IProductDataSourceFactory : in this case I'd have to take the language from a global state (http context, cnfiguration ...), I prefer to avoid that. The problem with my first solution is that if I need for 1 implementation I'll have to do for all of them, so if someone is using this implementation ... I don't understand why my Service locator is not dependencyinjection, my service doesn't know about the implementation of IProductDataAccess – remi bourgarel Nov 10 '11 at 08:50