你应该把矩阵数据和数学分开吗?

Should you separate matrix data and math?

我正在尝试制作一些简单的 classes 来完成 OpenGL 转换所需的数学运算。但是,现在我已经进行了一半,在查看不同的示例时,我发现大多数库都将数学与矩阵 class 本身分开(大多数时候在数学 class 中使用静态方法)。这到底是什么原因呢?

是否在创建多个矩阵对象时也会多次创建函数,这会完全影响性能或其他方面?

示例: 我发现将数学函数添加到矩阵 class 中要容易得多,因此我不需要将矩阵本身传递给函数:例如

class Matrix4f {

    //constructor...

    public void rotate(double theta) {
        setValue(0, 0, (float) Math.cos(theta));
        setValue(1, 0, (float) Math.sin(theta));
        setValue(0, 1, (float) Math.sin(theta));
        setValue(1, 1, (float) Math.cos(theta));
    }

    //getters and setters...
}

而不是

public class LinearAlgebraMath {
    public static Matrix4f rotate(Matrix4f target, double theta) {
        target.setValue(0, 0, (float) Math.cos(theta));
        target.setValue(1, 0, (float) Math.sin(theta));
        target.setValue(0, 1, (float) Math.sin(theta));
        target.setValue(1, 1, (float) Math.cos(theta));
        return target;    
    }
}

public class Matrix4f {

    //constructor...

    //getters and setters...
}

所以,最后我喜欢将数学函数添加到矩阵 class 因为这对我来说更容易(尤其是在处理使用多个数据结构的数学运算时)但我不想让我的偏好降低性能,所以我的问题是:下面的示例是否优于上面的示例,如果是,为什么?

Is it when you're creating multiple of the matrix objects that the functions are also created multiple times, which impacts performance or something else entirely?

这些函数是加载到 VM 中的 Class 对象的一部分。多个 Matrix 对象将仅引用该 function/method 的一个副本。因此内存等不会像您建议的那样使用实例方法受到影响。

我通常会按照您的建议将功能附加到 class 的实例。也许您正在使用的引用是从非 OO 语言(C?)翻译而来的,其中结构用于矩阵并且独立函数是强制性的?

非成员带来的实际好处是耦合更松散。他们无法修改 class 的内部结构,可以这么说。

举个例子,如果您遇到一个 user-reported 错误,应用程序抛出一个常规数组访问越界异常(不是特定于您的矩阵 class),代码与矩阵,那么如果你有一个非常简单的矩阵 class,它只有几种方法可能会弄乱它的生殖器(私处)并给它 STD,那么你的矩阵睡过的嫌疑人名单非常狭窄。同时,如果你有一个矩阵 class 有 200 种方法,它在聚会、喝醉和睡觉,所有有漏洞的东西,你要调查的嫌疑人名单是巨大的。

它还有一个好处是,如果矩阵 class 不旨在提供每个 matrix-related 人类已知的操作。这在设计任何东西以确保 public 界面有一个真正 "done" 的点时非常有用,在这个点上,您不会感觉到返回并无限期地添加越来越多的点的诱惑有一些 class 的野兽,它有 100 多种方法,文档必须跨越 20 页。能够将您的代码库分为稳定(不变)和不稳定(变化)部分是很好的,因此对于任何设计,您应该能够达到 "completeness" 的状态,就其在非常有限 时间。

旨在将您可以使用 class 执行的所有可能操作添加到 class 本身通常会使实现这样的目标变得不可能。添加到 class 的内容越多,您就越有理由添加更多内容。那里有一个恶性循环,你添加的越多,你的设计看起来就越不完整。极简主义是实现这一目标的方法。

最后,它使您的矩阵 class 更容易理解如何使用它,因为您可以立即使用它做的事情较少。有些人可能对 eigenvectors/values 不感兴趣,因此如果它提供的功能较少,可能有助于使您的界面更易于理解。

一种务实的方法是将各种背景的人一直在做的基本操作包括到您的设计中,例如 matrix/matrix 和 matrix/vector 乘法。在那里你仍然可以有一个清单,你可以在某个时候调用设计 "done" 而无需设计一个旨在做世界上所有可以想象的事情的神 class/monolith。然后将奇异的东西放入单独的接口中,比如单独的 classes 和静态方法只能通过矩阵的 public 接口进行操作。这些单独的静态方法无法访问内部结构,因此它们可以通过安全层,在该安全层中,您的矩阵 class 可以施加不变量,不允许越界访问其内部数据而不会抛出 MatrixOutOfBounds 异常或类似的东西。

在你的情况下,我想这只是一个更 one-to-one 的 OpenGL 翻译,它是一个 C API 方法是不可能的,但值得注意的是,从一般软件工程的角度来看,非成员更优越的耦合和内聚。它们在语法上可能更麻烦,但从维护和调试的角度来看,可以访问您的 class 私有内容的东西越少越好。太多的方法和你的私有变量变得类似于全局变量,因为它们有这样一个史诗scope/visibility,其中有很多东西可能会篡改它们。在用任何语言设计 classes 时要牢记这一点——如果可以仅使用 public 接口在其他地方实现,则寻找省略直接向 class 添加内容的理由其中 class。出于类似的原因,如果您可以避免从 class 继承来访问其受保护的成员,那么从 SE 的角度来看,在这些上下文中,组合优于继承,因为它会产生更松散的耦合。

从性能的角度来看,往往有更多理由将方法直接添加到 class,因为最有效的解决方案可能需要访问 class 的内部结构。不过,当人们强烈认为需要新方法时,还是宁可事后添加新方法,也好过提前将一大堆不必要的方法添加到 class 中。当涉及到是否应该向接口添加方法时,我喜欢遵循 ​​"when in doubt, leave it out" 的简单规则。