将接口用于数据类型是否是反模式?


9

假设我的模型中有多个实体(使用EF),例如用户,产品,发票和订单。

我正在编写一个用户控件,该控件可以在我的应用程序中打印实体对象的摘要,其中这些实体属于预先确定的集合,在这种情况下,我说可以概括用户和产品的摘要。

这些摘要都只有一个ID和一个描述,因此我为此创建了一个简单的接口:

 public interface ISummarizableEntity {     
       public string ID { get; }    
       public string Description { get; } 
 }

然后,对于有问题的实体,我创建一个实现此接口的局部类:

public partial class User : ISummarizableEntity
{
    public string ID
    {
        get{ return UserID.ToString(); }
    }

    public string Description 
    {
        get{ return String.Format("{0} {1} is from {2} and is {3} years old", FirstName, LastName, Country, Age); }
    }
}

public partial class Product: ISummarizableEntity
{
    public string ID
    {
        get{ return ProductID.ToString(); }
    }

    public string Description 
    {
        get{ return String.Format("{0} weighs {1}{2} and belongs in the {3} department", ProductName, WeightValue, WeightUnit, Department); }
    }
}

这样,我的用户控件/局部视图就可以绑定到ISummarizableEntity的任何集合,而根本不需要对源代码感兴趣。有人告诉我,不应将接口用作数据类型,但是我没有得到更多的信息。据我所知,尽管接口通常描述行为,但仅使用属性本身并不是反模式,因为属性无论如何都是吸气剂/设定器的语法糖。

我可以创建一个具体的数据类型,并从实体映射到该数据类型,但看不到好处。我可以使实体对象从抽象类继承,然后定义属性,但是由于我们不能有多个继承,因此我将实体锁定为不再使用。我也愿意让任何对象成为ISummarizableEntity(显然,我将重命名接口)

我脑海中使用的解决方案是可维护,可扩展,可测试且相当可靠的。您可以在这里看到反模式吗?


您有什么理由比拥有EntitySummaryUserProduct具有每种方法都更喜欢public EntitySummary GetSummary()吗?
Ben Aaronson

@Ben,您建议一个有效的选项。但这仍然需要接口的定义,以便调用者知道他们可以期望对象具有GetSummary()方法。本质上是相同的设计,在实现中增加了模块化程度。如果摘要需要独立于其来源(但是要简短地),则可能是一个好主意。
肯特A.15年

@KentAnderson我同意。取决于这些类的整体接口以及如何使用摘要,实际上可能不是一个好主意。
本·亚伦森2015年

Answers:


17

接口不描述行为。有时恰恰相反。

接口描述合同,例如“如果我要向接受ISummarizableEntity的任何方法提供此对象,则此对象必须是能够自我汇总的实体” –在您的情况下,定义为能够返回a字符串ID和字符串说明。

这是接口的完美使用。这里没有反模式。


2
“接口不能描述行为。” “总结自身”如何不是行为?
Doval 2015年

2
从OO纯粹主义者的观点来看,@ThomasStringer继承表示一个共同的祖先(例如,正方形圆形都是形状)。在OP的示例中,用户和产品没有任何合理的共同血统。在这种情况下,继承将是明确的反模式。
肯特(Kent A.)

2
@Doval:我猜接口的名称可能描述了预期的行为。但这不是必须的。该接口可以同等命名为IHasIdAndDescription,答案将相同。接口本身并不描述行为,而是描述期望。
pdr 2015年

2
@pdr如果通过耳机插孔发送20V,则会发生不良情况。形状不够;对于通过该插头传输什么样的信号有非常真实和非常重要的期望。这就是为什么假装接口没有附加行为规范是错误的原因。如果您的List行为不像列表,该怎么办?
2015年

3
具有适当接口的电源插头可以插入插座,但这并不意味着它将导电(所需的行为)。
JeffO

5

您为该设计选择了更好的路径,因为您正在定义多种不同类型的对象所需的特定行为类型。在这种情况下,继承将暗含实际上并不存在的类之间的公共关系。在这种情况下,可组合性优于继承。


3
接口与继承无关。
DougM 2015年

1
@DougM,也许我说的不好,但是我很确定我们同意。
肯特A.15年

1

避免使用仅带有属性的接口,因为:

  • 它混淆了意图:您只需要一个数据容器
  • 它鼓励继承:某人将来会混合考虑的可能性
  • 它防止序列化

在这里,您混合了两个问题:

  • 摘要作为数据
  • 作为合同的摘要

摘要由两个字符串组成:一个id和一个描述。这是纯数据:

public class Summary {
    private readonly string id;
    private readonly string description;
    public Summary(string id, string description) {
        this.id = id;
        this.description = description;
    }
    public string Id { get { return id; } }
    public string Description { get { return description; } }
}

现在,您已经定义了要定义合同的摘要:

public interface ISummarizableEntity {
    public Summary GenerateSummary();
}

请注意,在吸气剂中使用智能功能是一种反模式,应避免使用它:应该将其放在函数中。这是实现的样子:

public partial class User : ISummarizableEntity {
    public Summary GenerateSummary() {
        var id = UserID.ToString();
        var description = String.Format("{0} {1} is from {2} and is {3} years old", FirstName, LastName, Country, Age);
        return new Summary(id,description);
    }
}

public partial class Product : ISummarizableEntity {
    public Summary GenerateSummary() {
        var id = ProductID.ToString();
        var description = String.Format("{0} weighs {1}{2} and belongs in the {3} department", ProductName, WeightValue, WeightUnit, Department);
        return new Summary(id,description);
    }
}

我不同意“应该避免仅带有属性的接口”。提供您为什么会这样的推理。
欣快'16

你是对的,我加了一些细节
万纳
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.