依赖注入接口的C#命名空间 [英] C# namespacing for interfaces in dependency injection

查看:626
本文介绍了依赖注入接口的C#命名空间的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在C#中使用依赖注入模式,我希望在命名空间中尽可能分离逻辑。



问题



消费类的接口在哪个命名空间? >

动机的问题



首先让我们做一些正常的情况。作为第二部分解释的基础的书案。那么这个现实生活的案例就是这个问题。



书盒



编码器是爱丽丝,她使用 Alice 作为供应商的命名空间中的顶级名称,以避免与其他编码器发生冲突。对于这个例子,我们假设世界上没有其他的Alice。



我们假设她创建了3个命名空间:




  • Alice.Invaders - 将通过商店提供应用内购买的游戏。

  • Alice.Shop - 几个游戏的可重复使用的商店。

  • Alice.Injector - 一个可重复使用的服务经理。



我们假设 Shop 有一个接口命名为 IShopService ,它提供了一个方法 Show()



我们假设侵略者有某种用户操作想要打开店铺的控制器。



我们假设这些服务,如 Shop ,通过 ServiceManager Invaders 的控制人。



Alice.Injector



Alice.Injector 本身是一个独立的项目t没有依赖关系,所以它不使用使用关键字:

 命名空间Alice.Injector 
{
public interface IService
{
//所有的服务都应该实现这个接口。
//这是必需的,因为C#是大量类型的,下面的
// Get()方法必须返回一个已知的类型。
}

public class ServiceManager
{
static public IService Get(string serviceName)
{
IService result;

//这里需要的东西来获得这个服务。
// Alice实现这个getter可以在文本文件
//中配置,所以如果她想用一个模拟商店
//来测试入侵者,那不是真正的购买,而是假的--one
//她可以交换注入的服务,而不用更改
//消费者的代码。

result = DoTheNeededStuff();

返回结果;
}
}
}



Alice.Shop



Alice.Shop 也是一个独立的项目(除了在消费服务的情况下)不知道存在的注射器。只是一家商店,就是这样。



由于爱丽丝认为,有一天鲍勃可能会做一些更好的商店,她准备让她的课程被依赖注入,随着分离 Shop 进入界面 IShop 然后执行,按照这篇文章: https://msdn.microsoft.com/library/hh323705%28v=vs.100%29.aspx



为了实现这一点,爱丽丝将使商店成为与 Alice.ServiceManager ,所以爱丽丝决定将 IShop 将重命名为 IShopService ,它将是一种 IService

 使用Alice.Injector 

命名空间爱丽丝。$ s
{
public interface IShopService:IService
{
public void Show();
}

public class Shop:IShopService
{
public void Show()
{
//这里Alice将所有代码打开店铺。
}
}
}



Alice.Invaders



最后,爱丽丝编码游戏。 Alice.Invaders 的代码通过<$ c获取一个 Shop (具有服务形状) $ c> ServiceManager 所以都是干净的代码。

 使用Alice.Injector 
使用Alice.Shop

命名空间Alice.Invaders
{
public class DefaultController
{
void OnShopClick()
{
IShopService shop = ServiceManager.Get(Shop)作为IShopService;
shop.Show();
}
}
}

到这里,这一切



现实情况



所以现在... Bob(爱丽丝的好朋友,众所周知,好奇,他们不会在今天发送封装的消息),一个超级好的店,甚至比爱丽丝做的更好。 Bob从零开始他的店铺。



所以Bob实现了与Alice注射器兼容的商店(Bob还使用了 Alice.Injector 在他的项目中注入其他东西)

 使用Alice.Injector 

命名空间Bob 。$ s
{
public interface IShopService:IService
{
public void Show();
}

public class Shop:IShopService
{
public void Show()
{
//这里Bob做了一个全新的商店从头开始。
}
}
}

所以...在这里是奇怪的情况!!




  • Bob.Shop 界面 - 如果Bob以 Bob.Shop 命名空间在上面显示的方式进行他的商店,那么Alice必须编辑她的代码以引用 Bob.Shop 获取 IShopService 接口(丑陋的她必须更改代码中的依赖关系,因为它是应该的使用依赖注入器来摆脱代码中的依赖关系)

  • 接口的命名空间 - 如果Alice和Bob都设置了 IShopService 在全球命名空间中,它也是丑陋的,因为有很多事情可能会发生冲突。

  • Alice.Shop 接口的命名空间 - 如果Bob使用常识,并说我想要做的是创建一个与兼容的商店与Alice的一个,然后我应该实施HER界面,所以Bob的代码最有可能是这样的:



Bob的代码使用 Alice .Shop 向后命名空间兼容性:

 命名空间Bob.Shop 
{
public class Shop:Alice.Shop.IShopService
{
public void Show()
{
//这里Bob从头开始创建一个全新的商店,
//借用了爱丽丝的界面。
}
}
}

