我想在C#中使用Dependency Injection
模式,我想让命名空间中的逻辑尽可能分开。
消耗类的interface
应该在哪个命名空间中?
首先让我们做一些"正常"案件。书籍案例将作为第二部分解释的基础。然后,"现实生活"这个问题就出现了。
让我们假设编码器是Alice,并且她使用Alice
作为供应商在名称空间中作为顶级名称,以避免与其他编码器发生冲突。在这个例子中,我们假设世界上没有其他的Alices。
假设她创建了3个名称空间:
Alice.Invaders
- 一款应通过商店提供应用内购买的游戏。Alice.Shop
- 多场比赛的可重复使用的商店。Alice.Injector
- 可重复使用的服务管理器。我们假设Shop
项目有一个名为interface
的{{1}},它提供了IShopService
方法。
让我们假设Show()
有某种控制器,在某些用户操作中想要打开商店。
我们假设服务(如Invaders
)是Shop
的控制器通过ServiceManager
获得的。
Invaders
本身是一个没有依赖关系的独立项目,所以它不使用"使用"关键字:
Alice.Injector
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认为Bob可能会在某天做出更好的商店,所以在将Alice.Shop
分离到接口Shop
然后执行之后,她会准备她的类进行依赖注入这篇文章:https://msdn.microsoft.com/library/hh323705%28v=vs.100%29.aspx
为实现这一目标,Alice将使该商店成为与IShop
兼容的服务类型,因此Alice决定将Alice.ServiceManager
重命名为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
获得Shop
(采用服务的形式),因此它都是干净的代码。
ServiceManager
到目前为止,这一切都很顺利。
所以现在......鲍勃(爱丽丝的一位好朋友,众所周知,好奇他们今天没有谈论发送入门信息),这家超级好的商店是不是比那个更好的商店呢?爱丽丝做了。鲍勃从零开始做他的店铺。
所以Bob实现了与爱丽丝的注入器兼容的商店(因为Bob也使用using Alice.Injector
using Alice.Shop
namespace Alice.Invaders
{
public class DefaultController
{
void OnShopClick()
{
IShopService shop = ServiceManager.Get( "Shop" ) as IShopService;
shop.Show();
}
}
}
在他的项目中注入其他东西)。
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按照上面显示的方式在Bob.Shop
命名空间内进行购物,那么Alice 必须编辑她用于引用Bob.Shop
以获取Bob.Shop
接口的代码(丑陋的她必须更改代码中的依赖项,因为它应该使用依赖注入器来消除更改代码中的依赖项)。 / LI>
IShopService
,那也很难看,因为有很多事情可能会发生冲突。IShopService
界面的命名空间 - 如果Bob使用常识并说"我想要做的是创建商店兼容与Alice的一个,那么我应该实现HER接口"所以鲍勃的代码很可能就像这样: Bob使用Alice.Shop
向后命名空间兼容性的代码:
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.Shop.Shop
Alice.Shop.IShopService
在提供Alice.Injector.ServiceManager
时能够提供另一个IService
。这里依然存在依赖关系:
Bob.Shop.Shop
正在将Alice.Invaders
投射到Alice.Injector.IService
,以便能够调用Alice.Shop.IShopService
方法。如果你不进行演员表演,你就不能向商店展示"。
所以最后,你是"取决于"在那个演员表上因此而且#34;某人"需要为您提供界面定义。
如果原来的商店不是由爱丽丝写的,而是由查理写的,那将是"丑陋的"仍需下载并保留Show()
项目的副本才能使用Charlie.Shop
。
因此...
1)Bob.Shop
居住的正确名称空间是什么?
2)"替换"项目提供自己的界面或借用原来的界面?
3)原来的店铺应该分成两个项目吗? (比如示例IShopInterface
和Alice.Shop
所以Alice.ShopImplementation
是veeery slim并且只包含接口?也许Alice.Shop
和Alice.Shop
作为嵌套命名空间,但仍然是两个sepated代码库,以便您可以下载ans Alice.Shop.Implementation
而无需下载Alice.Shop
?
4)这是否像Bob在其项目中包含Alice.Shop.Implementation
文件的副本一样简单,因此不需要依赖项?非常难看 - 如果他这样做并且我们希望拥有2家商店并将用户送到一家或其他商店,那就会发生冲突。
感谢。
答案 0 :(得分:1)
接口,注入器和实现应该在不同的命名空间中。接口应该在Alice.Shop.Interfaces
中,并且在该命名空间中不应该有任何实现。您可以更改/隐藏实现,但您应该在依赖注入中坚持使用接口。
Alice.Invaders正在将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)。
然后鲍勃从爱丽丝那里买了这家店。他不能改变阅览室(这很难,需要时间,阅览室也很好)。但他想要皮椅,所以他使用皮椅(用于IChairs的Inject LeatherChairs)。 如果他不能或不想改变阅览室,鲍勃应该坚持Alice.Shop.Interfaces
。
但是,让我们说。我很喜欢爱丽丝阅览室。我想设计一个像她一样的阅览室。但是我想为阅览室制定规则(IMyReadingRoom适配器,你得到的是ReadingRoom类而不是接口,你创建了自己的接口)。
简而言之:您应该始终坚持接口。您可以为第三方库创建自己的界面(Adapter)。这样你可以扩展或隐藏规则而不必坚持使用第三方库(但你应该坚持使用自己的界面)。您应该为第三方库实现编写适配器而不是它们的接口。
如果我们跳过适配器选择,Bob必须使用Alice.Shop.Interfaces
进行注入。