我有一些 Java 程序,现在我想知道它是否是模块化的,如果它是模块化的,那么到什么程度,因为模块化永远不能是二进制项,即 0 或 1。我如何确定特定代码是模块化的到这个程度。我想知道如何使代码更加模块化?
10 回答
模块化的一些基准:
- 您为执行特定任务重写了多少次类似的代码?
- 当您更改程序的某些部分时,您需要重构多少代码?
- 文件是否小且易于浏览?
- 应用程序模块是否在需要时充分且独立地执行?
- 您的代码是否具有最低限度的灾难性?当您仅删除一个函数或变量时,所有的地狱都会丢失吗?你在重命名一个类时会遇到 20 多个错误吗?(要检查这一点,您可以实现堆栈机制来跟踪应用程序中的所有跃点)
- 代码与自然语言使用的距离有多近?(即模块及其子组件代表更多真实世界的对象,而无需过多关注净源文件大小)。
至于您对使代码更加模块化的担忧,您应该先问自己上述问题,获得具体答案,然后看看这个。
基本理念是将您的应用程序分解为尽可能小的代码片段,整齐地排列在大量易于理解和访问的目录布局中。
应用程序中的每个方法都不能超过所需的最小处理量。将这些方法组合成越来越多的宏级方法应该会引导您回到您的应用程序。
关键点是
- 关注点分离
- 凝聚
- 封装(通过接口通信)
- 可替代性
- 可重用性
这种模块系统的一个很好的例子是标准汽车零件,如盘式制动器和汽车音响。在制造汽车时,您不想从头开始制造汽车音响。您宁愿购买它并将其插入。您也不希望制动系统影响汽车音响——或者更糟糕的汽车音响影响制动系统。
为了回答您的问题,“我如何确定特定代码在很大程度上是模块化的”,我们可以形成问题来测试模块化。您能否轻松地用其他东西替换您的模块而不影响应用程序的其他部分?
XML 解析器可能是另一个例子。一旦您获得了 DOM 接口,您就真的不关心下面使用的是哪个 XML 解析器实现(例如 Apache Xerces 或 JAXP)。
在 Java 中,另一个问题可能是:所有功能都可以通过interface
s 访问吗?接口几乎可以解决低耦合问题。
另外,你能用一句话描述你系统中的每个模块吗?例如,汽车音响播放音乐和收音机。盘式制动器使车辆安全减速。
(这是我写的什么是组件驱动开发?)
根据 Wikipedia,基于组件的开发是基于组件的软件工程 (CBSE)的别名。
[它] 是软件工程的一个分支,其优先级是在给定软件系统中可用的广泛功能方面的关注点分离。
这有点模糊,所以让我们看看更多细节。
单个组件是封装了一组相关功能(或数据)的软件包或模块。
所有系统进程都放置在单独的组件中,因此每个组件内的所有数据和函数在语义上都是相关的(就像类的内容一样)。因为这个原则,人们常说组件是 模块化的和内聚的。
所以,根据这个定义,一个组件可以是任何东西,只要它真正做好一件事并且只做一件事。
关于系统范围的协调,组件通过接口相互通信。[...] 这个原则导致组件被称为封装。
所以这听起来越来越像我们认为好的 API 或 SOA 应该有的样子。
提供的接口由棒棒糖表示,所需的接口由连接到 UML 中组件外边缘的打开套接字符号表示。
组件的另一个重要属性是它们是可 替换的,因此如果后续组件满足初始组件的要求(通过接口表示),那么一个组件可以被另一个组件替换(在设计时或运行时)。
可重用性是高质量软件组件的一个重要特征。应该设计和实现一个软件组件,以便它可以在许多不同的程序中重用。
可替代性和可重用性是使组件成为组件的原因。那么这和面向对象编程有什么区别呢?
面向对象编程 (OOP) 的理念是软件应该根据它所代表的实际或想象对象的心智模型来编写。[...]
相比之下,基于组件的软件工程没有做出这样的假设,而是声明应该通过将预制组件粘合在一起来开发软件,就像在电子或机械领域一样。
为了回答您关于如何使代码更加模块化的具体问题,有几种方法:
模块化的最佳工具之一是发现代码重用。如果您发现您的代码在不止一次的地方执行相同(或非常相似)的事情,那么它是模块化的一个很好的候选者。
确定哪些逻辑可以独立,从某种意义上说,其他逻辑可以使用它们而无需知道它们是如何构建的。这有点类似于您在 OO 设计中所做的,尽管模块/组件不一定需要与 OO 中的建模对象相对应。
由于这已被标记为“osgi”,因此我可以提出与 OSGi 相关的观点。
简短的回答是,可以从完全的意大利面条代码到小步骤的模块化;不一定是大爆炸。例如,即使意大利面条代码也依赖于某种肉酱日志库,所以从某种意义上说,它已经是模块化的,只是在其中包含一个非常大的 Metball(对不起,模块)。
诀窍是将大肉丸分成一个更小的块,然后是一个稍微小一点的肉丸,然后递归。也不必一口气完成。每次只需多切一点,直到没有东西可以去除。
至于 OSGi,仍然可以将 uber-jar 放入包中。实际上,您可以在不更改位的情况下执行此操作;通过在适当的位置修改 Manifest.MF,或者将其包装在另一个 JAR 中并在清单中指定 Bundle-ClassPath: metaball.jar。
如果做不到这一点,像 BND 这样的工具可以帮助生成您需要的正确数据,然后可以轻松地将其放入 OSGi 运行时中。但要小心过度耦合的代码,以及与类加载器混在一起的东西——那些会让你绊倒的。
假设我理解您的问题,您想知道是什么使代码模块化,因为代码模块显然需要彼此之间存在一些依赖关系才能工作。这是我的回答:
如果您可以将系统分解为模块,并且可以单独测试这些模块,那么这很好地表明系统是模块化的。
正如你所说的模块化不是一个二元的东西,所以它取决于你的相对定义。
我会说:您可以在需要执行该功能的任何程序中使用给定的方法吗?它是“黑匣子”吗?你不需要知道它在幕后做什么?如果答案是否定的,即该方法只能在该程序中正常工作,那么它就不是真正的模块化。
模块化与开发代码的人有关。但我认为普遍的共识是模块化代码是具有可以轻松换出而不更改大部分原始代码的部分的代码。
恕我直言,如果您有 3 个模块 AB 和 C,并且您想完全更改或替换模块 C,如果这样做是一项简单的任务,那么您就有了模块化代码。