在这种情况下,已经到位:




  • Bob可以创建实现的 Bob.Shop.Shop Alice.Shop.IShopService

  • 爱丽丝不需要更改单行代码。

  • Alice.Injector.ServiceManager 可以在中提供另一个 IService Bob.Shop.Shop



问题



仍然有依赖关系:



Alice.Invaders 正在投射爱丽丝.Injector.IService Alice.Shop.IShopService ,以便能够调用 Show()方法。如果你不这样做,你不能显示商店。



所以最后,你是依赖那个演员,因此有人需要为您提供界面定义。



如果原始商店不是由爱丽丝写的,而是由查理写的,那么仍然需要下载丑陋并保留 Charlie.Shop 项目的副本,以便使用 Bob.Shop



所以...



问题



1)什么是正确的命名空间为 IShopInterface 住在?



2)替换项目是否提供自己的界面或借用原来的一个?



3)原来的商店是否应该分两个项目? (例如 Alice.Shop Alice.ShopImplementation so Alice.Shop 是veeery slim,只包含接口?也许 Alice.Shop Alice.Shop.Implementation as一个嵌套的命名空间,但仍然有两个分离的代码库,所以你可以下载安装 Alice.Shop 而不下载 Alice.Shop.Implementation



4)这样简单,Bob确实包含一个的副本Alice.Shop.IShopInterface 在他的项目文件,所以不需要依赖?非常丑陋 - 如果他这样做,我们想要有2家商店,并将用户发送到一家或其他商店,那将会发生冲突。



谢谢。

解决方案

接口,注入和实现应该在不同的命名空间中。接口应该在 Alice.Shop.Interfaces 中,并且在这个命名空间中不应该有任何实现。您可以更改/隐藏实现,但是您应该遵循依赖注入的接口。


Alice.Invader正在转换Alice.Injector.IService以
Alice.Shop.IShopService为了能够调用Show()方法。
如果你不这样做,你不能显示店铺。


您的DefaultController实现是不好。如果我想使用它,我不知道我需要哪些服务。它对我说,我现在不需要任何东西。



您应该使用构造函数注入。

  public class DefaultController 
{
private readonly IShopService _shopService;

DefaultController(IShopService shopService)
{
_shopService = shopService;
}

void OnShopClick()
{
_shopService.Show();
}
}

如果我需要defaultcontroller,我会知道哪些服务需要这个实现。



编辑:



爱丽丝有一个商店。她说我想要一个有5把椅子的阅览室。但她会决定椅子是从木头或皮革(IChairs)。当她打开商店时,她决定使用木椅(为IChairs注入WoodChairs)。然后,Bob从爱丽丝购买商店。他不能改变阅览室(这很难需要时间,阅览室很好)。但是他想要皮椅,所以他使用真皮椅子(IChairs的Inject LeatherChairs)。



Bob应该坚持 Alice.Shop.Interfaces 如果他不能或不想改变阅览室。



但是让我们说。我喜欢爱丽丝阅读室。我想设计像她一样的阅览室。但是我想让我的阅读室的规则(IMyReadingRoom适配器,你得到ReadingRoom类不接口,你创建自己的接口)。



总之:你应该坚持接口。您可以为第三方库创建自己的界面(适配器) 。这就是你可以扩展或隐藏规则,而不必坚持第三方库(但你应该坚持使用自己的接口)。您应该为第三方库实现而不是为其界面编写适配器。



如果我们跳过适配器选项,Bob必须使用 Alice.Shop.Interfaces 注入。


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

Question

In which namespace should the interface of the consumed class be?

Motivation of the question

First let's do some "normal" case. A book-case to be used as the basis for the second part of the explanation. Then, the "real-life" case, which arises the question.

Book case

Let's assume the coder is Alice and that she uses Alice as a top-level name in the namespaces as a vendor, to avoid any conflict with other coders. For this example, we'll assume there are no other Alices in the world.

Let's assume she creates 3 namespaces:

  • Alice.Invaders - a game that shall provide in-app purchases via a shop.
  • Alice.Shop - a reusable shop for several games.
  • Alice.Injector - a reusable service manager.

Let's assume the Shop project has an interface named IShopService which provides a method Show().

Let's assume the Invaders has some kind of controller that at some user-action wants to open the shop.

Let's assume the services, like the Shop, are gotten via a ServiceManager by the controller of the Invaders.

Alice.Injector

The 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's code.

            result = DoTheNeededStuff();

            return result;
        }
    }
}

Alice.Shop

The Alice.Shop is also an independent project which (except in the case it consumes services) is not aware of the existance of an injector. Just is a shop and that's it.

