问题描述
我想在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
然后执行,按照这篇文章:
为了实现这一点,爱丽丝将使商店成为与 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
中,并且在这个命名空间中不应该有任何实现。您可以更改/隐藏实现,但是您应该遵循依赖注入的接口。
您的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 theBob.Shop
namespace the way displayed above, then Alice must edit her code to referenceBob.Shop
to get theIShopService
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 theAlice.Shop.IShopService
- Alice does not need to change a single line of code.
- The
Alice.Injector.ServiceManager
is able to provide anotherIService
when serving theBob.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.
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#命名空间的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!