假设我有一个用于执行单个功能的类。执行该功能后,可以将其销毁。有没有理由更喜欢这些方法之一?
// Initialize arguments in constructor
MyClass myObject = new MyClass(arg1, arg2, arg3);
myObject.myMethod();
// Pass arguments to method
MyClass myObject = new MyClass();
myObject.myMethod(arg1, arg2, arg3);
// Pass arguments to static method
MyClass.myMethod(arg1, arg2, arg3);
我故意对细节模糊不清,试图获得针对不同情况的指导方针。但我并没有考虑像Math.random()这样的简单库函数。我正在考虑更多执行某些特定复杂任务的类,但只需要一个(公共)方法来执行它。
答案 0 :(得分:253)
我曾经喜欢充满静态方法的实用工具类。他们对辅助方法进行了很好的整合,否则会导致冗余和维护。它们非常容易使用,没有实例化,没有处置,只是火不会忘记。我想这是我第一次不知不觉地尝试创建面向服务的体系结构 - 许多无状态服务只是完成了自己的工作而没有其他工作。然而,随着系统的发展,龙将会到来。
<强>多态性强>
假设我们有方法UtilityClass.SomeMethod愉快地嗡嗡作响。突然间我们需要稍微改变功能。大多数功能都是相同的,但我们必须改变几个部分。如果它不是静态方法,我们可以创建一个派生类并根据需要更改方法内容。因为它是一种静态方法,我们不能。当然,如果我们只需要在旧方法之前或之后添加功能,我们可以创建一个新类并在其中调用旧类 - 但这只是粗略的。
界面困境
由于逻辑原因,无法通过接口定义静态方法。由于我们无法覆盖静态方法,因此当我们需要通过接口传递静态类时,静态类是无用的。这使我们无法使用静态类作为策略模式的一部分。我们可以通过passing delegates instead of interfaces修补一些问题。
<强>测试强>
这基本上与上面提到的界面问题密切相关。由于我们交换实现的能力非常有限,我们也很难用测试代码替换生产代码。同样,我们可以将它们包装起来,但它需要我们更改代码的大部分内容,以便能够接受包装而不是实际的对象。
促进blob
由于静态方法通常用作实用方法,而实用方法通常会有不同的用途,我们很快就会得到一个充满非连贯功能的大类 - 理想情况下,每个类在系统中应该有一个目的。只要他们的目的明确定义,我宁愿拥有五次课程。
参数蠕变
首先,这个小巧可爱且无辜的静态方法可能需要一个参数。随着功能的增长,添加了几个新参数。很快又添加了可选的参数,因此我们创建了方法的重载(或者只是在支持它们的语言中添加默认值)。不久,我们有一个方法,需要10个参数。实际上只需要前三个,参数4-7是可选的。但是如果指定了参数6,则需要填写7-9 ...如果我们创建了一个具有执行此静态方法的单一目的的类,我们可以通过获取所需的参数来解决这个问题。构造函数,并允许用户通过属性或方法设置可选值,以同时设置多个相互依赖的值。此外,如果一种方法已经发展到这种复杂程度,那么无论如何它很可能都需要在自己的类中。
要求消费者无缘无故地创建课程实例
最常见的一个论点是,为什么要求我们类的使用者创建一个用于调用这个方法的实例,而之后却没有使用该实例?在大多数语言中创建类的实例是非常便宜的操作,因此速度不是问题。向消费者添加额外的代码行是为将来构建更易维护的解决方案奠定基础的低成本。最后,如果你想避免创建实例,只需创建一个类的单例包装器,以便于重用 - 尽管这确实要求你的类是无状态的。如果它不是无状态的,你仍然可以创建处理所有内容的静态包装方法,同时从长远来看仍然可以带来所有好处。最后,您还可以创建一个隐藏实例化的类,就好像它是一个单例:MyWrapper.Instance是一个只返回新MyClass()的属性;
只有西斯处理绝对值
当然,我不喜欢静态方法。对静态方法没有任何风险的真实实用程序类是静态方法的优秀案例 - 以System.Convert为例。如果您的项目是一次性的,对未来的维护没有要求,那么整体架构确实不是很重要 - 静态或非静态,并不重要 - 但是开发速度确实如此。
标准,标准,标准!
使用实例方法不会阻止您使用静态方法,反之亦然。只要有分化背后的推理并且它是标准化的。没有什么比查看使用不同实现方法的业务层更糟糕了。
答案 1 :(得分:84)
我更喜欢静态方式。由于Class不表示对象,因此创建它的实例是没有意义的。
只应为其方法存在的类应保持静态。
答案 2 :(得分:17)
如果没有理由为了执行该函数而创建类的实例,那么使用静态实现。为什么让这个类的使用者在不需要时创建一个实例。
答案 3 :(得分:15)
如果您不需要保存对象的状态,则无需首先实例化它。我将使用您将参数传递给的单一静态方法。
我还要警告一个巨大的Utils类,它有许多不相关的静态方法。这可能会匆忙地变得杂乱无章。最好有很多类,每个类都有很少的相关方法。
答案 4 :(得分:6)
我会说静态方法格式是更好的选择。而且我也会让类静态化,这样你就不必担心意外地创建类的实例。
答案 5 :(得分:5)
我真的不知道这里的情况是什么,但我会把它作为一个方法放在arg1,arg2或arg3所属的类中 - 如果你可以在语义上说那些类中的一个将拥有该方法。
答案 6 :(得分:4)
我建议根据提供的信息很难回答。
我的直觉是,如果你只是想要一个方法,并且你要立即抛弃类,那么将它作为一个带有所有参数的静态类。
当然,很难确切地说明为什么需要为这一个方法创建单个类。这是大多数人假设的典型“公用事业类”情况吗?或者您正在实施某种规则类,未来可能会有更多规则类。
例如,让该类可插入。然后你想为你的一个方法创建一个接口,然后你想要将所有参数传递到接口,而不是传递给构造函数,但你不希望它是静态的。
答案 7 :(得分:3)
你的班级可以变得静止吗?
如果是这样,那么我会把它变成一个'Utilities'类,我会将所有的单函数类放入其中。
答案 8 :(得分:3)
如果此方法是无状态的并且您不需要传递它,那么将它定义为静态是最有意义的。如果你需要传递方法,你可以考虑使用delegate而不是你提出的其他方法。
答案 9 :(得分:3)
对于简单的应用程序和internal
助手,我会使用静态方法。对于带有组件的应用程序,我很喜欢Managed Extensibility Framework。以下是我正在编写的文档的摘录,用于描述您在API中找到的模式。
I[ServiceName]Service
界面定义。作为一个人为的例子:
public interface ISettingsService
{
string ReadSetting(string name);
void WriteSetting(string name, string value);
}
[Export]
public class ObjectRequiringSettings
{
[Import]
private ISettingsService SettingsService
{
get;
set;
}
private void Foo()
{
if (SettingsService.ReadSetting("PerformFooAction") == bool.TrueString)
{
// whatever
}
}
}
答案 10 :(得分:2)
我会在构造函数中执行所有操作。像这样:
new MyClass(arg1, arg2, arg3);// the constructor does everything.
或
MyClass my_object(arg1, arg2, arg3);
答案 11 :(得分:0)
需要考虑的另一个重要问题是系统是否将在多线程环境中运行,以及是否具有静态方法或变量的线程安全......
你应该注意系统状态。
答案 12 :(得分:0)
你或许可以一起避免这种情况。尝试重构,以便获得arg1.myMethod1(arg2, arg3)
。
如果更有意义的话,用arg2或arg3交换arg1。
如果你无法控制arg1的类,那么装饰它:
class Arg1Decorator
private final T1 arg1;
public Arg1Decorator(T1 arg1) {
this.arg1 = arg1;
}
public T myMethod(T2 arg2, T3 arg3) {
...
}
}
arg1d = new Arg1Decorator(arg1)
arg1d.myMethod(arg2, arg3)
理由是,在OOP中,处理数据的数据和方法属于一起。此外,您还可以获得Mark所提到的所有优势。
答案 13 :(得分:0)
我认为,如果您的类或类实例的属性不会在构造函数或方法中使用,则不建议将方法设计为类似于静态&#39;图案。应始终在“帮助”中考虑静态方法。方式。
答案 14 :(得分:0)
根据您是只想做某事还是要做某件事然后返回某事,您可以执行以下操作:
public abstract class DoSomethingClass<T>
{
protected abstract void doSomething(T arg1, T arg2, T arg3);
}
public abstract class ReturnSomethingClass<T, V>
{
public T value;
protected abstract void returnSomething(V arg1, V arg2, V arg3);
}
public class DoSomethingInt extends DoSomethingClass<Integer>
{
public DoSomethingInt(int arg1, int arg2, int arg3)
{
doSomething(arg1, arg2, arg3);
}
@Override
protected void doSomething(Integer arg1, Integer arg2, Integer arg3)
{
// ...
}
}
public class ReturnSomethingString extends ReturnSomethingClass<String, Integer>
{
public ReturnSomethingString(int arg1, int arg2, int arg3)
{
returnSomething(arg1, arg2, arg3);
}
@Override
protected void returnSomething(Integer arg1, Integer arg2, Integer arg3)
{
String retValue;
// ...
value = retValue;
}
}
public class MainClass
{
static void main(String[] args)
{
int a = 3, b = 4, c = 5;
Object dummy = new DoSomethingInt(a,b,c); // doSomething was called, dummy is still around though
String myReturn = (new ReturnSomethingString(a,b,c)).value; // returnSomething was called and immediately destroyed
}
}