如何理解模块、组件和对象

模块、组件和对象这三个名词,是软件开发中非常常见的说法。在很多软件平台、库、框架中,都使用这三个名词作为描述其复杂结构的单元结构。模块、组件、对象三者虽然有相似的含义,但是,也有非常大的差别,本文探究这三个概念的异同目的,并非仅仅是规范这些用语,而是希望能对复杂软件的解构单位,作一次较完整的思考。

孙子兵法云:“制众如治寡,分而治之”。意思是:处理数量庞大而复杂的事物,其实和处理简单、单一的事物一样简单,关键是要把处理的目标分解开。软件系统的发展过程里,也对分解有很多不同的尝试。

早期结构化编程流行的阶段,软件是一个被层层分解的函数,每个函数都被划分成更多,具备平行含义的子函数。整个程序的结构就好像一本分类学书籍,由一个主题目main(),划分成非常多的子标题,子标题下面又分为更小的标题,形成一个树状的结构。

如果我们需要对这样一个程序的某部分代码重用,也就是说抽取其中一部分代码,放到其他程序里面,那么,一般来说都要以“函数”为包装形式,否则,拷贝整段代码的缺点是显而易见的。函数用作来重用代码,天然的有其接口形式:参数与返回值(或者是输入参数与输出参数)。因此,一个函数,就能成为一个封闭的代码块,具备固定的输入和输出接口,我们称之为模块。

然而,当我们在编写“函数模块”的时候,往往会发现,针对同一块数据,往往需要多个函数来一起操作。比如我们操作文件,针对特定的一个文件,我们需要打开、添加、删除、关闭这个文件。如果每个操作都是一个函数,那么我们的输出参数往往都需要一个代表文件的指针;而这个指针,又往往是其他函数的输入参数。——这种做法,实际上是破坏了模块的重用性,因为针对“模块”这个概念,我们设想的是,能简单使用一些输入参数,就能获得操作结果,而不希望还要关心输入参数的状态,以及维护输出参数的状态。由于我们编写的大多数商业系统中,都是针对一些数据进行操作,而不仅仅是进行某些数据的运算,所以数据状态反而成了程序的核心,各种所谓的“模块”,都是为了修改、操作某些特定数据的工具而已。

也许有人会说,这样的工具模块不也是挺好的吗?当然,从代码重用的角度来说,模块和工具都可以很方便的被重用在需要的地方。但是,我们希望被重用的代码,是能够比较简单的去使用的,如果使用“工具型函数”,需要用户去小心的维护一个复杂的数据模型,那么就达不到“简单”的重用代码的目的了。举个例子,如果我们有10个工具函数,都是操作同一个数据指针的,而这些函数的调用还是有一定的调用顺序或其他逻辑约束的,那么我们在使用的时候,就要认字仔细的查看例程,彻底搞明白这10个函数的关系,然后按设计者的思路小心翼翼的去使用。否则,如果只是看了下函数手册就用起来,很可能就会碰到诸如“未定义”的结果这类奇怪的错误。

显然,我们对于“模块”的追求,无法简单的用函数这个工具来实现,因此人们想到,我们能不能把数据和函数组合起来,这样使用的时候,就不需要在使用的时候去维护复杂的状态。——因此诞生了面向对象。这里的“对象”,指的就是把数据,和操作这些数据函数,根据内在逻辑封装起来的单位。由于有了这种封装,一个对象除了代表业务数据状态以外,还附带了一系列的“方法”,这些“方法函数”是可以随时随意的调用,而用来操作对象的。

虽然这些“方法函数”同样还是可以设置“输入参数”和“输出参数”,但是我们完全可以把方法函数所依附的对象(this对象),作为自由操作的输入或输出参数。我们在编写方法函数的时候,就可以有意识的去管理所依赖的数据状态——this对象,这样任何一个方法函数,都可以明确而唯一的操作this的内容,而无需用户去操心。

举个例子,如果我们用面向对象的类库去操作文件,我们只需要创建一个File对象,就可以随意的删除、更新、修改这个对象的内容。如果这个对象对应的文件没有打开,或者不存在,那么File对象本身会有一个状态值做记录,那些删除、更新的方法就可以先判断一下这个状态值,从而返回“错误”提示。这样就可以避免用户去按照某些逻辑约束使用各个方法,极大的降低了对象作为“模块”的学习难度。

对象这个概念,对于那些希望把数据+操作构建成模块的情况,是非常合适的。但是,他也有一些缺点。其中有一个非常典型,就是对象模型是一个编程语言的概念,本质上一个对象只是内存中的一堆数据。然而我们要使用的模块,往往不止是内存中的一堆数字就能解决问题的。比如我们需要管理数据库中的一个表,或者操作一个GUI的按钮,又或者控制一个游戏里面的动画角色……这些都不止用内存中的“属性”能满足。

这个时候,人们依托IDE工具,把许多需要复用的复杂数据,和对象模型关联起来,封装成一个个可以根据预先约束的用法去使用的模块,这就是组件了。组件一般会比对象的约束要多,因为每一类组件,都有明确的使用接口,以便能“组合”到某个框架里面。比如JavaBean规范规定,所有这类的组件,必须要以getter/setter的形式对外提供属性的读写。又比如Flex规范中,组件甚至可以仅仅用XML来描述,而不必要是某种程序代码。

[delphi中的数据库、表空间可以用图形化修改属性]

