【问题标题】:Database queries in OOP... design pattern?OOP 中的数据库查询...设计模式?
【发布时间】:2013-12-20 15:40:06
【问题描述】:

这是基本问题。假设我有一个基类 Foo。这个类调用数据库来获取一些数据缓存在类中。

public class Foo
{
    // Properties/fields go here.

    public virtual void ReadData()
    {
        // Queries the database for information and stores it in Foo.
    }
}

现在,假设我有一个名为 Bar 的派生类。这个类想要缓存一些额外的数据。

public class Bar : Foo
{
    // Additional properties/fields go here.

    public override void ReadData()
    {
        base.ReadData();

        // Queries the database for additional information and stores it in Bar.
    }
} 

现在,看看这个,如果您正在执行常规 OOP,这似乎是一件很常见的事情。但是,在这种情况下,您访问数据库两次,效率很低。我正在开发一个遗留代码库,该代码库在所有地方都进行了这样的实践。在下一个版本中,他们希望优化数据库访问(意味着减少对数据库的调用)。

是否存在适用于 OOP 的数据库访问设计模式,以便我可以最大限度地减少数据库上的查询量?

【问题讨论】:

  • 您的示例没有显示它两次访问数据库的位置/方式。恕我直言,仅凭 cmets 无法知道真正发生了什么,以及是否(以及如何)可以减少
  • 只是不要调用base.ReadData?但是做一个组合的 SQL 呢?
  • 我会研究组合的概念,其中 ReadData() 方法通过两种不同的实现被推入它自己的 IDataReader 接口。然后你可以互换使用 Foo 或 Bar 中的那些。此外,通常您希望方法尽可能小,这有利于这种方法。基本上,如果你能想象以两种或多种不同的方式做某事,它应该是一个接口而不是继承。
  • @Jason 用于查询数据库的 cmets 包括打开 SqlConnection、创建 SqlCommand、在命令上调用 ExecuteReader(),然后在 SqlDataReader 上调用 Read。因此,在 Foo.ReadData 和 Bar.ReadData 中,它们都在打开一个新连接并创建一个新命令、获取数据并存储它。
  • 我是否正确地假设使用 ORM(这会从这些类中完全删除 SQL 访问代码)不是一种选择?

标签: c# sql database oop design-patterns


【解决方案1】:

最简单的技巧是调用基类方法。相反,让继承的类使用专门的查询来一次性获取数据。

如果您的要求是减少查询次数,那么 oop 对此没有任何魔力。

那么,另一种选择是拥有一个缓存代理,这是处理缓存的常规设计模式。

【讨论】:

    【解决方案2】:

    如何构建一个表达式树来定义要获取的数据以及如何映射数据?

    父类的 ReadData() 将请求数据提取表达式并通过单个 SQL 命令进行提取。通过覆盖,子类可以根据需要添加、操作或替换父类的表达式。子类将不再需要直接查询数据库。

    编写引擎以将表达式树转换为 SQL 可能需要一些工作......

    在伪代码中: 基类

    public void ReadData() {
        var expression = DataExpression();
        var connection = // new db connection
    
        // translates the expression into SQL, executes the query, retrieves results, maps those results
        DataExpressionHelper::Process(expression, connection);
    }
    protected virtual SqlExpression DataExpression() {
        // Return an  expression tree like:
        //  From("BaseDataTable").Map("Name" => this.Name).Map("Age" => this.Age);
    }
    

    儿童班

    protected override SqlExpression DataExpression() {
        var expression = base.DataExpression();
        // Then, something to the effect of:
        //  expression += Join("ChildClassTable).On("parent.Id = child.ParentId).Map("Gender" => this.Gender);
    }
    

    【讨论】:

    • 我绝对喜欢这个主意。但是,它并不能解决我当前的问题,而且它确实不是“设计模式”。我会在产品的下一个版本中记住这种 API。 :-)
    【解决方案3】:

    这个问题的一般解决方法是use composition, rather than inheritance

    使这项工作最有效也可能意味着更改Foo 类型的设计,因此它不会直接缓存数据,而是仅提供有关您要缓存的内容的信息。然后另一种类型会查看FooBar 的实例,将它们想要缓存的内容组合在一起,并在一次调用中获取所有内容。

    这里的另一个问题是类对象直接与数据库对话通常是个坏主意。通常,您希望将一个对象或几个对象组合在一起以创建数据访问层。所有数据库访问都将通过这些对象。缓存将在更高级别进行。

    【讨论】:

    • 您的意思似乎不是“使用组合”。组合意味着对象之间的“is-a-part-of”关系,而继承是“is-a”关系。你所链接的绝对是一个使用组合的设计概念,但它不仅仅是“组合”。我喜欢他们的替代名称 Composite Reuse Principle,它正确地定义了这个概念。
    • 我喜欢这个想法,但不幸的是,我无法重新设计系统中的所有对象。对于新系统,我会牢记这个概念。这个概念似乎类似于依赖注入,除了不涉及 XML 或非代码外部文件。无论如何,感谢您提供的信息,但我认为它不会解决我目前的困境。
    • @MichaelYanni:依赖注入从不暗示了这些。这些是做到这一点的方法。就像您的问题的最佳解决方案一样,这称为策略模式。
    【解决方案4】:

    如果您的问题是(并且确实是)“是否有适用于 OOP 的数据库访问设计模式,以便我可以最大限度地减少数据库上的查询量?”

    答案是 - 没有设计模式可以最大限度地减少查询量。您的应用程序的总体设计可能会导致更多或更少的查询。

    看看这三件事:

    1. 延迟加载

    2. 缓存

    3. 对于执行查询的性能:如果您使用动态 Sql - Sql 查询的参数化将最大限度地减少 DB 服务器用于执行查询的资源

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-03-27
      • 1970-01-01
      • 2012-05-25
      • 2011-01-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多