【问题标题】:dependency inversion for data class(classes that define the structure of certain type)数据类的依赖倒置(定义某种类型结构的类)
【发布时间】:2021-03-20 11:21:36
【问题描述】:

所以我有这个数据类:

    public class RecentProject
    {
    public string ProjectName { get; set; }

    public string ProjectPath { get; set; }

    public DateTime CreationDate { get; set; }

    public string OutputFolder { set; get; } = "";
    }

它只是定义了最近项目的属性,我想应用依赖倒置,所以我从类中提取了一个接口:

    public interface IRecentProject
   {
    DateTime CreationDate { get; set; }
    string OutputFolder { get; set; }
    string ProjectName { get; set; }
    string ProjectPath { get; set; }
    }

然后我做了一个ioc容器(控制反转)并将类类型注册为接口:

Mvx.IoCProvider.RegisterType<IRecentProject, RecentProject>();

所以当我想创建一个最近使用的项目时,在我的应用程序的任何地方:

Mvx.IoCProvider.Resolve<IRecentProject>();

但是在我这样做之后,我遇到了一些当前设置很难解决的问题,所以我认为这可能不是在此类中应用依赖倒置的正确方法,因为没有任何依赖倒置好处适用喜欢:

  • 单元测试:因为我不会对数据类进行单元测试
  • 更改类实现的能力:因为类中的任何更改都需要更改接口才能使用添加的新功能

那我该怎么办,我在这个话题上搜索了很多,没有找到明确的答案,
请提前帮助和感谢。

【问题讨论】:

  • 对于每个抽象,您都必须问自己附加值是什么。没有免费抽象这样的东西,所以不要仅仅因为你可以(或者因为你认为“其他人都在做”)而这样做。那么:为什么要在这里做呢?
  • 连接一个没有行为的类没有多大意义。类真的只是基本属性吗?
  • @JHBonarius 没有特别的理由我认为这是实现依赖倒置的方式“di 指出高级模块不应该依赖于低级模块;两者都应该依赖于抽象”,不是是吗?
  • @Crowcoder (尽管这可能会在未来发生变化)但目前是的,它只是基本属性
  • 好吧,我并没有真正看到这里的附加值。我可以想到这些纯属性类(模型?)具有接口的原因:如果您通过同一个接口共同访问多个不同的这些模型。 IE。你有class MyType1 : IIntclass MyType2:IInt,你可以使用void MyMethod(IInt object) 的通用方法。不过,您可以使用普通继承来实现这一简单。但是一个类只能有一个父级和多个接口,因此在更复杂的情况下这可能很重要。

标签: c# .net inversion-of-control dependency-inversion


【解决方案1】:

如果RecentProject 是一个纯数据类,即它不包含逻辑,那么确实不需要单元测试或抽象。当然,IoC 不在此范围内。

当涉及到逻辑和多态性时,事情看起来会有所不同。例如,您可能希望拥有两种类型的项目,每种项目都以自己(简单)的方式实现名称验证:

public class RecentCSharpProject : IRecentProject
{
    .
    .
    .
    public string ProjectName
    {
        get => this.projectName;
        set
        {
            if (!value.EndsWith("csproj"))
            {
                throw (new Exception("This is not a C# project"));
            }
            this.projectName = value;
        }
    }
}

public class RecentFSharpProject : IRecentProject
{
    .
    .
    .
    public string ProjectName
    {
        get => this.projectName;
        set
        {
            if (!value.EndsWith("fsproj"))
            {
                throw (new Exception("This is not an F# project"));
            }
            this.projectName = value;
        }
    }
}

您仍然可以选择跳过单元测试,而且 IoC 仍然无关紧要(要注入服务,而不是数据模型)。​​

但是,您现在可以实例化适当的类,同时仍能向外界“说”界面语言:

public IRecentProject AddProjectToFileMenu(string projectName, bool isFSharp)
{
    IRecentProject project = (isFSharp ? new RecentFSharpProject() : new RecentCSharpProject());
    project.ProjectName = projectName; // Internally validate extension according to concrete class
    // TODO: add project to file-menu

    return (project);
}

【讨论】:

  • 请注意,(AFAIK)getter 不应该抛出一个普遍的共识......(并且您忘记在两个类中添加 :IRecentProject
  • 你的意思是我假设的设置器......好吧,不确定共识部分,但我同意 SetProjectName 方法会更优雅。
  • 不。我说的是吸气剂。好吧,这更像是一场持续的辩论:getter 和 setter 的最初想法是访问封装的字段。因此,它们不应该有任何副作用。如果有人试图通过 getter 获取字段值,他们不会期望异常。这应该在之前处理(甚至在设置无效值的字段之前)
  • 我很抱歉,但我确实遗漏了一些东西.. 我的示例中的 getter 没有抛出异常,我很确定..
猜你喜欢
  • 2019-03-23
  • 2015-12-05
  • 2022-09-28
  • 1970-01-01
  • 1970-01-01
  • 2022-01-14
  • 2019-02-08
  • 1970-01-01
  • 2017-09-08
相关资源
最近更新 更多