使用所有静态方法的类有什么问题吗?

时间:2010-03-18 14:28:13

标签: c# java oop

我正在进行代码审查,并遇到了一个使用所有静态方法的类。入口方法接受几个参数,然后开始调用其他静态方法传递入口方法接收的全部或部分参数。

它不像Math类具有很大程度上不相关的实用函数。在我自己的正常编程中,我很少编写Resharper弹出的方法并说“这可能是一个静态方法”,当我这样做时,它们往往是无意识的实用方法。

这种模式有什么问题吗?如果类的状态保存在字段和属性中,或者使用参数在静态方法中传递,这只是个人选择的问题吗?

UPDATE :传递的特定状态是数据库中的结果集。该类的职责是从DB的结果集中填充Excel电子表格模板。我不知道这是否有所不同。

16 个答案:

答案 0 :(得分:21)

您所描述的只是结构化编程,可以在C,Pascal或Algol中完成。这没有什么本质上的错误。那些的情况是OOP更合适,但是OOP不是最终的答案,如果结构化编程能够最好地解决当前的问题,那么一个充满静态方法的类就可以了。

答案 1 :(得分:20)

  

这有什么不对吗?   图案?这只是一个问题   个人选择若状态如何   class在字段和属性中保存   或者在静态中传播   使用参数的方法?

根据我自己的个人经验,我已经研究了100个KLOC应用程序,这些应用程序具有非常深的对象层次结构,一切都继承并覆盖其他所有内容,一切都实现了六个接口,甚至接口继承了六个接口,系统实现了书中的每一种设计模式等。

最终结果:真正的OOP-tastic架构具有如此多的间接层,因此需要花费数小时来调试任何内容。我最近开始使用这样的系统开始工作,其中学习曲线被描述为“砖墙,后面是山”。

有时过于热心的OOP导致课程如此精细,以至于它实际上是一种净伤害。

相比之下,许多函数式编程语言,甚至像F#和OCaml(和C#!)这样的OO,都会鼓励平坦和浅层的层次结构。这些语言的库往往具有以下属性:

  • 大多数对象都是POCO,或者最多只有一个或两个级别的继承,其中对象不过是逻辑相关数据的容器。
  • 而不是彼此调用的类,你有控制对象之间交互的模块(相当于静态类)。
  • 模块往往会对数量非常有限的数据类型起作用,因此范围很窄。例如,OCaml List模块表示列表上的操作,Customer模块促进对客户的操作。虽然模块与类上的实例方法具有或多或少相同的功能,但与基于模块的库的关键区别在于模块更加自包含,更不精细,并且往往对其他模块具有很少的依赖性。
  • 通常不需要子类化对象覆盖方法,因为您可以将函数作为特殊化的第一类对象传递。
  • 虽然C#不支持此功能,但functors提供了一种子类化专用模块的方法。

大多数大型库往往比更深,例如Win32 API,PHP库,Erlang BIF,OCaml和Haskell库,数据库中的存储过程等。所以这种编程风格是战斗测试,似乎在现实世界里工作得很好。

在我看来,最好设计的基于模块的API往往比最好设计的OOP API更容易使用。但是,编码风格在API设计中同样重要,因此如果团队中的其他人都在使用OOP并且有人以完全不同的方式实现某些内容,那么您应该要求重写以更紧密地匹配您的团队编码标准。

答案 2 :(得分:5)

重新解释这个问题是否有帮助:

您能否将静态方法操作的数据描述为具有以下内容的实体:

  • 一个明确的含义
  • 保持内部状态一致的责任。

在这种情况下,它应该是一个实例化的对象,否则它可能只是一堆相关的函数,就像数学库一样。

答案 3 :(得分:4)

这是我经常遇到的涉及静态方法的重构工作流程。它可以为您的问题提供一些见解。

我将从一个具有相当好的封装的类开始。当我开始添加功能时,我遇到了一个功能,它不需要访问我的类中的私有字段,但似乎包含相关的功能。在这发生几次之后(有时只发生一次),我开始在我实现的静态方法中看到新类的轮廓,以及新类如何与我第一次实现静态方法的旧类相关。

我将这些静态方法转换为一个或多个类的好处是,当您这样做时,通常会更容易理解和维护您的软件。

答案 4 :(得分:3)

我觉得如果要求类保持某种形式的状态(例如属性),那么它应该被实例化(即“正常”类。)

如果只有这个类的一个实例(因此所有的静态方法),那么应该有一个单例属性/方法或工厂方法,它在第一次调用时创建该类的实例,然后只提供那个例子,当其他人要求它时。

话虽如此,这只是我个人的意见以及我实施它的方式。我相信别人会不同意我的意见。老实说,在不了解任何事情的情况下,很难给出/反对每种方法的理由。

答案 5 :(得分:3)

IMO最大的问题是如果你想要调用你提到的类的测试类,就没有办法替换那个依赖。因此,您不得不同时测试客户端类和静态调用的类。

如果我们正在讨论像Math.floor()这样的实用程序方法的类,那么这不是一个真正的问题。但是,如果类是真正的依赖项,例如数据访问对象,那么它将所有客户端绑定到其实现中。

编辑:我不同意人们说这种“结构化编程”没有“错误”。我会说这样的类在普通的Java项目中遇到代码气味至少是一种代码味道,并且可能表明对创建者的面向对象设计存在误解。

答案 6 :(得分:2)