As Alice thinks that maybe Bob will make some better shop someday, she prepares her class to be dependency-injected, following the separation of the Shop into an interface IShop and then the implementation, following this article: https://msdn.microsoft.com/library/hh323705%28v=vs.100%29.aspx

To achieve that, Alice will make the shop to be a service kind of compatible with the Alice.ServiceManager, so Alice decides that the IShop will be renamed to 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 codes the game. The code of the Alice.Invaders gets a Shop (that takes the shape of a service) via the ServiceManager so it is all clean-code.

using Alice.Injector
using Alice.Shop

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

Up to here, this all works nicely.

Real-life case

So now... Bob (a good friend of Alice, as you all know, curious that they don't talk about sending encripted messages today), does a super-nice shop that is even nicer than the one that Alice did. Bob does his shop from scratch.

So Bob implements the shop compatible with Alice's injector (as Bob also uses the Alice.Injector for injecting other stuff 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... here it is the weird situation!!

  • Bob.Shop Namespace for the interface - If Bob does his shop inside the Bob.Shop namespace the way displayed above, then Alice must edit her code to reference Bob.Shop to get the IShopService interface (ugly she has to change the dependency in the code, as it was supposed to use Dependency Injectors to get rid of changing the dependencies in the code).
  • No namespace for the interface - If both Alice and Bob setup the IShopService in the global namespace, it is ugly too, as there is much things that could conflict there.
  • Alice.Shop Namespace for the interface - If Bob makes use of common sense and says "What I want to do is to create a shop compatible with Alice's one, then I should implement HER interface" so Bob's code most likely will be like this one:

Bob's code using Alice.Shop backwards 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's interface.
        }
    }
}

In this case it seems everything is in place:

  • Bob may create the Bob.Shop.Shop that implements the Alice.Shop.IShopService
  • Alice does not need to change a single line of code.
  • The Alice.Injector.ServiceManager is able to provide another IService when serving the Bob.Shop.Shop.

Problem

Still there is a dependency here:

Alice.Invaders is casting the Alice.Injector.IService to an Alice.Shop.IShopService in order to be able to call the Show() method. If you don't do that cast, you can't "show the shop".

So at the end, you are "depending" on that cast and therefore "someone" needs to provide you the interface definition.

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

So...

Questions

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

2) Does the "replacement" project provide it's own interface or borrow the original one?

3) Should maybe the original shop be splitted in TWO projects? (like for example Alice.Shop and Alice.ShopImplementation so Alice.Shop is veeery slim and only contains the interfaces? Maybe Alice.Shop and Alice.Shop.Implementation as a nested namespace, but still two sepated code-bases so you can download ans install Alice.Shop without downloading Alice.Shop.Implementation?

4) Is that as simple as Bob does include a copy of Alice.Shop.IShopInterface file in his project so no dependencies are needed? Very ugly - if he does so and we want to have the 2 shops and send the users to one or other shop, that would conflict.

Thanks.

解决方案

Interfaces, injector and implemantation should be in different namespaces. Interfaces should be in Alice.Shop.Interfaces and there shouldn't be any implemantation in this namespace. You can change/hide implementation, but you should stick with Interfaces in dependency Injection.

Alice.Invaders is casting the Alice.Injector.IService to an Alice.Shop.IShopService in order to be able to call the Show() method. If you don't do that cast, you can't "show the shop".

Your DefaultController implemantation is not good. If I want to use it, I don't know anything about which services I need. It says to me, I don't need anything now.

You should use constructor injection.

public class DefaultController
 {
   private readonly IShopService _shopService;

   DefaultController(IShopService shopService)
   {
     _shopService=shopService;
   }

   void OnShopClick()
   {
     _shopService.Show();
   }
  }

If I need defaultcontroller, I would know which services I need with this implemantation. And you don't need to cast.

Edit:

Let's say Alice has a shop. She says I want a reading room which has 5 chairs. But she will decide chairs are from wood or leather (IChairs). When she openes the shop, she decides to use wood chairs (Inject WoodChairs for IChairs).

Then Bob buys the shop from Alice. He can't change reading room (it's hard it will take time and reading room is just fine). But he wants leather chairs so he uses leather chairs (Inject LeatherChairs for IChairs).

Bob should stick Alice.Shop.Interfaces if he can't or doesn't want to change Reading Room.

But let's say. I like Alice Reading Room much. I want to design a reading room like hers. But I want to make my rules for Reading Room (IMyReadingRoom adapter, you get ReadingRoom class not Interface and you create your own interfaces).

In short: You should stick interfaces always. You can create your own Interface (Adapter) for 3rd party libraries. That's way you can extend or hide the rules without stick to 3rd party library (But you should stick with your own Interface anyway). You should write adapter for 3rd party library implemantation not for their interfaces.

If we skip adapter choice Bob has to use Alice.Shop.Interfaces for injecting.

这篇关于依赖注入接口的C#命名空间的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