总体来说,所谓组件,是在某套使用规范下,特别构建的软件模块。这种模块很多依托对象模型,有所谓“属性”和“方法”。但是这些属性和方法,为了能提供更直观方便的使用接口,一般会有所约束。一旦满足这些约束,开发者重用这种模块的时候,甚至是不需要用编程语言来调用这些“属性”、“方法”。所以组件和对象的差别,往往是在于其约束方面。很多组件都要求对象从某个基类派生,或者要有一个主动注册和校验的程序,才能从对象编程组件。但是反过来一般组件都提供某种编程语言下的对象模型,让用户可以对其编写代码。

因此,我们可以看出,模块、对象、组件之间是有一定的关系,但并不完全等同。一般来说,我们喜欢把任何可重用的代码都成为模块,我们希望模块是简单的、仅仅通过输入输出就能控制的重用代码,所以其含义是最广泛而通用的。可惜现实中能很简单使用的模块并不多,为了能处理复杂的问题,人们开发了很多编程的工具,其中一些为了解决特定问题,比如数据库、GUI、游戏,都强化了一些模块的使用方法,让可重用的代码,能更简单的投入使用,而这些易用性上的好处,都需要付出遵守一定的规范的代价。

这些能在特定问题解决框架下运行的模块,就成为了组件。虽然模块和组件本身都不需要采用面向对象的模型,但是面向对象作为编程上的一个重要概念,能帮助使用者理解和操作模块或者组件,并且因为其封装管理数据状态的特征,能降低编程上的复杂程度,更容易对业务领域建模,所以很多模块和组件,背后都采用了面向对象的概念来打造其编程接口和表现形式。

当我们自己希望开发一个框架的时候,我们往往会思考,如何让用户更方便的使用这个框架啊,如何提高框架的扩展能力。如果我们的框架是比较专门的解决某个方面的问题,那么我们把扩展接口设计成组件,可能是一个好主意,因为这样能提供更友好的用户使用界面,包括图形化或配置化的用法;但是这种组件接口也带来了一定的复杂性,在用户需要扩展自己的功能时,组件和框架的接口直接限制了框架、组件的功能外延。

所以,一般通用性比较高的框架或平台,其模块形式往往是类库,比如JDK或者C#的标准库,这种仅仅以对象为接口的模块,其编程灵活性是更加高的。然而,如果我们要处理的问题,更多的是数据计算,而不是数据状态的管理;或者我们要管理的数据模型高度抽象,比如Linux下以文件这个模型来管理所有的内存、外设,这样我们甚至可以不用对象模型,直接用函数就能解决问题。

不过一般来说,作为业务逻辑开发领域的程序员,要处理的数据模型往往不会太简单,有大量的显示、修改、逻辑判断的需求,这方面还是构建对象模型来的更容易建模些。

最后,可能会有疑问的是,函数能不能用来构建组件呢?实际上是可以的,比如我们在一些GUI编程中,如按钮的响应事件,就直接传入一个函数(VB);或者在多线程编程中,一个线程任务的接口往往就是一个函数。但是,由于函数这个模型比较简单,而且难以在空间上复制,必须和代码统一到一起,所以我们更倾向用对象来做组件,否则函数的约束也没有太多手段。

感谢大家的阅读,如觉得此文对你有那么一丁点的作用,麻烦动动手指转发或分享至朋友圈。如有不同意见,欢迎后台留言探讨。

原文发布于微信公众号 - 韩大(handa1740168)

原文发表时间:2016-03-02

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏马洪彪

Java设计模式(四)Builder建造者模式

一、场景描述 建造者模式同工厂模式、抽象工厂模式一样,用于创建继承类对象。 工厂模式:http://www.cnblogs.com/mahongbiao/p/8...

3097
来自专栏java一日一条

5 分钟搞定 Java Comparable 接口

还有一个不错的视频(https://marcus-biel.com/java-comparable-interface-video-tutorial/)。

714
来自专栏木可大大

微信小程序开发实战

讲解小程序的一些必须要掌握的基础知识,如文件类型、项目结构、配置文件等。部分之前老课程中没有涉及的重要新特性也会在本章节提及

492
来自专栏跟着阿笨一起玩NET

display:none与visible:hidden的区别

display:none和visible:hidden都能把网页上某个元素隐藏起来,但两者有区别:

351
来自专栏PHP技术

PHP 代码规范简洁之道

原文出处: Scholer 1. 统一的编码规范 编码规范往简单说其实就是三个方面: 换行 空格 变量命名 放在 PHP 里面,还有一些附加的地方,比如关键字...

3566
来自专栏企鹅号快讯

Python数据类型—字符串(1)

Python数据类型 — 字符串(1) 除了数值,我们还常常需要对文字进行处理。本文将介绍Python用于文字处理的数据类型 — 字符串,并解释如何在计算机系统...

1818
来自专栏java一日一条

5 分钟搞定 Java Comparable 接口

我们应该如何对事物进行比较和排序?这问题听上去有点莫名其妙,但我希望你认真考虑一下。比方说,我们有一组苹果:

271
来自专栏漫漫前端路

从 Vue typings 看 “this”

在 2.5.0 版本中,Vue 大大改进了类型声明系统以更好地使用默认的基于对象的 API。

722
来自专栏喵了个咪的博客空间

zephir-(11)流程控制语句

#zephir-流程控制语句# ? ##前言## 先在这里感谢各位zephir开源技术提供者 ZEPHIR实现了一个简化的控制结构语句,类似的语言如C,PHP等...

2774
来自专栏Java技术分享

XML语言基本介绍

怎么解释呢,拿我们平时熟悉的HTML语言来说,HTML就是一种规定了特定标签的语言,而XML语言则没有规定特定的标签(可能有一些基本的?目前我还没有学习到,以后...

1756

扫描关注云+社区