【问题标题】:Inheritence vs Composition - An OOP Architectural Consideration继承与组合 - 在 OOP 架构方面的考虑
【发布时间】:2012-07-17 15:01:30
【问题描述】:

我无法找到一种干净的方式来实现我的分层。

这是我拥有的层(下层支持上层,或者通过继承 或作曲):

Business Logic Layer (BLL)
Datastore Layer (DSL)
Database Layer (DBL), Web-Service Buffer (WSB)

当 BLL 对象发出 get 请求 getRecords() 时,它反过来要求 DSL 完成它。

然后,DSL 决定天气是使用本地 DBL,还是使用 WSB(它与远程 Web 服务前端与“主”数据库通信)。


选项 1 - 组合(BLL 有一个 DSL,DSL 有一个 DBL)


我的问题是,由于 DSL 和 DBL 对象是在 BLL 中组成的,它们对包含 BLL 的内容一无所知,那么他们应该如何进行包含 BLL 字段的特定 DB 查询?


选项 2 - 继承(BLL:DSL,DSL:DBL)


即使较低层现在可以通过公共/受保护的继承访问 BLL,但关于 DSL 和 DBL 如何准确知道要生成哪些特定查询字符串的问题仍然存在。我认为 BLL 可以保留一组静态字符串,但这会产生双向依赖,我认为这是一个非常有缺陷的设计,会带来可怕的后果。

注意:每个 BLL 都有一个对应的表,用于序列化/反序列化。

注意:我不想使用反射,并且想限制泛型的使用(除非绝对没有其他方法。我正在为我的 WSB 使用泛型,它正在工作很好,虽然我主要关心的是在 DSL 和 DBL 层中生成特定于 BLL 的查询字符串)。

注意:将会有许多不同的 BLL 对象使用 DSL 和 DBL 层,而不仅仅是一个(否则这将是微不足道的)。

public class BL1 
{
  private DSL _dsLayer;

  public getRecords() 
  {
    // ...
    _dsLayer.getRecords();
    // ...
  }
}

public class DSL 
{
  private DBL _dbLayer;
  private WSB _wsBuffer;

  public getRecords() 
  {
    if(_dbLayer.getRecords() != null)
    {
      return records;
    }
    else
    {
      return _wsBuffer.getRecords();
    }
  }
}

public class DBL
{
  private string _db = "file.db3";

  public getRecords()
  {
    select ????? - how to know what fields to grab
  }
}

感谢您花时间回答这个问题,非常感谢。

【问题讨论】:

标签: c# oop design-patterns inheritance composition


【解决方案1】:

您应该使用组合。我认为继承没有意义,因为这些是完全不同类型的实体,实际上一个不能从另一个继承。他们将继承哪些领域?谁从谁那里继承?

BLL 需要传递“字段”列表才能到达 DSL。要么作为 DSL 方法的参数,要么以其他方式。 DSL 方法只是接收一些字段列表作为参数并使用它们。我认为这是一个可行的解决方案。

此外,您应该在每一层创建接口并针对它们进行编程,而不是使用类型本身。例如,在您编写的示例代码中,将 DBL 和 WSB 更改为 IDBL 和 IWSB。这将帮助您更好地测试并允许代码中的松散耦合。

public class DSL 
{
  private IDBL _dbLayer;
  private IWSB _wsBuffer;
....

}

【讨论】:

  • 是的,我猜你对传递给 DSL 的“字段列表”参数是正确的。出于某种原因,我认为这会创建一个依赖项,但它只是一个函数参数,就像其他任何权利一样?谢谢设计。
  • 传递参数不是依赖的原因或来源。您将使用其方法的类将成为您的容器类的依赖项(因此 DSL 是 BLL 等的依赖项)。要分离该依赖项,您应该创建一个接口,从中派生您的依赖类,并将调用类的相关成员声明为接口而不是依赖类型本身。答案中的 IDBL 和 IWSB 之类的。这是“依赖注入”原则的基本原则之一。
  • 你是说让 BLL 实现 IDBL (BLL : IDBL) 吗?如果是这样,我是否必须为每个 BLL 对象编写特定的 IDBL 方法实现(我会有很多,至少 10 个)?
  • ... 错误,别管最后一个问题的细节,但是这个概念仍然存在。 DSL/DBL我只想写一次,如果涉及到接口,是不是每一种BLL都要实现接口方法(有很多)?
  • 我想我知道你在说什么:public class DBL : IDBL {...}public class DSL { private IDBL _dbl; }
【解决方案2】:

通常,当您具有“Is-A”关系时,应使用继承。既然你不能说 BLL 'Is-A' DSL 或 DSL 'Is-A' DBL,那么我会考虑组合而不是继承。这具有使测试每个逻辑层更容易的副作用,因为您可以存根或模拟每个依赖项。

通常,在任何公开的 API 中,服务器端(因此 DSL 在 BLL -> DSL 方面)需要公开对象才能完成工作。您指出 DSL 不应该知道 BLL 对象是正确的。因此,挑战在于为 DSL 编写一个干净的 API,该 API 暴露给 BLL 以查询数据。

我建议同时查看DDD 中的RepositorySpecification 模式。这些有助于解决您提出的一些问题。

【讨论】:

  • 非常有用的见解,我喜欢概括,所以感谢链接(我也有 Fowler 的书,所以我一定会检查那里的存储库模式)。跨度>
  • 问题:在编写 API 时,所有方法通常是静态的,还是通常涉及实例变量(即 BLL 中是否有显式的 DSL 成员,或者只是调用静态DSL API 的?
  • 实例与静态并不真正相关,恕我直言。想想框架中存在的所有出色的 .NET API。其中许多要求您在使用之前创建对象的新实例。通常,我回避静态组合,因为它使事情更难测试(因为它需要组件之间更紧密的耦合)。作为一个建议,查看依赖注入可能对您来说很有趣。
【解决方案3】:

作文。 因为 dtryon 和 desigeek 说过的所有内容以及
因为在您的情况下,继承看起来不自然 + 它会使您的所有
层紧密耦合,几乎不会限制对源代码的任何修改。

我相信查看prefer-composition-over-inheritance SO-topic 会有所帮助。

【讨论】:

  • 谢谢,链接也很棒。好东西。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-05-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多