为什么静态方法不被认为是良好的OO实践?

Mik*_*ike 35 java oop static-methods scala language-concepts

我在阅读Programming Scala.在第4章的开头,作者评论说Java支持静态方法,这些方法是"不那么纯粹的OO概念".为什么会这样?

Jör*_*tag 61

面向对象大约有三件事:

  • 消息,
  • 本地保留和保护以及隐藏国家程序,以及
  • 所有事情的极端后期约束.

在这三个中,最重要的一个是消息传递.

静态方法至少违反了消息传递和后期绑定.

消息传递的思想意味着在OO中,计算由自包含对象的网络执行,这些对象向对方发送消息.发送消息是通信/计算的唯一方式.

静态方法不这样做.它们与任何对象无关.他们真的没有方法可言,按照通常的定义.他们真的只是程序.Java静态方法Foo.bar和BASIC子例程之间几乎没有区别FOO_BAR.

至于后期绑定:更现代的名称是动态调度.静态方法也违反了这一点,事实上,它甚至以它们的名字命名:静态方法.

静态方法打破了面向对象的一些非常好的属性.例如,面向对象的系统在对象充当功能时自动具有功能安全性.静态方法(或任何静态方法,无论是静态方法还是静态方法)都会破坏该属性.

您还可以在自己的进程中并行执行每个对象,因为它们只通过消息传递进行通信,从而提供一些简单的并发性.(就像Actors一样,基本上,这不应该太令人惊讶,因为Carl Hewitt创建了基于Smalltalk-71的Actor模型,而Alan Kay创建的Smalltalk-71部分基于PLANNER,而后者又由Carl Hewitt创建.演员和对象之间的关系远非巧合,事实上,它们本质上是同一个.)同样,静态(静态方法,尤其是静态方法)打破了这个不错的属性.


Gab*_*abe 32

不要将"不那么纯粹的OO概念"与"不良实践"混为一谈.作为"纯粹的OO"并不是你应该尝试实现的灵丹妙药.仅仅因为静态方法不将实例变量作为参数并不意味着它们没有用处.有些事情本身并不适用于物体,为了"纯洁",它们不应该被迫进入那种模具.

有些人认为事情应该是"纯粹的",因此任何"不纯"都是不好的做法.实际上,糟糕的做法只是做一些令人困惑,难以维护,难以使用的事情等.创建带有实例的静态方法是不好的做法,因为任何采用实例的方法都应该是实例方法.另一方面,实用程序和工厂函数之类的东西通常不会占用实例,因此它们应该是静态的.

如果你想知道他们为什么不是"纯粹的OO",那是因为它们不是实例方法."纯粹的"OO语言将所有东西都作为对象,所有函数都是实例方法.当然,这并不是一直非常有用.例如,考虑该Math.atan2方法.它需要两个数字,不需要任何状态.您甚至可以将它作为一种方法对象什么?在"纯粹的"OO语言中,Math它本身可能是一个对象(可能是单例),并且atan2是一个实例方法,但由于该函数实际上并不使用Math对象中的任何状态,因此它也不是"纯粹的OO" "概念.

  • 真正.另一方面,我看到太多代码,其中一切都是单身,一切都是静态的.这种代码通常更难以修改,更难以扩展,更难以重新创建它本来不打算做的事情.也就是说,有太多人对这一点过于教条. (2认同)
  • 实际上,`atan2` 函数测量*单个点* 相对于正 x 轴的角度的弧度。换句话说,它首先不应该采用两个数字参数,而应该采用单点参数。在面向对象的术语中,可以将其建模为点对象上的方法。 (2认同)
  • 我完全同意。大量的实用函数本质上根本不具有面向对象的性质。您可以将它们硬塞到面向对象语言中,但您所做的只是降低效率。 (2认同)

Edd*_*die 25

迄今为止尚未提及的静态方法不是非常OO的一个原因是接口和抽象类仅定义非静态方法.因此静态方法不适合继承.

另请注意,静态方法无法访问" super",这意味着无法在任何真实意义上覆盖静态方法.实际上,它们根本无法被覆盖,只能被隐藏.试试这个:

public class Test {
    public static int returnValue() {
        return 0;
    }

    public static void main(String[] arg) {
        System.out.println(Test.returnValue());
        System.out.println(Test2.returnValue());
        Test x = new Test2();
        System.out.println(x.returnValue());
    }
}


public class Test2 extends Test {
    public static int returnValue() {
        return 1;
    }
}
Run Code Online (Sandbox Code Playgroud)

当你运行它时,你将无法得到你所期望的. Test.returnValue()给出你所期望的. Test2.returnValue() 隐藏超类中相同名称的方法(它不会覆盖它),它给出了你所期望的.

人们可能天真地期望"非静态地"调用静态方法来使用多态.它没有.声明变量的类是用于查找方法的类.这是一种糟糕的形式,因为有人可能希望代码执行与实际操作不同的操作.

这并不意味着,"不要使用静态方法!" 它确实意味着您应该为那些您真正希望Class对象拥有该方法的实例保留静态方法的使用,而不仅仅是作为制作单例的懒惰方式.

  • 从我目前所见的情况来看,这个答案可能是最有意义的。打破三位一体的概念之一肯定是有悖常理的。 (2认同)

Gul*_*had 8

由于以下原因,静态方法不被视为良好的面向对象实践:

1) 防止重复使用:

静态方法不能被覆盖。它不能在界面中使用。

2)对象生命周期很长:

静态方法会在内存中保留一段日志时间,并且其垃圾收集需要很长时间。开发人员无法控制静态变量的销毁或创建。静态变量的过度使用会导致内存溢出。

3)另外,还有一些要点:

它不尊重封装,因为对象不会完全控制其状态。它不遵循控制反转、松耦合、依赖注入等概念。


Ili*_*sov 7

静态方法导致紧耦合,这违反了良好的面向对象设计.无法通过依赖倒置避免调用代码和静态方法中的代码的紧密耦合,因为静态方法固有地不支持面向对象的设计技术,例如继承性和多态性.

除了静态方法很难测试因为这些紧密耦合的依赖关系,这通常会导致代码依赖的第三方基础结构 - 比如数据库,并且很难在不实际进入和更改的情况下更改行为码.