所有静态方法的类有什么问题吗?
我在做代码审查,碰到一个使用所有静态方法的类。 入口方法需要几个参数,然后开始调用其他静态方法传递入口方法收到的全部或部分参数。
它不像一个math课程,与很大程度上不相关的效用函数。 在我自己的正常编程中,我很less写Resharperpopup的方法,并说“这可能是一个静态方法”,当我这样做时,他们往往是无意识的实用方法。
这种模式有什么问题吗? 如果一个类的状态被保存在字段和属性中,或者使用参数在静态方法中传递,这只是个人select的问题吗?
更新 :正在传递的特定状态是数据库的结果集。 该类的职责是从数据库的结果集中填充一个Excel电子表格模板。 我不知道这是否有所作为。
这种模式有什么问题吗? 如果一个类的状态被保存在字段和属性中,或者使用参数在静态方法中传递,这只是个人select的问题吗?
从我自己的个人经验来看,我曾经研究过100个KLOC应用程序,这些应用程序具有非常深刻的对象结构,一切都inheritance和覆盖了一切,一切都实现了六十个接口,甚至接口inheritance了六十个接口,系统实现了每一个书中的devise模式等
最终结果:一个真正的面向对象的体系结构,具有如此多的间接级别,以至于需要几个小时来debugging任何东西。 最近我开始了一个这样的系统,那里的学习曲线被描述为“一堵砖墙,一座山”。
有时过分热心的面向对象导致类如此细致,实际上是一个净伤害。
相比之下,许多函数式编程语言,甚至像F#和OCaml(和C#!)这样的面向对象编程语言都鼓励扁平化和浅层次的结构化语言。 这些语言的图书馆往往具有以下属性:
- 大多数对象是POCO,或者最多只有一个或两个级别的inheritance,其中对象不超过逻辑相关数据的容器。
- 而不是相互调用类,你有模块(相当于静态类)控制对象之间的交互。
- 模块倾向于在数量非常有限的数据types上运行,因此范围狭窄。 例如,OCaml列表模块表示列表上的操作,客户模块促进对客户的操作。 尽pipe模块与类实例方法或多或less有相同的function,但与基于模块的库的主要区别在于,模块更独立,粒度更小,并且对其他模块的依赖性很less。
- 通常不需要子类化对象覆盖方法,因为您可以将函数作为一stream的对象传递以进行专门化。
- 虽然C#不支持这个function,但是函子提供了一个子类化特殊模块的方法。
大多数大型图书馆都比较深,比如Win32 API,PHP库,Erlang BIF,OCaml和Haskell图书馆,数据库中的存储过程等等。所以这种编程风格是战斗testing,似乎可以很好地工作现实世界。
在我看来,最好devise的基于模块的API往往比最好devise的OOP API更易于使用。 然而,编码风格在APIdevise中同样重要,所以如果你的团队中的其他人都在使用OOP,并且有人用完全不同的风格去实现某些东西,那么你应该要求重写,以便更紧密地匹配你的团队编码标准。
您所描述的只是简单的结构化编程,就像在C,Pascal或Algol中所做的那样。 这没有什么内在的错误。 有些情况是OOP更合适,但是OOP并不是最终的答案,如果结构化编程最好能够解决问题,那么需要一个充满静态方法的类。
这有助于改变这个问题:
你能描述静态方法作为一个实体运行的数据:
- 一个明确的含义
- 保持内部状态一致的责任。
在这种情况下,它应该是一个实例化的对象,否则它可能只是一堆相关的函数,就像math库一样。
这是一个我经常遇到的涉及静态方法的重构工作stream。 它可能会提供一些洞察你的问题。
我将从一个封装合理的课程开始。 当我开始添加function时,我遇到了一些function,并不需要访问我的类中的私有字段,但似乎包含相关的function。 在这之后发生了几次(有时只是一次),我开始在我实现的静态方法中看到一个新类的轮廓,以及这个新类如何与我最初实现静态方法的旧类相关。
将这些静态方法转化为一个或多个类的好处是,当你这样做的时候,经常会更容易理解和维护你的软件。
我觉得如果要求类保持某种forms的状态(例如属性),那么它应该被实例化(即“正常”类)。
如果应该只有这个类的一个实例(因此是所有静态方法),那么应该有一个singleton属性/方法或一个工厂方法,在第一次调用时创build该类的一个实例,然后只提供该实例其他人要求。
话虽如此,这只是我的个人意见和我实施的方式。 我相信其他人会不同意我的观点。 如果不知道更多的话,说实话,很难说出或反对每种方法的理由。
国际海事组织最大的问题是,如果你想unit testing调用你提到的类的类,没有办法来取代依赖关系。 所以你不得不同时testing客户端类和静态调用类。
如果我们正在讨论像Math.floor()这样的实用方法的类,这不是一个真正的问题。 但是,如果这个类是一个真正的依赖关系,比如一个数据访问对象,那么它将所有的客户端连接到它的实现中。
编辑:我不同意人们说这种“结构化编程”没有什么错。 我想说像这样的类在普通的Java项目中遇到至less是一种代码异味,可能表示对创build者的面向对象devise的误解。
这种模式没有错。 事实上C#有一个名为静态类的构造,通过强制要求所有的方法都是静态的,用来支持这个概念。 此外,框架中还有许多类具有此function: Enumerable
, Math
等…
没有什么是错的。 这是一种更“function性”的编码方式。 它可以更容易testing(因为没有内部状态)和更好的性能在运行时(因为没有开销实例一个否则无用的对象)。
但是你立即失去了一些面向对象的能力静态方法不能很好的响应inheritance。 静态类不能参与许多devise模式,如工厂/服务定位器。
不,很多人倾向于为实用function创build完全静态的类,他们希望在相关的命名空间下进行分组。 完全静态类有很多有效的理由。
在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();
使用静态类的一个缺点是它的客户不能用一个testingdouble来代替它,以便进行unit testing。
同样,unit testing一个静态类也比较困难,因为它的协作者不能被testing双replace(事实上,所有没有dependency injection的类都会发生这种情况)。
这取决于传递的参数是否真的可以归类为状态 。
如果所有的实用function都以多种方式分离以避免重复,则可以通过静态方法相互调用。 例如:
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 }
好吧,我个人倾向于认为修改对象状态的方法应该是对象类的实例方法。 实际上,我认为这是一个规则:修改对象的方法是该对象类的实例方法。
但是有一些例外:
- 处理string的方法(如大写他们的第一个字母,或那种function)
- 方法是无状态的,只是将一些东西组合起来产生一个新的,没有任何内部状态。 他们显然是很less见的,但将它们静态化通常是有用的。
实际上,我认为静态关键字就是这样:一个选项应该小心使用,因为它违反了一些OOP原则。
将所有状态作为方法parameter passing可能是一个有用的devise模式。 它确保没有共享的可变状态,所以这个类本质上是线程安全的。 服务通常使用这种模式来实现。
但是,通过方法parameter passing所有的状态并不意味着这些方法必须是静态的 – 你仍然可以在非静态方法中使用相同的模式。 使这些方法成为静态的优点是调用代码可以通过引用它的名字来使用类。 没有必要注射,或查找或任何其他中间人。 缺点是可维护性 – 静态方法不是dynamic分派,不能轻易分类,也不能重构到接口。 我build议使用静态方法时,只有一个可能的类的实现,并有一个强有力的理由不使用非静态方法。
“一个类的状态是使用参数在静态方法中传递的?” 这是程序编程的工作原理。
一个包含所有静态方法的类,并且没有实例variables(除了静态最终常量)通常是一个工具类,例如Math。 做一个unility class没什么问题,(不是自己做的)顺便说一句:如果做一个实用程序,你可以避免使用class来限制对象。 在java中,你可以通过明确定义构造函数来完成,但是使构造函数是私有的。 正如我所说的,创build一个实用类没有任何问题,如果大部分工作是由一个utiulity类来完成的(这个类不是通常意义上的类,而是更多的函数集合),那么这是概率标志问题还没有用面向对象的方法来解决。 这可能或可能不是一件好事
入口方法需要几个参数,然后开始调用其他静态方法传递入口方法收到的全部或部分参数。 从这个声音来看,整个类只是一个有效的方法(这肯定会是其他静态方法是私有的(并且只是帮助函数),并且没有实例variables(baring常量))。是和好的东西,这是esc。 结构化的/程序性的程序devise,相当整齐的把它们(function和它的帮手)都捆绑在一个类中。 (在C中你只是将它们全部放在一个文件中,并声明助手的静态(意思是不能从这个文件的外面访问))
如果不需要创build一个类的对象,那么创build所有方法作为该类的静态是没有问题的,但我想知道你正在做什么类静态方法。
我不太清楚你的入口方法是什么意思,但如果你正在谈论这样的事情:
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(...);
所有静态方法/单例往往使用较less的内存,但取决于你有多less用户可能不会注意到它。