这种模式没有错。 C#实际上有一个名为static classes的结构,它通过强制要求所有方法都是静态的来支持这个概念。此外,框架中有许多具有此功能的类:EnumerableMath等......

答案 7 :(得分:2)

没有任何问题。它是一种更“功能”的编码方式。它可以更容易测试(因为没有内部状态)并且在运行时具有更好的性能(因为没有开销来实现无用的对象)。

但是你立即失去了一些OO功能 静态方法对继承没有很好的响应(根本没有)。 静态类不能参与许多设计模式,例如工厂/服务定位器。

答案 8 :(得分:2)

不,很多人倾向于为实用程序函数创建完全静态的类,他们希望在相关的命名空间下进行分组。完全静态类有很多正当理由。

在C#中要考虑的一件事是,以前完全静态编写的许多类现在都有资格被认为是.net扩展类,它们的核心仍​​然是静态类。很多Linq扩展基于此。

一个例子:

namespace Utils {
    public static class IntUtils        {
            public static bool IsLessThanZero(this int source)
            {
                return (source < 0);
            }
    }
}

然后,您可以简单地执行以下操作:

var intTest = 0;
var blNegative = intTest.IsLessThanZero();

答案 9 :(得分:2)

使用静态类的一个缺点是它的客户端无法通过测试双重替换它以进行单元测试。

以同样的方式,单元测试静态类更难,因为它的协作者不能被测试双精度替换(实际上,这种情况发生在所有非依赖注入的类中)。

答案 10 :(得分:1)

这取决于传递的参数是否真的可以归类为 state

如果所有实用程序功能在多个方法中分开以避免重复,则使用静态方法相互调用是可以的。例如:

public static File loadConfiguration(String name, Enum type) {
    String fileName = (form file name based on name and type);
    return loadFile(fileName); // static method in the same class
}

答案 11 :(得分:1)

嗯,个人而言,我倾向于认为修改对象状态的方法应该是该对象类的实例方法。事实上,我认为这是一个大拇指:修改对象的方法是该对象类的实例方法。

但是有一些例外:

  • 处理字符串的方法(比如大写它们的第一个字母或那种特征)
  • 无状态的方法,简单地组装一些东西来生成一个没有任何内部状态的新东西。它们显然很少见,但通常有助于使它们静止。

事实上,我认为静态关键字是这样的:一个应该小心使用的选项,因为它打破了一些OOP原则。

答案 12 :(得分:1)

将所有状态作为方法参数传递可能是一种有用的设计模式。它确保没有共享的可变状态,因此该类本身是线程安全的。服务通常使用这种模式实现。

但是,通过方法参数传递所有状态并不意味着方法必须是静态的 - 您仍然可以使用与非静态方法相同的模式。使方法静态的优点是调用代码只需通过名称引用它就可以使用该类。不需要注射,查找或任何其他中间人。缺点是可维护性 - 静态方法不是动态调度,不能轻易地进行子类化,也不能重构为接口。我建议使用静态方法,当只有一个可能的类实现,并且有充分理由不使用非静态方法时。

答案 13 :(得分:0)

“类的状态是......在使用参数的静态方法中传递?” 这就是程序编程的工作原理。

具有所有静态方法的类,并且没有实例变量(静态最终常量除外)通常是实用程序类,例如Math。 创建一个unility类没有什么不对,(不是自己的) 顺便说一句:如果创建一个实用程序类,你可能会阻止使用类来使用crteate对象。在java中你可以通过明确定义构造函数,但使构造函数私有来做到这一点。 虽然我说创建实用程序类没有任何问题, 如果大部分工作是由一个utiulity类完成的(因为esc。不是通常意义上的类 - 它更像是一组函数) 那么这就像使用面向对象的paradim解决问题一样。 这可能是也可能不是好事

入口方法需要几个参数,然后开始调用其他静态方法传递入口方法接收的全部或部分参数。 从这个声音来看,整个类只是有效的一种方法(这绝对是个例子,其他静态方法是私有的(并且只是辅助函数),并且没有实例变量(baring常量)) 这可能是好事, 这是esc。结构化/程序化编程,相当整洁,将它们(函数和它的助手)全部捆绑在一个类中。 (在C yould中将它们全部放在一个文件中,并声明帮助程序是静态的(意味着不能从该文件的外侧访问))

答案 14 :(得分:0)

如果不需要创建类的对象,那么将所有方法创建为该类的静态就没有问题,但是我想知道你在使用类全静态方法做了什么。

答案 15 :(得分:0)

我不太确定你的入学方法是什么意思,但如果你在谈论这样的事情:

 MyMethod myMethod = new MyMethod();
 myMethod.doSomething(1);

 public class MyMethod {
      public String doSomething(int a) {
          String p1 = MyMethod.functionA(a);
          String p2 = MyMethod.functionB(p1);
          return p1 + P2;
      }
      public static String functionA(...) {...}
      public static String functionB(...) {...}
 }

这是不可取的。

我认为使用所有静态方法/单例是一种很好的方法来编写业务逻辑,而不必在类中保留任何内容。我倾向于将它用于单身人士,但这只是一种偏好。

 MyClass.myStaticMethod(....);

而不是:

 MyClass.getInstance().mySingletonMethod(...);

所有静态方法/单例都倾向于使用更少的内存,但是根据您拥有的用户数量,您可能甚至都没有注意到它。