【问题标题】:Handling large classes处理大类
【发布时间】:2012-04-18 08:41:24
【问题描述】:

在过去的几个月里,我最近开始使用 Java 进行编码。我有一个 Matrix 类,它有很多方法,变得过于臃肿。我还有一个 SquareMatrix 类,它扩展了 Matrix,并减少了一些臃肿。

我发现 Matrix 类中的许多方法通常与矩阵相关。它具有所有基础知识,例如 addMatrix(Matrix)、multiplyMatrix(Matrix)、multiplyMatrix(float),以及更复杂的方法,例如 getGaussian(Matrix) 或 getLUDecomposition(Matrix)。

在减少班级矩阵中的行数方面,我有哪些选择? 一个类变得非常大是正常的吗?我不这么认为... 这是我应该尽早考虑的事情并且重构很困难吗?还是有简单的解决方案?

谢谢!


编辑:在阅读了一些回复后,我正在考虑执行以下操作:

实用类矩阵包含矩阵的所有常用/基本方法

委托类(助手/子):

*助手类* getGaussian(..), getLUFactorization(..), ...

子类 (扩展矩阵) 方阵,LowerTriMatrix,UpperTriMatrix,...

委托似乎类似于在 C++ 中定义多个带有标头的 .cpp 文件。 仍然欢迎提供更多提示。


Edit2:另外,我更喜欢通过正确设计来重构,而不是快速修复。我希望它对未来的项目有所帮助,而不仅仅是这个。

【问题讨论】:

标签: java class matrix cohesion


【解决方案1】:

接口应该是completeminimal的,这意味着一个类型接口应该包含所有必要的方法来实现该类型所需的和有意义的任务,而且只有这些。

因此分析 Matrix 的 API 方法并确定它们中的哪些属于核心 API(哪些必须有权访问类的内部来完成它们的任务),哪些提供“扩展”功能。然后将类 API 简化为核心功能,将其余方法迁移到单独的帮助程序/实用程序/子类中,这些子类可以使用公共核心 API 实现其目标。

如需重构/单元测试帮助,请考虑获取 Michael Feathers 的 Working Effectively with Legacy Code

