3

我正在创建一个CommonDaoOperations包含几个通用方法的实用程序类:Create, Update, Delete. 这不是基类,因为一些 DAO 更复杂,不能使用这些泛型方法,但许多 DAO 可以。

我现在正在考虑该实用程序类应该是什么样子:

  • 只有静态泛型方法的静态类
  • 具有通用方法的常规类,每个 DAO 作为私有只读成员创建一次
  • 具有泛型方法的常规类,每个 DAO 方法创建一次(在每次调用中)

为每个 DAO / 方法创建一个类的实例显然比调用静态方法的成本更高,但我很确定这些成本在几乎任何应用程序中都可以忽略不计。

我倾向于解决方案 2 或 3,因为非静态类的好处(接口,可以模拟,可以派生/增强,如果有必要,将来可以通过构造函数收集参数(与 10 参数相比)静态类中的方法))。

所以我想真正的问题是:我应该将我的实用程序类创建为成员变量,还是按照 DAO 方法实例化它?

public void Create(User user) { 
   new CommonDaoOperations().Create(user);
}
public void Delete(User user) {
   var daoOps = new CommonDaoOperations();
   daoOps.CheckSomething(); // just an example of multiple calls to the class
   daoOps.Delete(user);
}

我很想听听其他开发人员对这些方法的看法,或者是否还有另一种/更好的方法来做到这一点。

编辑

刚刚意识到我应该更多地考虑方法#3 - 正如 Vadim 指出的那样,在每个方法中实例化具体类时替换具体类会很麻烦,但我可以在属性中考虑到这一点:

private CommonDaoOperations DaoOps {
    get { return new CommonDaoOperations(); }
}
public void Create(User user) {
   DaoOps.Create(user);
}

我相信这比上面的代码片段更易于维护,但是知道我在我的 DAO 中为“实用程序”类引入了一个属性,这本身可能是一种代码味道(正如 Ant P 指出的那样)。

概括

这是一个艰难的决定——虽然我接受了 Ant P 的回答,但 Vadim 的回答也是合理的。使用哪种方法取决于实用程序类,所有 3 种方法都有其用途(更新的 #3 除外)。至少这是我对提供的答案的看法。

  • 静态类确实有它们的用途,但也有许多上面简要提到的缺点。
  • 常规类,按方法实例化:实用程序类在需要的地方创建和使用。减少依赖,保持你的类型纯净。
  • 常规类,实例化为成员:当许多/所有方法需要实用程序类的实例时,创建成员变量可能是一个更好的主意。通过这种方式更改类型或实例化方式变得更加容易。
4

3 回答 3

2

除非您计划创建大量此类对象,否则我认为这不会影响性能。

我更喜欢(2)。只需要为每次使用创建它,这只是白写代码。此外,如果您想使用某种 IOC,获取实用程序类作为参数,更改其初始化方式或简单地将类更改为另一个类 - 更改单个成员比更改简单得多改变所有使用它的地方。

除非您有充分的理由,否则请远离静态或单例。(一个很好的例子是开发一个插件或插件,你不能控制你的类的初始化和使用方式)。

于 2013-07-26T07:25:50.870 回答
2

我会让那些更有资格的人对性能影响发表评论;但是,这是我对每一个的想法:

1.静态类

这个概念适用于不需要真正可扩展性的简单、“不全面”的实用程序方法,但是 - 正如您自己注意到的那样 - 您的常见 DAO 操作将变得更加复杂。这不太可能作为单个静态类非常易于管理,特别是当它用于多种不同类型的 DAO 时。

2.具体类,实例化per-DAO对象

这一切都很好,但你真的需要实用程序类成为单个 DAO 的成员吗?如果您在 DAO 的整个生命周期内需要实用程序类中的某种一致性或状态持久性,我可以理解这一点,但这些方法似乎相当模糊(正如其名称一样,它是一个“实用程序”类)。

剩下3. 具体类,按方法实例化。这对我来说似乎是最自然的解决方案。正如您在问题中承认的那样,这使您可以灵活地利用具体类的所有优点,同时将对象的范围限制在需要它的地方 - 单个方法调用。

如果你的类演变成整个 DAO 所需的东西,例如你突然需要维护对象的状态(或者如果你需要将它注入 DAO 的构造函数,或者类似的其他东西),你总是可以改变 where它已实例化(尽管在我看来,如果发生这种情况,您实际上不再有实用程序类,您需要重新考虑该类如何适合您的体系结构)。

于 2013-07-26T07:26:10.553 回答
1

在考虑静态类具体类之间的区别和用法时,一定要考虑到一些含义,例如查看可测试性(但这一点都不太确定,如下所示),但首先有一些假设做:

  • 实例类有状态,管理状态,行为与它的内部状态有关,如果操作在某些方面与内部状态无关,这些都是静态方法的真正候选者,但我会在后面说更多。这甚至是 封装的基础,并且与SRP(单一职责原则)密切相关,它说一个类应该有一个单一的职责,只做一件事,所以,这给了你一个事实,即方法都是相关的直接或间接地对其内部状态

  • 静态类没有也不会管理状态。也许有人会说这根本不是真的,请参阅单例。好吧,单例可能很好,但是设计为静态类的单例太接近反模式了,在这种情况下,可以像 IoC 容器一样管理单例,只管理一个实例。如果需要,我可以提供一些关于有无容器的示例。

好吧,有人说静态类是接近反模式的东西,因为例如可测试性......好吧,这不是真的,这取决于静态类和涉及的测试是什么相关的。我将报告一个非常好的例子,由伟大的软件架构师Udi Dahan提供,例如,在一篇关于领域事件的好文章中,他谈到了其他事情,关于静态类和可测试性,这里是链接如果您转到如何引发领域事件使用领域事件进行单元测试部分,事件拯救,他谈到了这一点。

在那之后,正如您所说,两者的另一个区别是内存成本,但其他人说。请记住,Reshaper 等工具会建议将不处理状态的实例类/方法转换为静态表示,这有利于内存和使用。

关于您的设计的最后一句话:CommonDaoOperations似乎是一个不处理状态的真正静态类,因此它是一个很好的候选者因为它的性质,它的工作。您可以改为使用 IoC 容器将其视为“单例”并以正确的方式配置该类。有很多方法可以在没有容器的情况下以其他方式实现这一点。这里有一篇简单的文章,讨论单例和静态类C# Singleton, Static Class。当然,创建一个返回助手的属性并不是一个好的设计,而一个为 get 操作返回一个新实例的属性总是一个糟糕的设计,这将有充分的理由证明......

因此,看到您的设计以及如何使用帮助程序类,Udi 在上面的链接中所说的话很好地描述了您应该实施的解决方案。

于 2013-07-26T09:17:15.927 回答