代码之家  ›  专栏  ›  技术社区  ›  MatthewMartin muthu

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

  •  45
  • MatthewMartin muthu  · 技术社区  · 16 年前

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

    它不像是一个有着基本上不相关的实用函数的数学类。在我自己的常规编程中,我很少在resharper弹出的地方编写方法,并说“这可能是一个静态方法”,当我这样做时,它们往往是无意识的实用方法。

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

    更新 :传递的特定状态是来自数据库的结果集。该类的职责是从数据库的结果集填充Excel电子表格模板。我不知道这有什么区别。

    16 回复  |  直到 16 年前
        1
  •  20
  •   Community Mohan Dere    9 年前

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

    从我个人的经验来看,我已经研究了100个Kloc应用程序,这些应用程序具有非常深的对象层次结构,所有东西都继承和覆盖了所有其他东西,所有东西都实现了6个接口,甚至接口继承了6个接口,系统实现了书中的每个设计模式等等。

    最终结果:一个真正的oop-tastic体系结构,具有如此多的间接级别,调试任何东西都需要数小时。最近我开始了一个类似这样的系统的工作,在那里学习曲线被描述为“砖墙,后面是山”。

    有时候,过分热心的OOP会导致类的粒度太小,以至于它实际上是一个净伤害。

    相比之下,许多函数式编程语言,甚至是像f和ocaml(和c)这样的OO语言。鼓励平庸和肤浅的等级制度。这些语言的库往往具有以下属性:

    • 大多数对象都是POCO,或者最多具有一到两个继承级别,其中对象不超过逻辑相关数据的容器。
    • 不是类相互调用,而是模块(相当于静态类)控制对象之间的交互。
    • 模块往往只作用于非常有限的数据类型,因此范围很窄。例如,OCAML列表模块表示对列表的操作,客户模块促进对客户的操作。虽然模块与类上的实例方法具有或多或少相同的功能,但是与基于模块的库相比,模块的关键区别在于模块更独立、更不细化,并且往往很少依赖于其他模块(如果有的话)。
    • 通常不需要对对象覆盖方法进行子类化,因为您可以将函数作为第一类对象传递给专门化。
    • 尽管C不支持此功能, functors 提供一种将专门模块划分为子类的方法。

    大多数大的库往往比深的库更宽,例如win32 api、php库、erlang bifs、ocaml和haskell库、数据库中的存储过程等。因此这种编程风格是一种战斗测试,在现实世界中似乎工作得很好。

    在我看来,最佳设计的基于模块的API比最佳设计的OOP API更容易使用。然而,编码风格在API设计中同样重要,所以如果团队中的其他人都在使用OOP,并且有人使用完全不同的风格来实现某些东西,那么您可能需要重写以更接近团队的编码标准。

        2
  •  21
  •   Thomas Pornin    16 年前

    您所描述的只是简单的结构化编程,可以在C、Pascal或Algol中完成。这并没有什么本质上的问题。那里 情况是OOP更合适,但OOP不是最终的答案,如果结构化编程最能解决手头的问题,那么一个充满静态方法的类就是解决方法。

        3
  •  5
  •   Anders Abel    16 年前

    是否有助于重新表述问题:

    您能否描述静态方法作为一个实体操作的数据,该实体具有:

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

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

        4
  •  4
  •   Waylon Flinn    16 年前

    这是我经常遇到的涉及静态方法的重构工作流。它可以让你对你的问题有一些了解。

    我将从一个具有相当好的封装性的类开始。当我开始添加功能时,我遇到了一个功能块,它实际上不需要访问我的类中的私有字段,但似乎包含相关的功能。在这种情况发生几次之后(有时只是一次),我开始在我实现的静态方法中看到新类的大纲,以及新类与我首先实现静态方法的旧类之间的关系。

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

        5
  •  3
  •   Andy Shellam    16 年前

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

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

    尽管如此,这只是我个人的观点,也是我执行它的方式。我相信其他人会不同意我的看法。老实说,在不知道更多的情况下,很难给出每种方法的理由。

        6
  •  3
  •   Adriaan Koster    16 年前

    IMO最大的问题是,如果你想对调用你提到的类的类进行单元测试,就没有办法替换这种依赖性。因此,您必须同时测试客户机类和静态调用的类。

    如果我们谈论的是一个使用诸如math.floor()这样的实用方法的类,这实际上不是一个问题。但是,如果类是一个真正的依赖项,例如数据访问对象,那么它将其所有客户机与实现联系起来。

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

        7
  •  2
  •   JaredPar    16 年前

    这个模式没什么问题。实际上,C有一个称为静态类的构造,它通过强制要求所有方法都是静态的来支持这个概念。此外,框架中还有许多类具有此功能: Enumerable , Math 等等…

        8
  •  2
  •   Jennifer Zouak    16 年前

    没什么问题。这是一种更“实用”的编码方式。它可以更容易测试(因为没有内部状态)和更好的运行时性能(因为没有实例的开销,否则是无用的对象)。

    但是你马上就失去了一些OO能力 静态方法对继承没有很好的响应。 静态类不能参与许多设计模式,如工厂/服务定位器。

        9
  •  2
  •   Brian Scott    16 年前

    不,许多人倾向于为希望在相关命名空间下分组的实用程序函数创建完全静态的类。拥有完全静态类的原因有很多。

    在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();
    
        10
  •  2
  •   bloparod    16 年前

    使用静态类的一个缺点是,它的客户机不能用测试双重对象替换它来进行单元测试。

    同样,对静态类进行单元测试也比较困难,因为它的合作者不能被测试双重对象替换(实际上,这发生在所有没有注入依赖项的类中)。

        11
  •  1
  •   Bozho    16 年前

    这取决于传递的参数是否真的可以被分类为 状态 .

    让静态方法互相调用是可以的,以防它的所有实用功能都拆分为多个方法以避免重复。例如:

    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
    }
    
        12
  •  1
  •   Riduidel    16 年前

    个人而言,我倾向于认为修改对象状态的方法应该是该对象类的实例方法。实际上,我认为这是一条经验法则:修改对象的方法是该对象类的实例方法。

    但也有一些例外:

    • 处理字符串的方法(如大写首字母或类似的特征)
    • 方法,它是无状态的,只是简单地组装一些东西来生成一个新的,没有任何内部状态。它们显然是罕见的,但通常使它们静止是有用的。

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

        13
  •  1
  •   mdma    16 年前

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

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

        14
  •  0
  •   Frames Catherine White    16 年前

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

    包含所有静态方法且没有实例变量(静态最终常量除外)的类通常是实用程序类,例如数学。 创建一个Unirity类没有任何错误,(而不是在一个单独的类中) 顺便说一句:如果生成一个实用程序类,就应该防止使用类aver来crteate对象。在Java中,您将通过显式定义构造函数来实现这一点,但使构造函数私有化。 正如我所说,创建一个实用程序类没有什么问题, 如果大部分工作是由一个实用类(wich esc)完成的。不是通常意义上的类-它更多是一个函数集合) 然后,这是一个问题,因为这个问题还没有用面向对象的Paradim来解决。 这可能是好事,也可能不是好事

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

        15
  •  0
  •   FosterZ    16 年前

    如果不需要创建一个类的对象,那么将所有方法都创建为该类的静态方法是没有问题的,但是我想知道对于一个充满静态方法的类,您在做什么。

        16
  •  0
  •   Striker    16 年前

    我不太确定你所说的进入方式是什么意思,但如果你说的是这样的话:

     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(...);
    

    所有静态方法/单例方法都会占用更少的内存,但取决于您拥有多少用户,您甚至可能不会注意到它。