实用程序类:静态类vs成员变量vs本地变量

时间:2013-07-26 07:14:32

标签: c# design-patterns

我正在创建一个包含多种通用方法的实用工具类CommonDaoOperationsCreateUpdateDelete。 这不是基类,因为一些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除外)。至少这是我对提供的答案的看法。

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

3 个答案:

答案 0 :(得分:2)

除非您计划创建特别多的这些对象,否则我认为它不会影响性能。

我更愿意(2)。只需要为每次使用创建它,只需编写代码即可。此外,如果您想要使用某种IOC,请将实用程序类作为参数,更改它的初始化方式,或者只是将类更改为另一个类 - 只需要更改一个成员就可以了。改变所有使用它的地方。

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

答案 1 :(得分:2)

我会对性能影响进行更合格的评论;但是,这是我对每个人的看法:

<强> 1。静态类

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

<强> 2。具体类,实例化每个DAO对象

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

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

您的课程是否应该演变成整个DAO所需的内容,例如:你突然需要维护对象的状态(或者你需要将它注入到DAO的构造函数中,或者沿着这些行注入其他东西),你总是可以改变实例化的位置(虽然在我看来,如果发生这种情况,你不再有实用程序类了,你需要重新考虑这个类如何适合你的架构。)

答案 2 :(得分:1)

在考虑静态类具体类之间的差异和用法时,确定需要考虑的含义,例如请参阅可测试性(但这不是那么肯定完全如下所示,但首先要做一些假设:

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

  • 静态类没有也没有管理状态。也许有人会说这根本不是真的,看看单身人士。好吧,单身人士可能很好,但设计为静态类的单身人士太靠近反模式,在这种情况下,单身人士可以像IoC容器一样管理,通过管理一个实例。如果需要,我可以提供一些关于有和没有容器的例子。

嗯,有人说静态类是接近反模式的东西,因为例如可测试性......好吧,这是不正确的,这取决于涉及的静态类和测试的相关性。 我将通过伟大的软件架构师 Udi Dahan 报告一个非常好的例子,例如,在一篇关于域事件的好文章中,他谈到了关于静态类和可测试性之间的其他内容,如果您转到如何提升域事件使用域事件进行单元测试部分,请在此处链接Domain Events Salvation,他谈到了这一点。

在那之后,正如你所说,关于这两者的另一个不同之处在于内存成本,但其他人则对此有所说明。请记住,像Reshaper这样的工具提出了将不处理状态的实例类/方法转换为静态表示的建议,这有利于内存和使用。

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

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