依赖注入中接口的 C# 命名空间
C# namespacing for interfaces in dependency injection
我想在 C# 中使用 Dependency Injection
模式,并且我希望在命名空间中尽可能分离逻辑。
问题
消耗的class的interface
应该在哪个命名空间?
提问的动机
首先让我们做一些"normal"案例。一个书柜,用作第二部分解释的基础。那么,"real-life"的情况下,问题就来了。
书柜
让我们假设编码员是 Alice,她使用 Alice
作为名称空间中的顶级名称作为供应商,以避免与其他编码员发生任何冲突。对于这个例子,我们假设世界上没有其他爱丽丝。
我们假设她创建了 3 个命名空间:
Alice.Invaders
- 应通过商店提供应用内购买的游戏。
Alice.Shop
- 多个游戏可重复使用的商店。
Alice.Injector
- 可重复使用的服务管理器。
假设 Shop
项目有一个名为 IShopService
的 interface
,它提供了一个方法 Show()
.
让我们假设 Invaders
有某种控制器,在某些用户操作中想要打开商店。
让我们假设 Shop
等服务是由 Invaders
的控制器通过 ServiceManager
获得的。
Alice.Injector
Alice.Injector
本身是一个独立的项目,没有依赖关系,所以没有使用"using"关键字:
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
Alice.Shop
也是一个独立的项目(除了它使用服务的情况)不知道注入器的存在。只是一家商店而已。
由于 Alice 认为 Bob 可能有一天会开一些更好的店,她准备 class 进行依赖注入,然后将 Shop
分离到接口 IShop
然后是实施,遵循这篇文章:https://msdn.microsoft.com/library/hh323705%28v=vs.100%29.aspx
为了实现这一点,Alice 将使商店成为一种与 Alice.ServiceManager
兼容的服务,因此 Alice 决定将 IShop
重命名为 IShopService
并且它将是一种 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
最后,Alice 编写了游戏代码。 Alice.Invaders
的代码通过 ServiceManager
得到一个 Shop
(采用服务的形式),所以它都是干净的代码。
using Alice.Injector
using Alice.Shop
namespace Alice.Invaders
{
public class DefaultController
{
void OnShopClick()
{
IShopService shop = ServiceManager.Get( "Shop" ) as IShopService;
shop.Show();
}
}
}
到目前为止,这一切都很好。
真实案例
所以现在... Bob(Alice 的好朋友,众所周知,好奇他们今天不谈论发送加密消息),做了一家超级好的商店,甚至比那个更好爱丽丝做到了。 Bob 从头开始经营他的商店。
所以 Bob 实现了与 Alice 的注入器兼容的商店(因为 Bob 也使用 Alice.Injector
在他的项目中注入其他东西)。
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.
}
}
}
所以...这就是奇怪的情况!!
Bob.Shop
接口的命名空间 - 如果 Bob 按照上面显示的方式在 Bob.Shop
命名空间内进行操作,那么 Alice 必须 编辑她的代码以引用 Bob.Shop
以获得 IShopService
接口(丑陋的是她必须更改代码中的依赖项,因为它应该使用依赖注入器来摆脱更改代码中的依赖项)。
- 接口没有命名空间 - 如果 Alice 和 Bob 都在全局命名空间中设置
IShopService
,那也很难看,因为有很多东西可以那里有冲突。
Alice.Shop
接口的命名空间 - 如果 Bob 利用常识说 "What I want to do is to create a shop compatible with Alice's one, then I should implement HER interface" 那么 Bob 的代码很可能会像这样:
Bob 的代码使用 Alice.Shop
向后命名空间兼容性:
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.
}
}
}
在这种情况下,似乎一切就绪:
- Bob 可以创建实现
Alice.Shop.IShopService
的 Bob.Shop.Shop
- Alice 不需要更改一行代码。
Alice.Injector.ServiceManager
在服务 Bob.Shop.Shop
时能够提供另一个 IService
。
问题
这里还是有依赖:
Alice.Invaders
正在将 Alice.Injector.IService
转换为 Alice.Shop.IShopService
以便能够调用 Show()
方法。如果你不做那个演员,你不能 "show the shop".
所以最后,您是 "depending" 演员,因此 "someone" 需要为您提供接口定义。
如果原来的商店不是爱丽丝写的,而是查理写的,那么"ugly"仍然需要下载并保留Charlie.Shop
项目的副本才能使用Bob.Shop
.
所以...
问题
1) IShopInterface
的正确命名空间是什么?
2) "replacement" 项目是提供自己的接口还是借用原来的接口?
3) 是否应该将原来的商店分成两个项目? (比如 Alice.Shop
和 Alice.ShopImplementation
所以 Alice.Shop
很苗条,只包含接口?也许 Alice.Shop
和 Alice.Shop.Implementation
作为嵌套的命名空间,但仍然有两个分离的代码库,因此您可以下载 ans install Alice.Shop
而无需下载 Alice.Shop.Implementation
?
4) 这是否像 Bob 在他的项目中包含 Alice.Shop.IShopInterface
文件的副本一样简单,因此不需要依赖项?非常丑陋 - 如果他这样做,而我们想要拥有 2 家商店并将用户发送到一个或其他商店,那将发生冲突。
谢谢。
接口、注入器和实现应该在不同的命名空间中。接口应该在 Alice.Shop.Interfaces
中,并且在此命名空间中不应有任何实现。您可以 change/hide 实现,但您应该坚持使用依赖注入中的接口。
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".
您的 DefaultController 实现不好。如果我想使用它,我对我需要哪些服务一无所知。它对我说,我现在不需要任何东西。
你应该使用构造函数注入。
public class DefaultController
{
private readonly IShopService _shopService;
DefaultController(IShopService shopService)
{
_shopService=shopService;
}
void OnShopClick()
{
_shopService.Show();
}
}
如果我需要 defaultcontroller,我会知道这个实现需要哪些服务。而且你不需要施法。
编辑:
假设爱丽丝有一家商店。她说我想要一间有 5 把椅子的阅览室。但她会决定椅子是用木头还是皮革制成的 (IChairs)。当她开店时,她决定使用木椅(Inject WoodChairs for IChairs)。
然后鲍勃从爱丽丝那里买下了这家商店。他不能改变阅览室(这很难,需要时间,阅览室很好)。但是他想要皮椅,所以他用了皮椅(Inject LeatherChairs for IChairs)。
如果 Bob 不能或不想更改阅览室,他应该坚持 Alice.Shop.Interfaces
。
但是说吧。我很喜欢爱丽丝阅览室。我想设计一个像她一样的阅览室。但我想为阅览室制定规则(IMyReadingRoom 适配器,你得到的是 ReadingRoom class 而不是界面,你可以创建自己的界面)。
简而言之:你应该始终坚持接口。您可以为第 3 方库创建自己的界面 (Adapter)。这样您就可以扩展或隐藏规则而无需坚持使用第 3 方库(但无论如何您都应该坚持使用自己的界面)。您应该为第 3 方库实现而不是为它们的接口编写适配器。
如果我们跳过适配器选择,Bob 必须使用 Alice.Shop.Interfaces
进行注入。
我想在 C# 中使用 Dependency Injection
模式,并且我希望在命名空间中尽可能分离逻辑。
问题
消耗的class的interface
应该在哪个命名空间?
提问的动机
首先让我们做一些"normal"案例。一个书柜,用作第二部分解释的基础。那么,"real-life"的情况下,问题就来了。
书柜
让我们假设编码员是 Alice,她使用 Alice
作为名称空间中的顶级名称作为供应商,以避免与其他编码员发生任何冲突。对于这个例子,我们假设世界上没有其他爱丽丝。
我们假设她创建了 3 个命名空间:
Alice.Invaders
- 应通过商店提供应用内购买的游戏。Alice.Shop
- 多个游戏可重复使用的商店。Alice.Injector
- 可重复使用的服务管理器。
假设 Shop
项目有一个名为 IShopService
的 interface
,它提供了一个方法 Show()
.
让我们假设 Invaders
有某种控制器,在某些用户操作中想要打开商店。
让我们假设 Shop
等服务是由 Invaders
的控制器通过 ServiceManager
获得的。
Alice.Injector
Alice.Injector
本身是一个独立的项目,没有依赖关系,所以没有使用"using"关键字:
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
Alice.Shop
也是一个独立的项目(除了它使用服务的情况)不知道注入器的存在。只是一家商店而已。
由于 Alice 认为 Bob 可能有一天会开一些更好的店,她准备 class 进行依赖注入,然后将 Shop
分离到接口 IShop
然后是实施,遵循这篇文章:https://msdn.microsoft.com/library/hh323705%28v=vs.100%29.aspx
为了实现这一点,Alice 将使商店成为一种与 Alice.ServiceManager
兼容的服务,因此 Alice 决定将 IShop
重命名为 IShopService
并且它将是一种 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
最后,Alice 编写了游戏代码。 Alice.Invaders
的代码通过 ServiceManager
得到一个 Shop
(采用服务的形式),所以它都是干净的代码。
using Alice.Injector
using Alice.Shop
namespace Alice.Invaders
{
public class DefaultController
{
void OnShopClick()
{
IShopService shop = ServiceManager.Get( "Shop" ) as IShopService;
shop.Show();
}
}
}
到目前为止,这一切都很好。
真实案例
所以现在... Bob(Alice 的好朋友,众所周知,好奇他们今天不谈论发送加密消息),做了一家超级好的商店,甚至比那个更好爱丽丝做到了。 Bob 从头开始经营他的商店。
所以 Bob 实现了与 Alice 的注入器兼容的商店(因为 Bob 也使用 Alice.Injector
在他的项目中注入其他东西)。
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.
}
}
}
所以...这就是奇怪的情况!!
Bob.Shop
接口的命名空间 - 如果 Bob 按照上面显示的方式在Bob.Shop
命名空间内进行操作,那么 Alice 必须 编辑她的代码以引用Bob.Shop
以获得IShopService
接口(丑陋的是她必须更改代码中的依赖项,因为它应该使用依赖注入器来摆脱更改代码中的依赖项)。- 接口没有命名空间 - 如果 Alice 和 Bob 都在全局命名空间中设置
IShopService
,那也很难看,因为有很多东西可以那里有冲突。 Alice.Shop
接口的命名空间 - 如果 Bob 利用常识说 "What I want to do is to create a shop compatible with Alice's one, then I should implement HER interface" 那么 Bob 的代码很可能会像这样:
Bob 的代码使用 Alice.Shop
向后命名空间兼容性:
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.
}
}
}
在这种情况下,似乎一切就绪:
- Bob 可以创建实现
Alice.Shop.IShopService
的 - Alice 不需要更改一行代码。
Alice.Injector.ServiceManager
在服务Bob.Shop.Shop
时能够提供另一个IService
。
Bob.Shop.Shop
问题
这里还是有依赖:
Alice.Invaders
正在将 Alice.Injector.IService
转换为 Alice.Shop.IShopService
以便能够调用 Show()
方法。如果你不做那个演员,你不能 "show the shop".
所以最后,您是 "depending" 演员,因此 "someone" 需要为您提供接口定义。
如果原来的商店不是爱丽丝写的,而是查理写的,那么"ugly"仍然需要下载并保留Charlie.Shop
项目的副本才能使用Bob.Shop
.
所以...
问题
1) IShopInterface
的正确命名空间是什么?
2) "replacement" 项目是提供自己的接口还是借用原来的接口?
3) 是否应该将原来的商店分成两个项目? (比如 Alice.Shop
和 Alice.ShopImplementation
所以 Alice.Shop
很苗条,只包含接口?也许 Alice.Shop
和 Alice.Shop.Implementation
作为嵌套的命名空间,但仍然有两个分离的代码库,因此您可以下载 ans install Alice.Shop
而无需下载 Alice.Shop.Implementation
?
4) 这是否像 Bob 在他的项目中包含 Alice.Shop.IShopInterface
文件的副本一样简单,因此不需要依赖项?非常丑陋 - 如果他这样做,而我们想要拥有 2 家商店并将用户发送到一个或其他商店,那将发生冲突。
谢谢。
接口、注入器和实现应该在不同的命名空间中。接口应该在 Alice.Shop.Interfaces
中,并且在此命名空间中不应有任何实现。您可以 change/hide 实现,但您应该坚持使用依赖注入中的接口。
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".
您的 DefaultController 实现不好。如果我想使用它,我对我需要哪些服务一无所知。它对我说,我现在不需要任何东西。
你应该使用构造函数注入。
public class DefaultController
{
private readonly IShopService _shopService;
DefaultController(IShopService shopService)
{
_shopService=shopService;
}
void OnShopClick()
{
_shopService.Show();
}
}
如果我需要 defaultcontroller,我会知道这个实现需要哪些服务。而且你不需要施法。
编辑:
假设爱丽丝有一家商店。她说我想要一间有 5 把椅子的阅览室。但她会决定椅子是用木头还是皮革制成的 (IChairs)。当她开店时,她决定使用木椅(Inject WoodChairs for IChairs)。
然后鲍勃从爱丽丝那里买下了这家商店。他不能改变阅览室(这很难,需要时间,阅览室很好)。但是他想要皮椅,所以他用了皮椅(Inject LeatherChairs for IChairs)。
如果 Bob 不能或不想更改阅览室,他应该坚持 Alice.Shop.Interfaces
。
但是说吧。我很喜欢爱丽丝阅览室。我想设计一个像她一样的阅览室。但我想为阅览室制定规则(IMyReadingRoom 适配器,你得到的是 ReadingRoom class 而不是界面,你可以创建自己的界面)。
简而言之:你应该始终坚持接口。您可以为第 3 方库创建自己的界面 (Adapter)。这样您就可以扩展或隐藏规则而无需坚持使用第 3 方库(但无论如何您都应该坚持使用自己的界面)。您应该为第 3 方库实现而不是为它们的接口编写适配器。
如果我们跳过适配器选择,Bob 必须使用 Alice.Shop.Interfaces
进行注入。