C # name propagation for dependency injection interfaces

I want to use the Dependency Injection pattern in C #, and I want the logics to be as separated as possible in namespaces.

Question

What namespace should the interface consumed class be in?

Motivation of the question

First, let's make a “normal” case. A bookcase to be used as the basis for the second part of the explanation. Then the "real" case arises in which the question arises.

Book case

Suppose that the encoder is Alice, and that she uses Alice as the top-level name in namespaces as a provider, to avoid conflict with other codes. In this example, we assume that there are no other Alices in the world.

Suppose she creates 3 namespaces:

  • Alice.Invaders is a game that should provide in-store purchases through the store.
  • Alice.Shop is a reusable store for several games.
  • Alice.Injector is a multiple service manager.

Suppose the Shop project has an interface named IShopService , which provides the Show() method.

Suppose Invaders has some kind of controller that, with some user action, wants to open a store.

Suppose services such as Shop are received through the ServiceManager by the Invaders controller.

Alice.Injector

Alice.Injector itself is an independent project that has no dependencies, so it does not use the "using" keyword:

 namespace Alice.Injector { public interface IService { // All the services shall implement this interface. // This is necessary as C# is heavily typed and the // Get() method below must return a known type. } public class ServiceManager { static public IService Get( string serviceName ) { IService result; // Do the needed stuff here to get the service. // Alice implements this getter configurable in a text-file // so if she wants to test the invaders with a mock-shop // that does not do real-purchases but fake-ones // she can swap the injected services without changing the // consumer code. result = DoTheNeededStuff(); return result; } } } 

Alice.Shop

Alice.Shop also an independent project, which (except when it consumes services) is not aware of the existence of an injector. Just a store and that.

As Alice thinks that maybe someday Bob will make some better store, she prepares her class for injection dependent after splitting Shop into the IShop interface, and then implementing, following this article: https://msdn.microsoft. com / library / hh323705% 28v = vs. 100% 29.aspx

To achieve this, Alice will make the store a service that is compatible with Alice.ServiceManager , so Alice decides that IShop will be renamed IShopService , and it will be a kind of IService .

 using Alice.Injector namespace Alice.Shop { public interface IShopService : IService { public void Show(); } public class Shop : IShopService { public void Show() { // Here Alice puts all the code to open the shop up. } } } 

Alice.Invaders

Finally, Alice encodes the game. The Alice.Invaders code gets the Shop (which takes the form of a service) through the ServiceManager , so this is all pure code.

 using Alice.Injector using Alice.Shop namespace Alice.Invaders { public class DefaultController { void OnShopClick() { IShopService shop = ServiceManager.Get( "Shop" ) as IShopService; shop.Show(); } } } 

So far, all this has worked great.

Real case

So, now ... Bob (Alice’s good friend, as you all know, is curious that they don’t talk about sending messages written today) makes a super-nice store that is even nicer than the one Alice did. Bob makes his store from scratch.

So, Bob implements a store compatible with Alice's injector (since Bob also uses Alice.Injector to Alice.Injector other things in his projects).

 using Alice.Injector namespace Bob.Shop { public interface IShopService : IService { public void Show(); } public class Shop : IShopService { public void Show() { // Here Bob does a brand new shop from scratch. } } } 

So ... this is a strange situation!

  • Bob.Shop Namespace for the interface . If Bob makes his store inside the Bob.Shop namespace as shown above, Alice needs to edit her code for the Bob.Shop link to get the IShopService interface (ugly she needs to change the dependency in the code, as it was supposed to use dependency injectors to get rid of from changing dependencies in the code).
  • There is no namespace for the interface . If both Alice and Bob install IShopService in the global namespace, this is also ugly, as there are many things that can conflict there.
  • Alice.Shop Namespace for the interface . If Bob uses common sense and says, "I want to do to create a store with Alice one, then I have to implement the HER interface, "so Bob's code will most likely look like this:

Bob code using Alice.Shop reverse namespace compatibility:

 namespace Bob.Shop { public class Shop : Alice.Shop.IShopService { public void Show() { // Here Bob does a brand new shop from scratch, // which borrows Alice interface. } } } 

In this case, everything seems to be in place:

  • Bob can create Bob.Shop.Shop that implements Alice.Shop.IShopService
  • Alice does not need to change one line of code.
  • Alice.Injector.ServiceManager may provide another IService when serving Bob.Shop.Shop .

Problem

However, there is a dependency:

Alice.Invaders passes Alice.Injector.IService to Alice.Shop.IShopService to be able to call the Show() method. If you do not do this actor, you cannot "show the store."

So, in the end, you are “dependent” on this act, and therefore “someone” must provide you with a definition of the interface.

If the original store was not written by Alice, but Charlie would be ugly, you still have to download and save a copy of the Charlie.Shop project in order to use Bob.Shop .

So...

Questions

1) What is the correct namespace for IShopInterface live in?

2) Does the project provide a “replacement” for its own interface, or does it borrow the original?

3) Maybe the original store will be divided into two projects? (for example, Alice.Shop and Alice.ShopImplementation , so Alice.Shop is thin veeery and contains only interfaces? Perhaps Alice.Shop and Alice.Shop.Implementation as an embedded namespace, but still two separate code bases so you could download as install Alice.Shop without downloading Alice.Shop.Implementation ?

4) Is it as simple as Bob including a copy of the Alice.Shop.IShopInterface file in his project, so no dependencies are required? Very ugly - if he does, and we want to have 2 stores and send users to one or another store that will conflict.

Thanks.

+1
source share
1 answer

Interfaces, injector, and implementation must be in different namespaces. Interfaces should be in Alice.Shop.Interfaces and there should be no implementations in this namespace. You can change / hide the implementation, but you must stick to the interfaces depending on the Injection.

Alice.Invaders distinguishes Alice.Injector.IService from Alice.Shop.IShopService to be able to call the Show () method. If you do not do this actor, you cannot "show the store."

Your DefaultController function is not suitable. If I want to use it, I don’t know anything about what services I need. He tells me that I don’t need anything right now.

You must use constructor injection.

 public class DefaultController { private readonly IShopService _shopService; DefaultController(IShopService shopService) { _shopService=shopService; } void OnShopClick() { _shopService.Show(); } } 

If I need a default controller, I would know what services I need with this implementation. And you do not need to quit.

Edit:

Say Alice has a store. She says I need a reading room with 5 chairs. But she decides that the chairs are made of wood or leather (IChairs). When she opens the store, she decides to use wooden chairs (Inject WoodChairs for IChairs).

Then Bob buys a store from Alice. He cannot change the reading room (time will not be easy for him, and the reading room is simply beautiful). But he wants leather chairs, so he uses leather chairs (Inject LeatherChairs for IChairs).

Bob should stick with Alice.Shop.Interfaces if he cannot or does not want to change the reading room.

But let's say. I really like the reading room of Alice. I want to create a reading room like her. But I want to make my own rules for the reading room (the IMyReadingRoom adapter, you get the ReadingRoom class, not the interface, and create your own interfaces).

In short : you should always stick to interfaces. You can create your own interface ( Adapter ) for third-party libraries. This way you can extend or hide the rules without inserting a third-party library (but you should still stick to your interface). You must write an adapter to implement third-party libraries not for your interfaces.

If we skip the choice of adapter, Bob should use Alice.Shop.Interfaces for injection.

+1
source

Source: https://habr.com/ru/post/1237383/


All Articles