【问题标题】:Should DTO models be used as parameters when calling methods of an interface contract? [closed]调用接口合约的方法时,是否应该使用 DTO 模型作为参数? [关闭]
【发布时间】:2017-05-09 09:55:33
【问题描述】:

我和我的团队正在讨论是否使用 DTO 模型作为接口契约的方法参数。请记住,我们正在使用一个接口,因此我们可以使用依赖反转并将其模拟为单元测试的依赖。

public interface IContract {
    object Method1(DTOModel model);
}

public class Implementation1 {
    public object Method1(DTOModel model) {
        //do stuff with Property1 and Property2
    }
}

public class Implementation2 {
    public object Method1(DTOModel model) {
       //do stuff with Property3 and Property4
    }
}

public class DTOModel {
    public string Property1 {get;set;}
    public string Property2 {get;set;}
    public string Property3 {get;set;}
    public string Property4 {get;set;}
}

场景是这样的:我们试图在我们的实现中保持灵活性,以便我们可以同时使用它们。我不同意这种方法,除非两种实现都需要合同中的相同信息。在我看来,这将是一个单独的合同/实施,或者当前合同必须更改,并且只能同时使用一个实施。

我的想法是,如果您以这种方式使用 DTO,那么您将通过允许两个不同的实现根据设置的属性表现出不同的行为来违反合同。这也意味着消费者必须知道正在使用哪个实现才能知道完成操作所需的属性。最后,这意味着两个实现都可以访问彼此不需要的属性。

我解决此问题的方法是将DTOModel 的属性传递给实现,并使Implementation2 遵守该合同:

public object Method1(string property1, string property2) {
    //do stuff with property1 and property2
}

问题是:如果知道您的实现不依赖于整个合同和/或依赖合同的不同部分来做不同的事情,是否可以像上面那样传递 DTO?或者合同是否应该明确说明实现需要做什么?

提前致谢。

更新: 澄清一下,这两种实现都有相同的目的,但在不同的提供者上运行。例如,我们可能使用 2 个会员提供者,一个提供者需要 2 个特定属性,而另一个需要 2 个不同的属性。它们的目的是相同的,因为它们都将验证用户,但是每个实现的方式不同。

【问题讨论】:

  • 如果方法不需要需要整个 DTO 对象,那么它不应该需要整个 DTO 对象。您总是希望尽可能避免紧耦合。仅仅因为该方法恰好具有该方法所需的信息而要求该方法使用一个不相关的 DTO 对象似乎是错误的。

标签: c# .net interface architecture


【解决方案1】:

如果一个方法只需要几个参数,直接将它们作为参数传递。如果它需要三个或四个以上的参数,那么重构它以使用 DTO 可能不是一个坏主意。

但是,如果您走这条路,请创建特定于这些操作的 DTO 类(不要到处共享一个庞大、凌乱的类)。如果您的实现根本不同以至于它们不能共享相同的输入参数,我会回到绘图板并检查它们是否应该共享相同的接口。此外,重要的是,不要传递您在任何其他层(传输、UI 等)中使用的相同 DTO——这会导致不应该存在的令人讨厌的耦合。

【讨论】:

  • 澄清一下,两种实现都会做类似的事情,比如通过会员提供商登录某人。不同之处在于它们是 2 个会员提供者,每个会员提供者需要一组不同的值来进行身份验证,比如 impl1 使用用户名和密码,impl2 使用某种签名密钥或其他东西。可能是一个不好的例子,但这就是我们正在讨论的问题。
  • 在这种情况下,我可能会考虑创建一个“AuthenticationProvider”类(或类似的东西),它可以针对不同的目的进行扩展——例如UserCredentialAuthenticationProvider 和 SignedKeyAuthenticationProvider。然后,您的接口方法可以简单地接受 AuthenticationProvider 并且每个实现都可以将其转换为适当的类型(希望在传递错误类型的情况下抛出一个很好的有意义的异常)。
【解决方案2】:

嗯,我认为这种方法会破坏 Liskov 原则。

可替换性是面向对象编程中的一项原则,它指出,在计算机程序中,如果 S 是 T 的子类型,则类型 T 的对象可以替换为类型 S 的对象(即类型 T 的对象可以在不改变 T 的任何理想属性(正确性、执行的任务等)的情况下,用子类型 S 的任何对象替换。

在您的示例中,如果您将 Implementation1 更改为 Implementation2,则将不再满足此原则。

【讨论】:

  • 这两个实现仍然会被合约满足并且仍然可以轻松交换,唯一的问题是挂在 DTO 上的多余属性。我觉得它也违反了单一职责规则,因为你有 1 个对象服务于 2 个目的。
  • 如果程序在您更改实现时表现相同,那么很好,但我很怀疑,因为您更改了 DTO 对象的两个不同属性。是的,同意单一责任。这对我来说绝对是代码味道。
  • 我已将问题更新为更具体一点,以防它改变您的答案。
【解决方案3】:

我个人同意你的分析。我会传递 DTO 的属性。我不确定通过 DTO 是否违反了特定的主体或模式 - 可能是,它有一点代码味道,但我的问题是它模棱两可,并且实施的人不清楚意图未来的合同。

假设有人出现并实现了 Implementation3,他们得到了这个 DTO。他们拥有所有这些属性,他们不确定什么是有用的,什么不是。此外,随着需求的增长和变化,DTO 肯定会增长和变化。在不知不觉中,DTO 没有 4 个属性。也许它有 8 或 10 个属性,因为每个构建实现的人对参数的要求可能略有不同。您最终可能会遇到有人实现合同并获得一个作为参数传递的具有 8 个无用属性的对象。

我要么传入对给定任何实现的方法明确有用的特定属性,要么创建多个 DTO 并传入任何相关的。在这种情况下,DTO 本身的合同可能是一种选择。如果每种方法的要求不同,请创建单独的方法。

【讨论】:

  • 我完全同意。使用 DTO 的目的似乎听起来很灵活,但其含义让我感到害怕。我们需要一些外部输入。
  • 我已将问题更新为更具体一点,以防它改变您的答案。
  • 我需要更多的上下文/代码来进一步讨论,但是当你说 2 个不同的提供者来验证需要 2 个属性的用户时 - 我们是否像 EmailAuthProvider、TokenAuthProvider、UsernameAuthProvider 一样使用电子邮件、用户名、令牌,密码等?也许使用 1 个提供者 - 使用显式方法。那么 AuthenticationProvider 与 AuthByUsername(uname, pw), AuthByEmail(email, pw), AuthByToken(token)?如果需要其他实现,您仍然可以使用合同。如果特定提供程序不支持特定类型的身份验证,则引发异常,以便开发人员知道。
猜你喜欢
  • 2011-04-01
  • 2019-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-13
  • 1970-01-01
  • 2023-04-05
  • 2017-07-08
相关资源
最近更新 更多