单一方法类——最佳方法?

假设我有一个用于执行单个函数的类。执行该函数之后,就可以销毁它。是否有任何理由选择这些方法之一?

// 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 ()这样的简单库函数。我在考虑更多的类,它们执行一些特定的、复杂的任务,但只需要一个(公共)方法来执行。

94537 次浏览

我更喜欢静态的方式。因为 Class 不表示对象,所以创建对象的实例是没有意义的。

只存在于其方法中的类应保持静态。

你的类可以是静态的吗?

如果是这样,那么我将把它变成一个‘ Utitility’类,我将把所有的单函数类放进去。

如果没有理由为了执行函数而创建类的实例,那么使用静态实现。为什么要让这个类的使用者在不需要的时候创建一个实例。

我真的不知道这里是什么情况,但是我想把它作为一个方法放在 arg1、 arg2或 arg3所属的类中——如果你能从语义上说这些类中的一个拥有这个方法。

我认为静态方法格式是更好的选择。我也会让类是静态的这样你就不用担心会不小心创建一个类的实例。

如果不需要保存对象的 国家,那么首先就不需要实例化它。我会选择传递参数的单个静态方法。

我还要提醒注意一个巨大的 Utils 类,它有几十个不相关的静态方法。这可能会在匆忙中变得杂乱无章和难以操作。最好有许多类,每个类只有很少的相关方法。

我只需要在构造函数中做所有的事情,就像这样:

new MyClass(arg1, arg2, arg3);// the constructor does everything.

或者

MyClass my_object(arg1, arg2, arg3);

如果这个方法是无状态的,并且您不需要传递它,那么将它定义为静态方法是最有意义的。如果您确实需要传递这个方法,您可以考虑使用 授权,而不是其他建议的方法。

我过去喜欢用静态方法填充实用工具类。他们对辅助方法进行了大量的整合,否则这些方法将会导致冗余和维护的地狱。它们非常容易使用,不需要实例化,不需要处理,只需要启动和忘记。我想这是我第一次在不知情的情况下尝试创建一个面向服务的体系结构——许多无国籍的服务只是做自己的工作,没有别的。然而,随着系统的发展,龙也会出现。

多态性
假设我们有 UtilityClass 方法。快乐地前进的某种方法。突然之间,我们需要稍微改变一下功能。大多数功能是相同的,但我们必须改变一对夫妇的部分,尽管如此。如果它不是一个静态方法,我们可以创建一个派生类并根据需要更改方法内容。因为它是一个静态方法,所以我们不能。当然,如果我们只是需要在旧方法之前或之后添加功能,我们可以创建一个新类,并在其中调用旧的类——但这太恶心了。

界面问题
由于逻辑原因,静态方法不能通过接口定义。由于我们不能重写静态方法,所以当我们需要通过它们的接口传递它们时,静态类是无用的。这使得我们无法使用静态类作为策略模式的一部分。我们可以通过 传递委托而不是接口解决一些问题。

测试
这基本上与上面提到的界面问题密切相关。由于我们交换实现的能力非常有限,我们也会遇到用测试代码替换生产代码的麻烦。同样,我们可以将它们包装起来,但是这需要我们更改代码的大部分,以便能够接受包装器而不是实际的对象。

培养斑点
由于静态方法通常被用作实用方法,而实用方法通常有不同的用途,所以我们很快就会得到一个大型类,其中充满了不一致的功能——理想情况下,每个类在系统中都应该有一个单一的用途。只要类的目的明确,我宁愿使用五倍的类。

参数蠕变
首先,这个可爱而单纯的静态方法可能只有一个参数。随着功能的增加,添加了一些新的参数。很快又添加了更多的可选参数,所以我们创建了方法的重载(或者只是添加默认值,使用支持它们的语言)。不久,我们就有了一个接受10个参数的方法。只有前三个是真正需要的,参数4-7是可选的。但是如果指定了参数6,那么也需要填充7-9... ... 如果我们创建一个类的唯一目的就是做这个静态方法所做的事情,我们可以通过在构造函数中接受所需的参数来解决这个问题,并允许用户通过属性设置可选值,或者通过方法同时设置多个相互依赖的值。此外,如果一个方法已经发展到这种程度的复杂性,它很可能无论如何都需要在它自己的类中。

无缘无故地要求使用者创建类的实例
最常见的争论之一是,为什么要求我们类的使用者创建一个实例来调用这个单一的方法,而事后却不使用这个实例呢?在大多数语言中,创建类的实例是一种非常非常廉价的操作,因此速度不是问题。向使用者添加额外的代码行可以降低成本,从而为将来更具可维护性的解决方案奠定基础。最后,如果您想避免创建实例,只需要创建一个类的单例包装器,以便于重用——尽管这确实要求您的类是无状态的。如果它不是无状态的,您仍然可以创建处理所有事情的静态包装器方法,同时从长远来看仍然可以获得所有好处。最后,您还可以创建一个类来隐藏实例化,就好像它是一个单例: MyWrapper。实例是一个只返回新 MyClass ()的属性;

只有西斯才能做到绝对
当然,我不喜欢静态方法也有例外。对于静态方法来说,没有任何膨胀风险的真正实用工具类是很好的例子。转换为例子。如果您的项目是一次性的,对未来的维护没有要求,那么整个体系结构真的不是很重要——静态的或非静态的,并不重要——但是,开发速度很重要。

标准,标准,标准!
使用实例方法并不妨碍您也使用静态方法,反之亦然。只要区分背后有理由,而且是标准化的。没有什么比查看拥有不同实现方法的业务层更糟糕的了。

我认为根据提供的信息很难回答。

我的直觉是,如果只有一个方法,并且要立即抛弃这个类,那么就让它成为一个接受所有参数的静态类。

当然,很难确切地说明为什么只为这一个方法创建一个类。这是大多数人假设的典型的“实用程序类”情况吗?或者您正在实现某种规则类,将来可能还会有更多的规则类。

例如,让那个类是可插入的。然后您需要为您的一个方法创建一个 Interface,然后您需要将所有的参数传递到接口中,而不是传递到构造函数中,但是您不希望它是静态的。

对于简单的应用程序和 internal助手,我将使用静态方法。对于带组件的应用程序,我很喜欢 托管扩展性框架。下面是我正在撰写的文档的摘录,该文档描述了您将在我的 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
}
}
}

需要考虑的一个更重要的问题是,系统是否会在多线程环境中运行,以及使用静态方法或变量是否是线程安全的..。

您应该注意系统状态。

您可能能够避免所有这种情况。尝试重构,以获得 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)

其原因是,在面向对象方法中,数据和处理这些数据的方法属于一起。而且你得到了马克提到的所有优势。

我认为,如果类的属性或类的实例不会用在构造函数或方法中,就不建议将方法设计成“ static”模式。静态方法应该总是以“帮助”的方式思考。

这取决于你是否只想做一些事情或者做一些事情,然后返回一些你可以做的事情:

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
}
}