Rhino Mocks、依赖注入和关注点分离

发布于 2024-08-25 16:21:56 字数 678 浏览 7 评论 0 原文

我是模拟和依赖注入的新手,需要一些指导。

我的应用程序使用典型的 N 层架构,其中 BLL 引用 DAL,UI 引用 BLL 但不引用 DAL。非常简单。

比方说,我有以下类:

class MyDataAccess : IMyDataAccess {}
class MyBusinessLogic {}

每个类都存在于单独的程序集中。

我想在 MyBusinessLogic 的测试中模拟 MyDataAccess。因此,我向 MyBusinessLogic 类添加了一个构造函数,以获取用于依赖项注入的 IMyDataAccess 参数。但现在,当我尝试在 UI 层上创建 MyBusinessLogic 实例时,它需要对 DAL 的引用。

我以为我可以在 MyBusinessLogic 上定义一个默认构造函数来设置默认的 IMyDataAccess 实现,但这不仅看起来像代码,而且实际上并没有解决问题。我仍然有一个签名中带有 IMyDataAccess 的公共构造函数。所以UI层仍然需要引用DAL才能编译。

我正在尝试的一种可能的解决方案是使用 IMyDataAccess 参数为 MyBusinessLogic 创建一个内部构造函数。然后我可以使用测试项目中的访问器来调用构造函数。但还是有那种味道。

这里常见的解决方案是什么。我一定是做错了什么。我该如何改进架构?

I am new to mocking and dependency injection and need some guidance.

My application is using a typical N-Tier architecture where the BLL references the DAL, and the UI references the BLL but not the DAL. Pretty straight forward.

Lets say, for example, I have the following classes:

class MyDataAccess : IMyDataAccess {}
class MyBusinessLogic {}

Each exists in a separate assembly.

I want to mock MyDataAccess in the tests for MyBusinessLogic. So I added a constructor to the MyBusinessLogic class to take an IMyDataAccess parameter for the dependency injection. But now when I try to create an instance of MyBusinessLogic on the UI layer it requires a reference to the DAL.

I thought I could define a default constructor on MyBusinessLogic to set a default IMyDataAccess implementation, but not only does this seem like a codesmell it didn't actually solve the problem. I'd still have a public constructor with IMyDataAccess in the signature. So the UI layer still requires a reference to the DAL in order to compile.

One possible solution I am toying with is to create an internal constructor for MyBusinessLogic with the IMyDataAccess parameter. Then I can use an Accessor from the test project to call the constructor. But there's still that smell.

What is the common solution here. I must just be doing something wrong. How could I improve the architecture?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

习惯成性 2024-09-01 16:21:56

您可以这样定义您的类:

public class MainForm : Form
{
    private readonly businessLogic;

    public MainForm(IBusinessLogic businessLogic)
    {
        this.businessLogic = businessLogic;
    }
}

public class BusinessLogic : IBusinessLogic
{
    private IDataLayer dataLayer;

    public BusinessLogic(IDataLayer dataLayer)
    {
        this.dataLayer = dataLayer;
    }
}

public class DataLayer : IDataLayer
{
    public DataLayer(string connectionString)
    {
    }
}

请注意主窗体如何不了解此处的 DAL。现在我们确实需要一段了解所有类的代码,以便可以将它们连接在一起。这通常在应用程序启动时完成:

public static void Main(string[] args)
{
   var dataLayer = new DataLayer("foo");
   var businessLogic = new BusinessLogic(dataLayer);
   var mainForm = new MainForm(businessLogic);

   Application.Run(mainForm);
}

当然,这是一个简化的示例。如果您在实践中拥有数十个或数百个类,那么此类启动连接可能会变得非常庞大且复杂,尤其是当循环依赖关系发挥作用时。这就是创建依赖注入框架来用 XML 配置文件、代码配置或 .NET 属性替换代码的原因。但基本思想是相同的。

.NET 依赖注入框架示例:AutoFac城堡春天.NETStructureMapNinject托管扩展性框架

You can define your classes like this:

public class MainForm : Form
{
    private readonly businessLogic;

    public MainForm(IBusinessLogic businessLogic)
    {
        this.businessLogic = businessLogic;
    }
}

public class BusinessLogic : IBusinessLogic
{
    private IDataLayer dataLayer;

    public BusinessLogic(IDataLayer dataLayer)
    {
        this.dataLayer = dataLayer;
    }
}

public class DataLayer : IDataLayer
{
    public DataLayer(string connectionString)
    {
    }
}

Note how the main form doesn't know about the DAL here. Now we do need a piece of code which knows all the classes, so that they can be wired together. This is typically done at the start of the application:

public static void Main(string[] args)
{
   var dataLayer = new DataLayer("foo");
   var businessLogic = new BusinessLogic(dataLayer);
   var mainForm = new MainForm(businessLogic);

   Application.Run(mainForm);
}

Of course, this is a simplified example. If you have dozens or hundreds of classes in practice, then such start-up wiring can get very big and complicated, especially when cyclic dependencies come into play. That's why dependency injection frameworks were created to replace that code by XML configuration files, configuration by code, or .NET attributes. The basic idea is the same though.

Examples of dependency injection frameworks for .NET: AutoFac, Castle, Spring.NET, StructureMap, Ninject and the Managed Extensibility Framework.

妥活 2024-09-01 16:21:56

回答了一个问题昨天介绍了如何构建.NET我认为该解决方案可以解决您的问题。

在我的回答中,您的关键点应该是没有一个“实现”程序集引用其他“实现”程序集。这应该可以解决您的关注点分离问题。

此外,该结构几乎还要求使用依赖注入,并且非常适合单元测试和模拟的使用。

我希望它有帮助。

I answered a question yesterday on how to structure a .NET solution that I think will solve your problem.

The key point for you in my answer should be that none of the "implementation" assemblies have references to other "implementation" assemblies. This should solve you separation of concerns issue.

Further, the structure also almost mandates the use of dependency injection and it is very well suited for unit testing and the use of mocking.

I hope it helps.

往日情怀 2024-09-01 16:21:56

如果您想避免从 UI dll 引用数据访问 dll,那么您可以将数据访问接口/基类提取到第三个库中,并让其他两个库都引用它。

If you want to avoid having to reference the data access dll from the UI dll, then you could extract the data access interfaces/base classes into a third library and have both the other two reference that.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文