【讨论】:

    【解决方案2】:

    重构绝对是一件好事,但是很难可靠地重构除非你有好的单元测试Test-driven development 提供了一种实现通常易于重构的代码库的方法。

    对于没有单元测试的现有代码,这意味着在你需要首先隔离要重构的区域之前,先编写单元测试,然后再重构。不过,如果这只是一个个人项目,您可能可以通过编写测试来避免您希望在重构中完好无损地生存的最小功能集。

    关于如何进行手头的重构,有几个原则和方法,就像其他一些答案已经指出的那样。在大多数情况下对我有用的是:

    严格执行以下样式规则:

    1. 方法不能超过 40 行
    2. Class fan-out 不能超过 20 个

    还有其他方法,但我个人认为这些方法最有助于让自己远离糟糕的设计。限制方法长度迫使您真正考虑方法签名并编写简洁、重点突出的方法。这通常使重复更容易发现和删除。限制类扇出在类层面也有类似的效果,它迫使你认真思考类的角色并使其简洁。

    诸如checkstyle 之类的工具可以强制执行这两项,以及许多其他样式规则。

    EclipseIDEA IntelliJ 都提供了“提取方法”重构,这使得它变得像突出显示要提取的代码并点击快捷键一样简单。 IntelliJ 甚至会检查类中是否出现完全相同的代码,并将它们替换为对新方法的调用。事实上,我建议始终使用此类重构工具,而不是手动复制粘贴和编辑。

    【讨论】:

    • +1 用于提及扇出。但是,我要在 (1) 规则中添加一条注释:如果您是“独奏”编码器(并且没有其他人需要访问您的代码),您可以使用方法,只要它们适合整个屏幕。例如,如果您总是使用大型监视器,您可以将需要重构前的最大限制设置为 60 行,并避免(或延迟)耗时的重构工作。
    • TDD 确实不错 - 用于编写新代码。但是,它对一堆没有单元测试的现有代码有什么帮助呢?
    • @UnaiVivi,这不取决于您的显示器大小,而是取决于心理承受能力。事实上,方法越长,就越难理解和维护。即使现在你的方法对你来说似乎完全清楚,但对于 6 个月后的未来的你来说,与任何其他开发人员一样,这将同样困难。
    • @PéterTörök 我同意你的看法。我只是指出了一些对我有用的经验法则(以及我自己的心理能力)。
    • @PéterTörök 刚刚添加了一些(不完整的)注释,说明如何在没有单元测试的情况下处理现有代码。 UnaiVivi 任何物超所值的 IDE 都可以轻松提取方法,因此开发人员剩下的唯一工作就是进行思考。我发现这种想法真的很有帮助。
    【解决方案3】:

    快速而肮脏让你臃肿的班级看起来干净的方法(通过将混乱移到别处)

    您可以将方法的内容放在外部实用程序类的静态方法中。

    例子:

    矩阵.java

    import MatrixUtils;
    //[...]
    public class Matrix
    {
        //[...]
        Matrix()
        {
            //[...]
        }
        //[...]
        public Gaussian getGaussian(Matrix m)
        {
            return MatrixUtils.computeGaussian(m);
        }
        public LUDecomposition getLUDecomposition(Matrix m)
        {
            return MatrixUtils.computeLUDecomposition(m);
        }
    }
    

    MatrixUtils.java

    protected class MatrixUtils
    {
        private MatrixUtils()//Utility class: prevent instantiation
        {//nothing
        }
        //[...]
        protected static Gaussian computeGaussian(Matrix m)
        {
            //do your superlong 100+lines Algorithm and return the result
        }
        public static LUDecomposition computeLUDecomposition(Matrix m)
        {
            //do your superlong 100+lines Algorithm and return the result
        }
    }
    

    通过这种方式(又快又脏,正如我所说),您可以干净地浏览 Matrix 类(尤其是当您有很多方法和 javadoc 时)。

    当您想要访问实际代码时,您可以使用 MatrixUtils。

    如果由于某种原因,每个方法都超长(超过 200 行代码!?)并且如果由于某种其他原因,您确实需要保持 .java 文件简短,您甚至可以创建一个每个 [uber-long] 方法的“静态类”(即仅具有静态方法的实用程序类)。

    ...当然,在任何情况下,您都应该在某种程度上进行“过程”重构,正如@oksayt 所提到的,您可以通过将一些代码块放入单独的(但连贯的)函数中来缩短方法。

    【讨论】:

    • 有什么你可以提出的不是“又快又脏”的建议吗?
    • 嗯,又快又脏并不总是坏事!我明确表示“快速而肮脏”只是作为“免责声明”,以防止“纯粹主义者”投反对票。但是让我告诉你,在现实的商业生活中,编码是由(1)快速和肮脏的解决方案,(2)优雅/高效设计模式的应用,(3)你自己的“最佳实践”组成的,这是一个微妙的平衡。尊重最佳编码标准和您非常个人化的编码风格。
    • 无论如何,如果您无法识别可以重构为新对象的实体,以便将部分代码委托给其他类(尤其是在存在“原始计算”或一个漫长而复杂的算法——你的情况可能就是这样),那么使用实用程序类可能会成为最好的解决方案之一,而不是最终的“肮脏”。
    【解决方案4】:

    “非常大”是什么意思?

    我认为方法不应超过 100 行,类不应超过 1000 行,包括 javadoc。如果您需要更多,请检查您的设计。使用委托,即将你的逻辑分成模块/类,并从另一个类中调用一个类。

    【讨论】:

    • 我阅读了一些关于委托的内容,这绝对是我要开始实施的事情。
    【解决方案5】:

    对于基本的矩阵运算,您可以使用具有合适许可证的开源实现。你可以考虑:-

    http://math.nist.gov/javanumerics/jama/

    http://code.google.com/p/efficient-java-matrix-library/

    http://commons.apache.org/math/

    这肯定会减少您的整体代码大小,并且您会受益于更多经过测试、使用过的代码库。

    希望这会有所帮助。

    【讨论】:

    • 我正在自学 Java。我更担心学习正确的设计原则,而不是借用别人的代码。我也通过做而不是观察学到更多。不过感谢您的回答。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-30
    • 2021-04-08
    相关资源
    最近更新 更多