内容提要

  • 本书以图配文,以计算机的三大原则为开端,相继介绍了计算机的结构、手工汇编、程序流程、算法、数据结构、面向对象编程、数据库、TCP/IP网络、数据加密、XML、计算机系统开发以及SE的相关知识。

前言

  • 其实不仅是计算机,其他学问亦是如此。首先要划出一个“知识的范围”,精通一门学问所必知必会的知识都在这个范围内。其次是掌握该范围内每个知识点中“基础中的基础知识”。最后是能独当一面的“目标”,即掌握了这些知识可以做什么。

计算机是怎样跑起来的——本书将要讲解的主要关键词

  • 设置这一部分的目的,是为了让诸位能带着问题阅读正文的内容。 ●本章重点 各章的本章要点部分揭示了正文的主题。诸位可以读一读,以确认这一章中是否有想要了解的内容。 ●正文 正文部分会以讲座的方式,从各章要点中提到的角度出发,对计算机的运行机制予以解释说明。其中还会出现用Visual Basic或C语言等编程语言编写的示例程序,编写时已力求精简,即便是没有编程经验的读者也能看懂。 ●专栏“来自企业培训现场” 专栏部分将会与诸位分享笔者自担任讲师以来,从培训现场收集来的各种各样的轶事。诸位可以时而站在讲师的角度、时而站在听众的角度读一读这部分。专栏部分不仅有严肃认真的话题,更有有趣逗乐的笑话,想必会对诸位有所帮助。

第1章 计算机的三大原则

  • 本书以本章介绍的计算机的三大原则为基础,内容延伸至硬件和软件、编程、数据库、网络以及计算机系统。

  • 计算机的三大原则

  • 1.计算机是执行输入、运算、输出的机器2.程序是指令和数据的集合3.计算机的处理方式有时与人们的思维习惯不同

  • 计算机是由硬件和软件组成的。诸位可以把硬件和软件的区别理解成游戏机(硬件)和收录在CD-ROM中的游戏(软件)的区别。这样就能理解硬件和软件各自的基础了(三大原则中的第一点和第二点)。

  • 是无论是多么复杂的功能,都是通过组合一个又一个由输入、运算、输出构成的流程单位来实现的,这是毋庸置疑的事实。如果打算用计算机做点什么的话,就要考虑该如何进行输入、如何获取输出以及进行怎样的运算才能从输入得到输出。

  • 下面介绍软件,即程序的基础。所谓程序,其实非常简单,只不过是指令和数据的集合。无论程序多么高深、多么复杂,其内容也都是指令和数据。所谓指令,就是控制计算机进行输入、运算、输出的命令。把向计算机发出的指令一条条列出来,就得到了程序。这里成套出现的输入、运算、输出,就是之前在硬件的基础一节中说明过的流程。向计算机发出的指令与计算机硬件上的行为一一对应是理所当然的。 在程序设计中,会为一组指令赋予一个名字,可以称之为“函数”“语句”“方法”“子例程”“子程序”等。这里稍微说些题外话,在计算机行业,明明是同一个东西,却可以用各种各样的术语来指代它,这种现象请诸位注意。如果只想用一个名字的话,一般情况下笔者推荐称之为函数,因为这个名字通俗易懂。 程序中的数据分为两类,一类是作为指令执行对象的输入数据,一类是从指令的执行结果得到的输出数据。

  • 在程序设计中,会为一组指令赋予一个名字,可以称之为“函数”“语句”“方法”“子例程”“子程序”等。这里稍微说些题外话,在计算机行业,明明是同一个东西,却可以用各种各样的术语来指代它,这种现象请诸位注意。如果只想用一个名字的话,一般情况下笔者推荐称之为函数,因为这个名字通俗易懂。

  • 程序中的数据分为两类,一类是作为指令执行对象的输入数据,一类是从指令的执行结果得到的输出数据。在编程时程序员会为数据赋予名字,称其为“变量”。

  • 所谓编译就是把用C语言等编程语言编写的文件(源文件)转换成用机器语言(原生代码)编写的文件。

  • 无论是哪个程序,其内容都是数值的罗列,每个数值要么是指令,要么是数据。

  • 计算机本身只不过是为我们处理特定工作的机器。

  • 使用计算机的目的就是为了提高手工作业的效率。

  • 在用计算机替代手工作业的过程中,要想顺应计算机的处理方法,有时就要违背人们的思维习惯。请诸位特别留心这一点。

  • 熟悉计算机的人经常会说出一些令人费解的话,例如“在这里打开文件,获得文件句柄”“把用公钥加密后的文件用私钥解密”。那么,他们所说的“文件句柄”是什么呢?——是数字。“公钥”是什么呢?——是数字。“私钥”呢?——当然还是数字。无论计算机所处理的信息是什么形式,只要把它们都当成是数字就可以了。虽然这有些违背人们的思维习惯,但是处理数字对计算机来说却是非常简单的。

  • 有关计算机三大原则的说明到此结束。只要理解了这三大原则,即使遇到难懂的最新技术,也能轻松应对。

  • 计算机是执行程序的机器。程序是指令和数据的集合。为了使互联网上相互连接的计算机能通过程序协同工作,微软公司采用了SOAP以及XML规范。SOAP是关于调用指令的规范,XML则是定义数据格式的规范。

  • 计算机的协同工作指的是,输入到一台计算机中的数据,可以通过互联网传送到与这台计算机相连的其他计算机上执行运算,运算所输出的结果再返回给这台计算机。

  • 那么就可以把答案归结为“因为那些都是适合计算机的处理方式”。

  • 1.6 为了贴近人类,计算机在不断地进化

  • 计算机进化的目的只有一个——与人类更加相近。要想贴近人类,就必须从计算机的处理方式中摒弃不符合人们思维习惯的部分。

  • 平面的2D(二维)游戏进化成了立体的3D(三维)游戏

  • 无论是哪一种进化,都是为了使计算机的处理方式更加贴近人类。

  • Windows XP和Office XP末尾的XP,代表的就是Experience(体验)。

  •  图1.3 为了贴近人类,个人计算机操作系统也在进化

  • 面向组件编程(Component Based Programming)和面向对象编程(Object Oriented Programming)。

  • 面向组件编程的方法是通过将组件(程序的零件)组装到一起完成程序;面向对象编程的方法是先如实地对现实世界的业务建模,之后再把模型搬到程序中。使用符合人类思维习惯的编程方法,可以实现高效率的开发。

  • CPU(处理器)、内存以及I/O。

  •  图1.4 计算机硬件的组成要素

  • 只要用电路把CPU、内存以及I/O上的引脚相互连接起来

  • IC

  • 再为CPU提供时钟信号

  • 所谓时钟信号,就是由内含晶振的、被称作时钟发生器的元件发出的滴答滴答的电信号。

第2章 试着制造一台计算机吧

  • CPU是计算机的大脑,负责解释、执行程序的内容。有时也将CPU称作“处理器”。

  • 通常用Hz来表示驱动CPU运转的时钟信号的频率。1秒发出1次时钟信号就是1Hz,所以100MHz(兆赫兹)的话就是100×100万 = 1亿次/秒。M(兆)代表100万。

  • CPU上数据总线的条数,或者CPU内部参与运算的寄存器的容量,都可以作为衡量CPU性能的比特数。

  • 而在Windows个人计算机中广泛使用的Pentium(奔腾) CPU则是32比特的CPU。

  • 要想彻底掌握计算机的工作原理,最好的方法就是自己搜集零件,试着组装一台微型计算机。

  • 2.1 制作微型计算机所必需的元件

  • CPU是计算机的大脑,负责解释、执行程序。内存负责存储程序和数据。I/O是Input/Output(输入/输出)的缩写,负责将计算机和外部设备(周边设备)连接在一起。

  •  图2.1 Z80 微型计算机的电路图(本书末尾附有更大的电路图)

  • 为了驱动CPU运转,称为“时钟信号”的电信号必不可少。这种电信号就好像带有一个时钟,滴答滴答地每隔一定时间就变换一次电压的高低(如图2.2所示)。输出时钟信号的元件叫作“时钟发生器”。时钟发生器中带有晶振,根据其自身的频率(振动的次数)产生时钟信号。时钟信号的频率可以衡量CPU的运转速度。这里使用的是2.5MHz(兆赫兹)的时钟发生器。

  • 所需元件表中的74367和7404也是IC,用于提高连接外部设备时的稳定性。

  • 电路中有些地方有交叉,但若只是交叉在一起的话,并不表示电路在交叉处构成通路。只有在交叉处再画上一个小黑点才表示构成通路。

  • IC的引脚(所谓引脚就是IC边缘露出的像蜈蚣腿一样的部分)按照逆时针方向依次带有一个从1开始递增的序号。数引脚序号时,要先把表示正方向的标志,比如半圆形的缺口,朝向左侧。

  • 在表示IC的矩形符号中写上表明该引脚作用的代号。代号就是像RD(Read)表示执行读取操作,WR(Write)表示执行写入操作这样的代表了某种操作的符号

  • 寄存器是位于CPU和I/O中的数据存储器。

  • 2.6 连接用于区分读写对象是内存还是I/O的引脚

  • 对内存和I/O而言,还必须要分清CPU是要输入数据还是输出数据。

  • 表2.2 与读写内存、I/O相关的引脚上的值[插图]

  • CPU、内存、I/O中不但有地址总线引脚、数据总线引脚,还有其他引脚,通常把这些引脚统称为“控制引脚”。

  • 是用于同步的引脚,引脚是用于从Z80 PIO向Z80 CPU发出中断请求的引脚

  • 所谓中断就是让CPU根据外部输入的数据执行特定的程序

  • 电阻是为了防止短路而加入的,否则一旦按下了按键开关,+5V和0V就会直接接到一起发生短路。像这样通过加入电阻把+5V和0V连接起来的方法

  • 如何用开关输入0或1

  • 总线是连接到CPU中数据引脚、地址引脚、控制引脚上的电路的统称

  • 若将引脚的值设为0,则Z80 CPU从电路中隔离。当处于这种隔离状态时,就可以不通过CPU,手动地向内存写入程序了。像这样不经过CPU而直接从外部设备读写内存的行为叫作DMA(Direct Memory Access,直接存储器访问)。在诸位所使用的个人计算机里,硬盘等设备要读写内存时使用的就是DMA。

  • 上拉(Pull-up)

  • CPU在时钟信号的控制下解释、执行内存中存储的程序,按照程序中的指令从内存或I/O中把数据输入到CPU中,在CPU内部进行运算,再把运算结果输出到内存或I/O中。

  • 当微型计算机运行起来后,指拨开关可用于从外部输入数据,LED可用于向外部输出数据。

  • 连接时没有使用74367是为了在程序运行中可以通过Z80 PIO从指拨开关获得输入的数据。

  • 作为计算机大脑的CPU只能解释执行一种编程语言,那就是靠罗列二进制数构成的机器语言(原生代码)

  • 接通了微型计算机的电源后,请按下Z80 CPU上的DMA请求开关

  • 对计算机理解程度的深浅还是和有没有制作过微型计算机有很大关系的。

第3章 体验一次手工汇编

  • CPU中的标志寄存器(Flags Register)有什么作用?

  • 哪个寄存器对应哪个地址,取决于CPU和I/O之间的布线方式。

  • Flag的本意是“旗子”,这里引申为“标志”。一旦执行了算术运算、逻辑运算、比较运算等指令后,标志寄存器并不会存放运算结果的值,而是会把运算后的某些状态存储起来,例如运算结果是否为0、是否产生了负数、是否有溢出(Overflow)等。

  • 加深诸位对计算机的理解,使诸位犹如拨云见日,找到长期困惑着自己的问题的答案,不仅能因“我能看懂程序了”而获得成就感,更能因发现“计算机原来很简单啊”而信心倍增。

  • 因为程序的作用是驱动硬件工作,所以在编写程序之前必须要先了解微型计算机的硬件信息。

  • 可以使用哪种机器语言取决于CPU(也称作处理器)的种类。所谓机器语言就是只用0和1两个二进制数书写的编程语言。即便是相同的机器语言,例如01010011,只要CPU的种类不同,对它的解释也就不同。有的CPU会把它解释成是执行加法运算,有的CPU会把它解释成是向I/O输出。这就好比同样是man这个词,有的人会理解成“慢”,有的人会理解成“男人”。

  • 机器语言就是处理器可以直接理解(与生俱来就能理解)的编程语言。

  • 所谓时钟信号的频率,就是由时钟发生器发送给CPU的电信号的频率。

  • 微型计算机使用的是2.5MHz的时钟信号。时钟信号是在0和1两个数之间反复变换的电信号,就像滴答滴答左右摆动的钟摆一样。通常把发出一次滴答的时间称作一个时钟周期。

  • 每个地址都标示着一个内存中的数据存储单元,而这些地址所构成的范围就是内存的地址空间。

  • 连接着的I/O的种类,就是指连接着微型计算机和周边设备的I/O的种类

  • 这段由8比特二进制数构成的机器语言程序总共23个字节。若把这些字节一个接一个地依次写入内存中,所占据的内存空间就是00000000~00010110。一旦重置了CPU, CPU就会从0号地址开始顺序执行这段程序。

  • 如果理解了汇编语言,也就理解了机器语言,更进一步也就理解了计算机的原始的工作方式。

  • 标签的作用是为该行代码对应的内存地址起一个名字。编程时如果总要考虑“这一行的内存地址是什么来着?”就会很不方便,所以在汇编语言中用标签来代替地址。

  • 在浏览的过程中请注意这些指令的分类,按功能这些指令可以分成运算、与内存的输入输出和与I/O的输入输出三类。这是因为计算机能做的事也只有输入、运算、输出这三种了。

  • 操作数表示的是指令执行的对象。CPU的寄存器、内存地址、I/O地址或者直接给出的数字都可以作为操作数

  • 构成机器语言的是二进制数,而在汇编语言中,则使用十进制数和十六进制数记录数据。若仅仅写出123这样的数字,表示的就是十进制数;而像123H这样在数字末尾加上了一个H(H表示Hexadecimal,即十六进制数),表示的就是十六进制数。在代码清单3.2所示的程序中,使用的都是十进制数。

  • 在汇编语言中,读写内存的指令不同于读写I/O的指令。一旦执行了读写内存的指令,比如LD指令,引脚上的值就会变为0,于是内存被选为输入输出的对象;而一旦执行了读写I/O的指令,比如IN或OUT指令,引脚上的值就会变为0,于是I/O(这里用的是Z80 PIO)被选为输入输出的对象。

  • 计算机的硬件有三个基本要素,CPU、内存和I/O。CPU负责解释、执行程序,从内存或I/O输入数据,在内部进行运算,再把运算结果输出到内存或I/O。内存中存放着程序,程序是指令和数据的集合。I/O中临时存放着用于与周边设备进行输入输出的数据。

  • 既然数据的运算是在CPU中进行的,那么在CPU内部就应该有存储数据的地方。这种存储数据的地方叫作“寄存器”。

  • A寄存器也叫作“累加器”,是运算的核心。所以连接到它上面的导线也一定会比其他寄存器的多。F寄存器也叫作“标志寄存器”,用于存储运算结果的状态,比如是否发生了进位,数字大小的比较结果等。PC寄存器也叫作“程序指针”,存储着指向CPU接下来要执行的指令的地址。PC寄存器的值会随着滴答滴答的时钟信号自动更新,可以说程序就是依靠不断变化的PC寄存器的值运行起来的。SP寄存器也叫作“栈顶指针”,用于在内存中创建出一块称为“栈”的临时数据存储区域。

  • Z80 PIO带有两个端口(端口A和端口B)

  • 操作数必须是已存储在CPU寄存器中的数字,这是汇编语言的规定。

  • 先通过指令“LD A, 207”把数字207读入到寄存器A中,再通过指令“OUT (2), A”把寄存器A中的数据写入到I/O地址所对应的寄存器中。像“(2)”这样用括号括起来的数字,表示的是地址编号。端口A控制寄存器的I/O地址是2号。

  • “IN A, (0)”所在行的开头有一个标签“LOOP:”,代表着这一行的内存地址。正如刚才所讲的那样,在用汇编语言编程时,如果老想着“这一行对应的内存地址是什么来着?”就会很不方便,所以就要用“LOOP:”这样的标签代替内存地址。当把标签作为JP指令的操作数时,标签名的结尾不需要冒号“:”,但是在设定标签时,标签名的结尾则需要加上一个冒号,这一点请诸位注意。

  • 机器语言是唯一一种CPU能直接理解的编程语言。

  • 转换而成的机器语言有多少个字节取决于汇编语言指令的种类以及操作数的个数。

  • 中间隔了2个地址,这说明如果从0号地址开始存储一条2字节的机器语言

  • 由于刚刚从内存读出了一条2字节的指令(占用2个内存地址),所以PC寄存器的值要增加2,并接着从00000010号地址读出指令,解释并执行。

  • 通过反复进行“读取指令”“解释、执行指令”“更新PC寄存器的值”这3个操作,程序就能运行起来了。

  • 汇编语言与机器语言的对应关系

  • 在CPU的资料中,明确写有所有可以使用的助记符,以及助记符转换成机器语言后的数值。只要查看这些资料,就可以把用汇编语言编写的程序手工转换成机器语言的程序,这样的工作称为“手工汇编”。

  • 使用Windows自带的计算器程序就可以很方便地把十进制数转换成二进制数。从Windows的开始菜单中选择“运行”,输入calc后点击“确定”按钮,就可以启动计算器程序。 接下来,从计算器的“查看”菜单中选择“科学型”,这样就得到了一个可以用十进制数或二进制数表示数字的计算器了。首先选中“十进制”单选框,然后输入207,接下来选中“二进制”单选框,这样207就变成了二进制数的11001111(如图3.3所示)。

  • 用Windows的计算器程序把十进制数转换成二进制数

  • 机器语言中每条语句的字节数是多少,内存地址就相应地增加多少。

  • 在将一个2字节的数据存储到内存时,存储顺序是低8位在前、高8位在后(也就是逆序存储)。这样的存储顺序叫作“小端序”(Little Endian),与此相反,将数据由高位到低位顺序地存储到内存的存储顺序则叫作“大端序”(Big Endian)

  • 使用汇编语言编程时,因为要事无巨细地列出计算机的行为,所以程序会变得冗长繁复。

第4章 程序像河水一样流动着

  • 向着一个方向流淌;流着流着产生支流;卷成漩涡。 高级问题:用户的操作等产生事件后,由事件决定程序的流程。

  • 流程图(Flow Chart)是指用图的形式表示程序的流程。

  • 与河流的流动方式一样,程序的流程也分为三种。在程序中,把犹如水流向着一个方向流淌的流程称作“顺序执行”;把犹如水流流着流着产生了支流的流程称作“条件分支”;把犹如水流卷成漩涡的流程称作“循环”。

  • 高级问题:Windows应用程序的运行就是由事件驱动的。例如,选择“打开文件”菜单项就能打开一个窗口,在里面可以指定要打开文件的名称和存储位置。之所以能够这样是因为一旦触发了“选中了菜单项”这个事件,程序的流程就相应地流转到了处理打开窗口的那部分。

  • 程序员一般都是先考虑程序的流程再开始编写程序的。只有编写过程序的人才能体会到“程序是流动着的”。一个人编写的程序如果不能按照预期运行,就说明他还没有很好地掌握“程序是流动着的”这一概念。

  • 为什么说“程序是流动着的”呢?因为作为计算机大脑的CPU在同一时刻基本上只能够解释、执行一条指令。把指令和作为指令操作对象的数据排列起来就形成了程序。请想象把若干条指令一条挨一条地依次排列到一条长长的纸带上。然后把这条纸带展开抻平,从顶端开始依次解释并执行上面的每条指令,这样看起来程序就好像流动起来了。这就是程序的流程。

  • 计算机的硬件系统由CPU、I/O和内存三部分构成。内存中存储着程序,也就是指令和数据。CPU配合着由时钟发生器发出的滴答滴答的时钟信号,从内存中读出指令,然后再依次对其进行解释和执行。

  • CPU中有各种各样的各司其职的寄存器。其中有一个被称为PC (Program Counter,程序计数器)的寄存器,负责存储内存地址,该地址指向下一条即将执行的指令。每解释执行完一条指令,PC寄存器的值就会自动被更新为下一条指令的地址。

  • 个从10号地址开始的3字节指令。CPU解释执行完这条指令后,PC寄存器中的值就变成10+3 = 13了

  • 程序的三种流程正像是河流本身。从高山的泉眼中涌出的清泉形成了河流的源头(程序执行的起点)。水流从山中缓缓流下,有时向着一个方向流淌(顺序执行),有时中途分出了支流(条件分支),还有时由于地势卷起了漩涡(循环)。难道诸位不认为程序的流程也很美吗?完全就像是裱在画轴上的山水画一样(如图4.2所示)。还有一种称作“无条件分支”的流程,它就仿佛是大雨瓢泼引发的泥石流,突然就向着某处流去了,可以认为这是一种特殊的条件分支。

  •  图4.2 程序的三种流程

  • 用VBScript编写的“石头剪刀布游戏”

  •  图4.4 用流程图表示的“石头剪刀布”游戏

  • 流程图的方便之处在于它并不依赖于特定的编程语言。图4.4的流程图所表示的流程不仅能转换成VBScript程序,还可以转换成用其他语言编写的程序,比如C语言或Java语言。可以认为编程语言只不过是将流程图上的流程用文字(程序)重现出来罢了。各种编程语言的差异正如一种自然语言中各地方言的差异一样。只要给出了详细的流程图,就可以编写出基本相同的程序。笔者也曾有过这样的经历,画流程图花费了一个月之久,但是对照着流程图专心写程序只需要两天的时间。

  • 只使用表4.1中所示的符号,就可以画出程序的三种流程(如图4.5所示)。顺序执行只需用直线将矩形框连接起来(a)。条件分支用菱形表示(b)。循环的表示方法是通过条件分支回到前面的处理步骤(c)。这样就能将所有的流程都表示出来了。

  • 作为程序员必须要学会灵活地运用流程图。在思考程序流程的时候,也要首先在头脑中画出流程图。 表4.1 最低限度所需的流程图符号   图4.5 用流程图表示的顺序执行、条件分支、循环三种流程

  • 现在还在使用机器语言或汇编语言的人已经不多了。程序员使用的都是能够更加高效地编写程序的高级语言,如BASIC、C语言和Java等。

  • 条件分支本身也是通过跳转指令实现的。根据比较操作的结果,跳转到之前处理过的步骤就是循环;跳转到之后尚未处理的步骤就是条件分支

  • 所谓结构化程序设计就是“为了把程序编写得具备结构性,仅使用顺序执行、条件分支和循环表示程序的流程即可,而不再使用跳转指令”

  • 在很多高级语言中,还是提供了与机器语言中跳转指令相当的语句,例如VBScript中的GoTo语句。其实戴克斯特拉想表达的是“既然好不容易使用上了高级语言,就别再使用相当于跳转指令的语句了。即使不使用跳转语句,程序的所有流程仍然可以表述出来”。他这样说是因为跳转指令所带来的危害性不小,会使程序陷入到流程错综复杂的状态,就像意大利面条那样缠绕在一起(如图4.11所示)。

  •  图4.11 跳转指令(GoTo语句)使程序陷入“意大利面条”的状态

  • 在程序设计的世界中,如果看到了以“结构化”开头的术语,就可以这样认为:程序的流程是由程序块表示的,而不是用GoTo语句等跳转指令实现的。

  • 结构化异常处理

  • 在Visual Basic.NET中,用Try~Catch~End Try程序块来表示结构化异常处理(如代码清单4.4所示)。但是即使使用了结构化异常处理,在硬件上使用的也还是跳转指令,只是说在高级语言中不用再写相当于跳转指令的语句了。如果把用高级语言所编写的程序转换成机器语言,像结构化异常处理这样的语句还是会被转换为跳转指令。

  • 原始的错误处理机制和结构化异常处理的区别 

  • 所谓算法(Algorithm),就是解决既定问题的步骤。

  • 仅用一条语句就能实现出“石头剪刀布游戏”的编程语言是不存在的。如果眼下待解决的问题是如何编写“石头剪刀布游戏”,那么就必须考虑如何把若干条指令组合起来并形成一个解决问题的流程。如果能够想出可以巧妙实现“石头剪刀布游戏”的流程,那么这个问题也就解决了,换言之算法也就实现了。要是诸位被前辈问到:“这个程序的算法是怎样的呢?”那么只要回答清楚程序的流程就可以了。或者画出流程图也是可以的,因为表示程序流程的流程图本身就能解释算法。

  • 思考算法时的要点是要分两步走,先从整体上考虑程序的粗略流程,再考虑程序各个部分细节的流程。

  • 所有的程序从整体来看都具有一个一成不变的流程,那就是“初始化处理”→“循环处理”→“收尾处理”。

  • 请试想,用户是怎样使用程序的呢?首先,用户启动了程序(程序执行初始化处理)。接下来用户根据自己的需求操作程序(程序进入循环处理阶段)。最后用户关闭了程序(程序执行收尾处理)。这样的使用方法就可以直接作为程序的整体流程。还是以“石头剪刀布游戏”为例,分出初始化处理、循环处理、收尾处理之后,就可以画出如图4.12那样的粗略的流程图。图中把5次循环处理看作是一个整体,当成是一次处理(用矩形表示)。  图4.12 “石头剪刀布游戏”的粗略流程图

  • 首先,启动文字处理机,加载已经写到一半的稿件(初始化处理)。接下来,不断地输入文字(循环处理)。最后,保存稿件(收尾处理)。  图4.13 使用“文字处理机”的粗略流程图

  • 我建议那些因为程序没有按照自己的想法来工作而烦恼的人,不妨试试从勾画反映程序整体流程的粗略流程图下手。只要在此之上慢慢地细化流程,就能得到详细的流程图。接下来再按照流程图所示的流程埋头编写程序就轻松了。

  • 两种特殊的程序流程——中断处理和事件驱动(Event Driven)

  • 中断处理是指计算机使程序的流程突然跳转到程序中的特定地方,这样的地方被称为中断处理例程(Routine)或是中断处理程序(Handler),而这种跳转是通过CPU所具备的硬件功能实现的。

  • 人们通常把中断处理比作是接听电话。假设诸位都正坐在书桌前处理文件,这时突然来电话了,诸位就不得不停下手头的工作去接电话,接完电话再回到之前的工作。像这样由于外部的原因使正常的流程中断,中断后再返回到之前流程的过程就是中断处理流程。

  • Z80 CPU中有和两个引脚,它们可以接收从I/O设备发出的中断请求信号

  • 计算机具有硬件上处理中断的能力;中断一词的英文是Interrupt。

  • 程序员们经常用事件驱动的方式编写那些工作在GUI(Graphical User Inteface,图形用户界面)环境中的应用程序,例如Windows操作系统中的应用程序。这听起来好像挺复杂的,但其实如果把事件驱动想象成是两个程序在对话,理解起来就简单了。

  • 通常把用户在应用程序中点击鼠标或者敲击键盘这样的操作称作“事件”(Event)。负责检测事件的是Windows。Windows通过调用应用程序的WndProc()函数通知应用程序事件的发生。而应用程序则根据事件的类型做出相应的处理。这种机制就是事件驱动。

  • 虽然事件驱动的流程也可以用流程图表示,但是由于要排列很多的菱形符号(表示条件分支),画起来会很复杂。所以下面介绍便于表示事件驱动的“状态转化图”。状态转化图中有多个状态,反映了由于某种原因从某个状态转化到另一个状态的流程。

  • 如图4.15所示的计算器应用程序就可以看作包含三个状态:“显示计算结果”“显示第一个输入的数”以及“显示第二个输入的数”。随着用户按下不同种类的按键,状态也会发生转变。在状态转化图中,在矩形中写上状态的名称,用箭头表示状态转化的方向,并且在箭头上标注引起状态转化的原因(事件)(如图4.16所示)。  图4.15 Windows附带的计算器应用程序  图4.16 计算器应用程序的状态转化图 对于那些觉得画图很麻烦的人,笔者推荐使用“状态转化表”(如表4.2所示)。因为制表的话,用Microsoft Excel等表格软件就可以完成,修改起来也要比图方便。在状态转化表中,行标题是带有编号的状态,列标题是状态转化的原因,而单元格中是目标状态的编号。 表4.2 计算机程序的状态状态转化表的例子  ☆ ☆ ☆ 也许读完中断处理和事件驱动的这两节,诸位会觉得稍微有些混乱,但是程序的流程还是只有顺序执行、条件分支和循环这三种,这一点是没有改变的。其中的顺序执行是最基本的程序流程,这是因为CPU中的PC寄存器的值会自动更新。条件分支和循环,在高级语言中用程序块表示,在机器语言和汇编语言中用跳转指令表示,在硬件上是通过把PC寄存器的值设为要跳转到的目的地的内存地址来实现。只要能充分理解这些概念就OK了。

  • 黑灵芝、粽子叶、红孩儿、三乘轿、黄丝带、五缕须、蓝琉璃、钟子期、灰八哥、摆酒宴

  • [插图]

第5章 与算法成为好朋友的七个要点

  • “哨兵”指的是一种含有特殊值的数据,可用于标识数据的结尾等。

  • 字符串的末尾用0表示,链表的末尾用-1表示,像这种特殊的数据就是哨兵。

  • 例如为了达到“计算出两个数相加的结果”这个目的,就需要依次完成以下三个步骤,即“输入数值”“执行加法运算”“展示结果”。像这样的处理步骤,就被称为算法。

  • 学习编程语言与学习外语很像。为了将自己的想法完整地传达给对方,仅仅死记硬背单词和语法是不够的,只有学会了对话中常用的熟语,才能流利地对话。学习C语言、Java和BASIC等编程语言也是如此。仅仅囫囵吞枣地把关键词和语法记下来,是无法流利地和计算机对话的,可是一旦了解了算法就能将自己的想法完整地传达给计算机了。因为算法就相当于是程序设计中的熟语。

  • 只要理清在现实世界解决问题的步骤,再结合计算机的特性,就一定能想出算法。

  • 要点1:算法中解决问题的步骤是明确且有限的

  • 算法的定义是“被明确定义的有限个规则的集合,用于根据有限的步骤解决问题。例如在既定的精度下,把求解sin x的计算步骤无一遗漏地记录下来的文字”。

  • 算法就是“把解决问题的步骤无一遗漏地用文字或图表示出来”

  • “步骤必须是明确的并且步骤数必须是有限的”。

  • 举一个具体的例子,请诸位想一想解决“求出12和42的最大公约数”这个问题的算法。最大公约数是指两个整数的公共约数(能整除被除数的数)中最大的数。最大公约数的求解方法应该在中学的数学课上学过了。把两个数写在一排,不断地寻找能够同时整除这两个整数的除数。最后把这些除数相乘就得到了最大公约数(如图5.1所示)。  图5.1 在中学学的求解最大公约数的方法 用这个方法求出了6是最大公约数,结果正确。但是这些步骤能够称为算法吗?答案是不能,因为步骤不够明确。 步骤1的“用2整除12和42”和步骤2的“用3整除6和21”,是怎么知道要这样做的呢?寻找能够整除的数字的方法,在这两步中并没有体现。步骤3的“没有能同时整除2和7的除数”,又是怎么知道的呢?而且,到此为止无需后续步骤(即步骤数是有限的)的原因也是不明确的。 其实这些都是凭借人类的“直觉”判断的。在解决问题的步骤中,有了与直觉相关的因素,就不是算法了。既然不是算法,也就不能用程序表示了。

  • 请诸位注意以下三点:1.步骤是明确的、完全不依赖直觉的;2.步骤是机械的、不需要动脑筋就能完成的;3.使步骤终止的原因是明确的。

  •  图5.2 根据辗转相除法求解最大公约数的方法

  • 诸位即使读不懂这段程序代码的内容也没有关系,这里需要诸位注意的是该算法所描述的步骤是可以直接转换成程序的。 代码清单5.1 求解12和42最大公约数的程序 
      a = 12
      b = 42
      While a <- b
          If a - b Then
              a = a - b
          Else
              b = b - a
          End If
      Wend
      MsgBox “最大公约数为” & CStr(b) & “。”  图5.3 代码清单5.1的执行结果

  • 笔者建议从事编程工作的人手中要有一本能作为算法辞典的书

  • 表5.1 主要的典型算法 

  • 这次请思考一下解决“求解12和42的最小公倍数”这个问题的算法。所谓最小公倍数就是指两个整数的公共倍数(是一个数几倍的数)中最小的那个数。最小公倍数的求解方法诸位在中学的数学课上也应该学过了,但是很可惜求解步骤是依赖人类的直觉的。请再思考一个适用于计算机的机械的算法。诸位说不定会想“反正会有典型算法的吧,比如‘某某氏的某某法’”,然后就纠结于是否还要自己思考。 但是即使查了算法辞典之类的书,也还是找不到求解最小公倍数的算法。为什么呢?因为我们可以通过以下方法求解最小公倍数——用两个整数的乘积除以这两个整数的最大公约数。因此12和42的最小公倍数就是12×42÷6 = 84了。如此简单的算法不能算作典型算法。这个例子说明先自己思考算法,再去应用典型算法这一点很重要。

  • 要点4:利用计算机的处理速度

  • 这次再请诸位思考求解“判定91是否是素数”这一问题的算法。在用于判定素数的典型算法中,有一个被称为“埃拉托斯特尼筛法”的算法。在学习这个算法之前,先请诸位思考如果是在数学考试中碰到了这道题,要如何解答呢? 也许有人会这样想:用91分别除以比它小的所有正整数,如果没有找到能够整除的数,那么91就是素数。但是,如此繁琐的步骤可行吗?实际上这就是正确答案。埃拉托斯特尼筛法是一种用于把某个范围内的所有素数都筛选出来的算法,比如筛选100以内的所有素数,其基本思路就是用待判定的数除以比它小的所有正整数。例如要判定91是否是素数,只要分别除以2~90之间的每个数就可以了(因为1肯定能够整除任何数,所以从2开始检测)。这个步骤用程序表示的话,就变成了如代码清单5.2所示的代码。Mod是用于求除法运算中余数的运算符。如果余数为0则表示可以整除,因此也就知道待判定的数不是素数了。程序执行结果如图5.4所示。 代码清单5.2 判定是否是素数的程序 
    a = 91
    s = “是素数。"
    For i = 2 to (a - 1)
        If a Mod i = 0 Then
            s = “不是素数。"
            Exit For
        End If
    Next
    MsgBox CStr(a) & s  图5.4 代码清单5.2的执行结果

  • 无论是多么冗长繁琐的步骤,只要明确并且机械就能构成优秀的算法。

  • 在思考算法时不防时刻记着,解决问题时是可以利用计算机的处理速度的

  • 鸡兔同笼问题:鸡和兔子共计10只,把它们的脚加起来共计32只,问鸡和兔子分别有多少只?设有x只鸡,y只兔子,那么就可以列出如下的联立方程组。 

  • 求解鸡兔同笼问题的程序

  • 解决一个问题的算法未必只有一种。在考量用于解决同一个问题的多种算法的优劣时,可以认为转化为程序后,执行时间较短的算法更为优秀。

  • 判定91是否是素数的过程一下子就有结果了,可是要去判定999999937的话,笔者的电脑就要花费大约55分钟之久(言外之意999999937是素数)。

  • 改成用待判定的数除以比它的1/2小的所有数,处理时间就会缩短。之所以改成这样是因为没有必要去除以比它的1/2还大的数。

2021/3/31 发表想法 注:其实处理时间还能够进一步缩短,即只需要从2除到待判定数的平方根就足够了。这是因为假设一个数n不是素数,也就是说它是两个数p和q(p≠1且q≠1)的乘积,那么因数p或q最大为n的平方根。

  • 通过这一点改进,除法运算的处理时间就能够缩短1/2。

  • 通过这一点改进,除法运算的处理时间就能够缩短1/2。

  • 在算法技巧中有个著名的技巧叫作“哨兵”。这个技巧多用在线性搜索(从若干个数据中查找目标数据)等算法中。线性搜索的基本过程是将若干个数据从头到尾,依次逐个比对,直到找到目标数据。

  • 假设有100个箱子,里面分别装有一个写有任意数字的纸条,箱子上面标有1~100的序号。现在要从这100个箱子当中查找是否有箱子装有写着要查找数字的纸条。

  •  图5.6 未使用哨兵的流程图

  • 需要多次反复检查的就只剩下“第N个箱子中包含要找的数字吗?”这一点了,程序的执行时间也因此大幅度地缩减了。

  •  图5.7 使用了哨兵的流程图

2021/3/31 发表想法 突然感觉很有意思。

  • 图5.7 使用了哨兵的流程图

  • 当笔者第一次得知哨兵的作用时,对其巧妙性感到惊叹,兴奋异常。有些读者会感到“不太明白巧妙在哪里”,那么就讲一个故事来解释哨兵的概念吧。假设某个漆黑的夜晚,诸位在海岸的悬崖边上玩一个游戏(请勿亲身尝试)。诸位站在距悬崖边缘100米的地方,地上每隔1米就任意放1件物品。请找出这些物品中有没有苹果。 诸位每前进1米就要捡起地上的物品,检查是否拿到了苹果,同时还要检查有没有到达悬崖的边缘(不检查的话就有可能掉到海里)。也就是说要对这两种检查反复若干次。 使用了哨兵以后,就要先把起点挪到距悬崖边缘101米的地方,再在悬崖的边缘放置一个苹果(如图5.8所示)。这个苹果就是哨兵。通过放置哨兵,诸位就一定能找到苹果了。每前进1米时只需检查捡到的物品是不是苹果就可以了。发现是苹果以后,只需站在原地再检查一步开外的情况。如果还没有到达悬崖边缘,就意味着找到了真正要找的苹果。已经达到了悬崖边缘,则说明现在手中的苹果是哨兵,而没有找到真正要找的苹果。  图5.8 使用了哨兵的游戏

  • 所有的信息都可以用数字表示——这是计算机的特性之一

  • 思考一下判定石头剪刀布游戏胜负的算法。如果把石头、剪刀、布分别用数字0、1、2表示,把玩家A做出的手势用变量A表示,玩家B做出的手势用变量B表示,那么变量A和B中所存储的值就是这三个数中的某一个。请以此判断玩家A和B的输赢。

  • 表5.2 判定石头剪刀布输赢的表 

  • 如果变量A和B相等就是“平局” ● 如果用B+1除以3得到的余数与变量A相等就是“玩家B获胜” ● 其余的情况都是“玩家A获胜”

  • 构造算法时需要找出数字间的规律不仅适用于数学游戏

  • 工资=底薪+加班补贴+交通补贴-预扣税款

  • 要点7:先在纸上考虑算法

  • 最后介绍最为重要的一点,那就是思考算法的时候,要先在纸上用文字或图表描述出解决问题的步骤,而不要立刻开始编写代码。

  • 验算的时候,建议使用简单的数据,这样即使是用心算也能得出正确的结果。

  • 辗转相除法

  • 曾经有一本被誉为凡是立志成为程序员的人都应该去读的名著,那就是Niklaus Wirth的Algorithms+Data Structures=Programs。

第6章 与数据结构成为好朋友的七个要点

  • 栈和队列的区别是什么?

  • 栈中数据的存取形式是LIFO;队列中数据的存取形式是FIFO。

  • 变量的实质是按照变量所存储数据的大小被分配到的一块内存空间。

  • 使用了数组就可以高效地处理大量的数据。数组的实质是连续分配的一块特定大小的内存空间。

  • 程序员有必要把算法(处理问题的步骤)和数据结构(作为处理对象的数据的排列方式)两者放到一起考虑

  • 因为依靠指定地址的方式编写程序很麻烦,所以在C语言、Java、BASIC等几乎所有的编程语言中,都是使用变量把数据存储进内存,或从内存中把数据读出来的。

  • 对于程序员来说,他们并不需要知道变量a被存储到内存空间中的哪个地址上了。因为当程序运行时是由操作系统为我们从尚未使用的内存空间中划分出一部分分配给变量a的。如图6.1所示,变量是程序中数据存储的最小单位,每个变量都对应着一块物理上的内存空间。

  •  图6.1 内存的物理结构以及它与程序的关系

  • 代码清单6.2中列出了一段程序,把三个数据分别存入a、b、c三个变量中,再将a、b、c中的数据的值按照降序(从大到小的顺序)排列。在排序时为了交换两个变量的值还需要用到tmp变量。程序使用if语句一对儿一对儿地比较变量的大小,并根据比较的结果交换变量的值。

  • 把存入到三个变量中的数值按照降序排列

  • 为了实现想要实现的算法,有时不能只依靠离散的变量。

  • 通过使用数组,既可以同时定义出多个变量,又可以提高编写程序的效率。

  • 在用C语言编写的程序中,是通过指定数组名和数组所包含的元素个数来定义数组、以供使用的

  • 数组实际上是为了存储多个数据而在内存上集中分配出的一块内存空间,并且为这块空间整体赋予了一个名字。

  • 在代码清单6.3中,通过定义数组,操作系统就分配出了一块用于存储3个数据所需的内存空间,并将这块空间整体命名为x。可以通过在“[”和“]”之间指定序号(索引)的方式分别访问数组内的各块内存空间。 本例中通过“char x[3]; ”这条语句就分配出了数组整体所需的内存空间,其中每个元素的内存空间可以通过x[0]、x[1]、x[2]的方式进行访问。虽然本质上还是定义出了x[0]、x[1]、x[2]三个变量,但是比起单独使用a、b、c,使用数组可以更加高效地编写出能够实现排序等算法的程序。

  • 数组是数据结构的基础,之所以这么说是因为数组反映了内存的物理结构本身。在内存中存储数据的空间是连续分布的

  • 在程序中,往往要从内存整体中分配出一块连续的空间以供使用。

  •  图6.2 数组反映了内存物理结构本身

  • 结构 数组是数据结构的基础,只要使用数组就能通过程序实现各种各样的算法以处理大量的数据。

  • 通常把像变量i这样的用于记录循环次数的变量称为循环计数器(Loop Counter)。

  • 数组之所以方便,就是因为可以把循环计数器的值与数组的索引对应起来使用(如图6.3所示)。

  •  图6.3 把循环计数器的值和数组的索引对应起来

  • 用“冒泡排序”这种典型算法,将存储在数组中的1000个数字按降序排列吧。

  • 在冒泡排序算法中,需要从头到尾地比较数组中每对儿相邻的元素的数值,然后反复交换较大的数值和较小的数值的位置。

  • 代码清单6.5 通过冒泡排序算法排列数据 
    for (i = 999; i - 0; i–){
        for (j = 0; j < i; j++){
            if (x[i] - x[j]){
                tmp = x[i];
                x[i] = x[j];
                x[j] = tmp;
            }
        }
    }

  • 通过使用数组和for语句,就能编写出实现了线性搜索和冒泡排序算法的程序。

  • 数组是一种直接利用内存物理结构(计算机的特性)的最基本的数据结构。

  • 这些数据结构其实都是通过程序从逻辑上改变了内存的物理结构,即数据在内存上呈现出的连续分布状态

  • 表6.1 主要的典型数据结构 

  • “栈”(Stack)的本意是干草堆(如图6.4所示)。在牧场中,把喂家畜吃的干草堆积在地上就会形成一座小山。为了把干草堆成山就要从下往上不断地堆积。在程序中干草就相当于数据。而在给家畜喂食的时候,则要按照从上往下的顺序把堆积起来的干草(数据)取下来

  • 数据的使用顺序与堆积顺序是相反的。通常把这种存取方式称为LIFO(Last In First Out,后进先出),即最后被存入的数据是最先被处理的。

  • “队列”(Queue)就是等待做某事而排成的队。笔者经常要在东京的西日暮里站从营团地铁换乘日本铁路。下了地铁就要去买日本铁路的车票,在购票窗口前买票的乘客会排成一队。这就是现实世界中的队列(如图6.5所示)。队列与栈正相反,排在队头的乘客可以最先买到车票。通常把这种形式称为FIFO(First In First Out,先进先出),即最先被存入的数据也是最先被处理的。当无法一下子处理完数据的时候,就可以暂且先把这些数据排成队。

  •  图6.4 栈的示意图  图6.5 队列的示意图

  • “链表”的概念就相当于几个人手拉着手排成一排(如图6.6所示)。某个人只要松开拉住的那只手,再去拉住另一只手,这一排人(相当于数据)的排列顺序就改变了。而只要先松开拉住的手,再让一个新人加入进来并拉住他的手,就相当于完成了数据的插入操作。  图6.6 链表的示意图

  • “二叉树”的概念正如其名,就相当于一棵树。不过这棵树与自然界中的树稍有些不同,二叉树从树干开始分杈,树枝上又有分杈,但每次都只会分为两杈,在每个分杈点上有一片叶子(相当于数据)(如图6.7所示)。稍后诸位就会了解到二叉树其实是链表的特殊形态。  图6.7 二叉树的示意图

  • 栈和队列的相似点在于,它们都可以把不能立刻处理的数据暂时存储起来;不同点在于,栈对所存储数据的存取方式是LIFO的,而队列对所存储数据的存取方式是FIFO的。

2021/4/1 发表想法 这就是为什么for遍历数组,删除某个元素时,i需要减1的原因。

  • 在这两个函数中,都需要更新栈中所存储的数据的总数,以及更新栈顶指针的位置。

  • 在这两个函数中,都需要更新栈中所存储的数据的总数,以及更新栈顶指针的位置。

  • 通过使用由数组、栈顶指针以及入栈函数和出栈函数所构成的集合,就能实现栈这种数据结构了

  • 代码清单6.6 使用数组、栈顶指针、入栈函数和出栈函数实现栈 
    char Stack[100];         /* 作为栈本质的数组 */
    char StackPointer = 0;  /* 栈顶指针 */
    /* 入栈函数 */
    void Push(char Data){
        /* 把数据存储到栈顶指针所指的位置上 */
        Stack[StackPointer] = Data;
        /* 更新栈顶指针的值 */
        StackPointer++;
    }
    /* 出栈函数 */
    char Pop(){
        /* 更新栈顶指针的值 */
          StackPointer–;
          /* 把数据从栈顶指针所指的位置中取出来 */
          return Stack[StackPointer];
      }

  •  图6.8 数组变成了“数据的小山”

  •  图6.9 数组变成了“数据之环”

  • 所谓结构体,就是把若干个数据项汇集到一处并赋予其名字后所形成的一个整体。

  • 一旦定义完结构体,就可以把结构体当作是一种数据类型,用它来定义变量。

  •  图6.10 结构体数组的示意图

  • 链表是一种类似数组的数据结构,这个“数组”中的每个元素和另一个元素都好像是手拉着手一样

  • 0所示)。代码清单6.10 带有指向其他元素指针

  • 在C语言中,把存储着地址的变量称为“指针”。这里的“”(星号)就是指针的标志。诸位可以看到,Ptr就是以结构体TestResult的指针(struct TestResult)为数据类型的成员。这种特殊的结构体可以称为“自我引用的结构体”。之所以叫这个名字,是因为在结构体TestResult的成员中,含有以TestResult的指针为数据类型的成员,这就相当于TestResult引用了与自身相同的数据类型。

  • 那么,接下来就是链表的有趣之处了。因为Ptr中存储的是与下一个数组元素的连接信息,所以只要替换了Ptr的值,就可以对数组中的元素排序,使元素的排列顺序不同于其在内存上的物理排列顺序

  • 只要明白了链表的构造,也就明白了二叉树的实现方法。在二叉树的实现中,用的还是自我引用的结构体,只不过要改为要带有两个连接信息的成员的自我引用结构体(如代码清单6.11所示)。

  • 无论是在哪种编程语言中,数据结构的基础都是数组,因此设法灵活地运用数组才是关键。

第7章 成为会使用面向对象编程的程序员吧

  • OOP是Object Oriented Programming(面向对象编程)的缩略语。

  • 面向对象编程是一种基于以下思路的程序设计方法:将关注点置于对象(Object)本身,对象的构成要素包含对象的行为及操作[插图],以此为基础进行编程。这种方法使程序易于复用,软件的生产效率因而得以提升。其中所使用的主要编程技巧有继承、封装、多态三种。

  • 其实无论是哪种方法,只要能够通过实际的编程将其付诸实践,那么这种方法就是正确的。

  • 面向对象编程通过把组件拼装到一起构建程序

  • 在面向对象编程中,使用了一种称为“类”的要素,通过把若干个类组装到一起构建一个完整的程序。从这一点来看,可以说类就是程序的组件(Component)。面向对象编程的关键在于能否灵活地运用类。

  • 无论使用哪种开发方法,编写出来的程序其内容最终都会表现为数值的罗列,其中的每个数值要么表示“指令”,要么表示作为指令操作对象的“数据”。程序最终就是指令与数据的集合。

  • 用“函数”表示指令,用“变量”表示数据。

  • 于是一种新的编程方法就被发明出来了,即把程序中有关联的函数和变量汇集到一起编成组。

  • 通常把汇集到类中的函数和变量统称为类的“成员”(Member)。

  • 为了使C语言支持面向对象编程,人们扩充了它的语法,开发出了C++语言。而通过改良C++又开发出了Java和C#。

  • 可维护性体现在当程序投入使用后对已有功能的修改和新功能的扩充上。

  • “我是创造类的人,你是使用类的人”——在实际应用面向对象编程时要带着这个感觉。开发小组中的全体成员没有必要都对程序中的方方面面有所了解,而是组中有些人只负责制作组件(类),有些人只负责使用组件。当然也会有需要同时做这两种工作的情况。

  • [插图]图7.2 只要把组件组合起来就可以高效地开发、轻松地维护

  • 对于创造类的程序员,他们考虑的是程序的开发效率和可维护性,并决定应该将什么抽象为类。如果一个类的修改导致其他的类就也要跟着修改,这样的设计是不行的。必须把组件设计成即使是坏了(有缺陷了)也能轻松地替换,就像在汽车或家电等工业制品中所使用的组件那样。

  • 请诸位记住,对于类的使用者而言“类看起来是什么样子的”这种关于规范的描述通常被称为“接口”(Interface)。

  • 为了拉近计算机和人的距离,使计算机成为更容易使用的机器,围绕着计算机的各种技术都在不断发展。在人的直觉中,大件物品都是由组件组装起来的。因此可以说面向对象编程方法把同样的直觉带给了计算机,创造了一种顺应人类思维习惯的先进的开发方法。

  • 在面向对象编程中,可以通过“这个是由什么样的对象构成的呢?”这样的观点来分析即将转换成程序的现实世界。这种分析过程叫作“建模”。

  • [插图]图7.3 在建模的过程中要进行抽象归类和忽略部分细节

  • 面向对象编程可以借助UML设计程序

  • UML是通过统一历史上曾经出现的各种各样的表记方法而发明出来的,事实上UML已经成为了建模表记方法中的世界标准。

  • 表7.1 UML中规定的九种图[插图]

  • 在进行面向对象编程的设计时,要在一开始就把所需要的类确定下来,然后再在每个类中列举出该类应该具有的函数和变量,而不要到了后面才把零散的函数和变量组织到类中。也就是说,要一边观察作为程序参照物的现实世界,一边思考待解决的问题是由哪些事物(类)构成的。正因为在设计时要去关注对象,这种编程方法才被称为面向对象编程(ObjectOriented Programming,其中的Oriented就是关注的意思)。

  • [插图]图7.4 UML类图的示例

  • 面向对象编程通过在对象间传递消息驱动程序

  • 用C++等面向对象编程语言编写程序的话,程序可以通过由一个对象去调用另一个对象所拥有的函数这种方式运行起来。这种调用方式被称为对象间的“消息传递”。

  • 沉浸在面向过程编程中的程序员们通常都习惯于用流程图思考程序的运行过程。可是为了实践面向对象编程,就有必要改用时序图来考虑程序的运行过程。

  • “继承”(Inheritance)、“封装”(Encapsulation)和“多态”(Polymorphism,也称为多样性或多义性)被称为面向对象编程的三个基本特性。

  • 继承指的是通过继承已存在的类所拥有的成员而生成新的类。封装指的是在类所拥有的成员中,隐藏掉那些没有必要展现给该类调用者的成员。多态指的是针对同一种消息,不同的对象可以进行不同的操作。

  • 因而有很多人就会被所学到的语法结构和编程技术中涉及的大量知识所束缚,以致不能按照自己的想法编写程序。其实只要沉静下来,不拘泥于语法和技术,转而去关注使用这三个特性所带来的好处,就能顺应着自己的需求恰当地使用面向对象编程语言了。

  • 只要去继承已存在的类,就能高效地生成新的类。如果一个类被多个类所继承,那么只要修正了这个类,就相当于把继承了这个类的所有类都修正了。只要通过封装把外界不关心的成员隐藏起来,类就可以被当作是黑盒,变成了易于使用且便于维护的组件了。而且由于隐藏起来的成员不能被外界所访问,所以也就可以放心地随意修改这些成员。只要利用了多态,生成对同一个消息可以执行多种操作的一组类,使用这组类的程序员所需要记忆的东西就减少了。总之,无论是哪一点,都是面向对象编程所带来的好处,都可以实现开发效率和可维护性的提升。

  • 类和对象的区别

  • 类是对象的定义,而对象是类的实例(Instance)。

  • 假设我们定义了一个表示企业中雇员的类Employee。如果仅仅是定义完就可以立刻使用类Employee中的成员,那么程序中实际上就只能存在一名雇员。而如果规定了要先创建类Employee的对象才能使用,那么就可以需要多少就创建多少雇员了(通过在内存上创建出类Employee的副本)。

  • “类是做饼干的模具,用模具做出来的饼干是对象”这句话的含义了吧。有了一个做饼干的模具(类),那么需要多少就能做出多少饼干(对象)。

  • 使用类的程序员可以通过三种方法使用类,关于这一点诸位要有所了解。这三种方法分别是:1.仅调用类所持有的个别成员(函数和变量);2.在类的定义中包含其他的类(这种方法被称作组合);3.通过继承已存在的类定义出新的类。应该使用哪种方法是由目标类的性质以及程序员的目的决定的。

  • Java和.NET其实是位于操作系统(Windows或Linux等)之上,旨在通过隐藏操作系统的复杂性从而提升开发效率的程序集,这样的程序集也被称作“框架”(Framework)。框架由两部分构成,一部分是负责安全执行程序的“执行引擎”,另一部分是作为程序组件集合的“类库”(如图7.8所示)。

  • [插图]图7.8 框架提供了高效的开发环境

  • 程序员是工程师,工程是一种亲身参与的活动而不是一门学问。请诸位把面向对象编程作为一种能提升编程效率、写出易于维护的代码的编程方法,在适当的场合实践面向对象编程,而不要被它各种各样的概念以及所谓的编程技巧所束缚。

  • 面向对象编程就是通过把组件拼装到一起进行编程的方法

第8章 一用就会的数据库

  • 表(Table)就是被整理成表格形式的数据。

  • 键用于设定表和表之间的关系(Relationship),而索引是提升数据检索速度的机制。

  • 其上每个值都能够唯一标识一条记录的字段称为主键。为了在表和表之间建立关系而在表中添加的、其他表主键的字段称为外键。而索引是与键无关的机制。

  • 所谓数据库(Database)就是数据(Data)的基地(Base)。

  • [插图]图8.2 使用关系型数据库实现的名片数据库的示意图

  • 数据库就默认是关系型数据库。

  • 数据库的实质虽然是某种数据文件,但是诸位编写的应用程序并不是直接去读写这些数据文件,而是以DBMS作为中介间接地读写(如图8.3所示)。

  • [插图]图8.3 DBMS是应用程序和数据文件的中介

  • [插图]图8.10 可以把多对多关系分解成两个一对多关系

  • 索引能够提升检索和排序的速度

  • 提升数据检索和排序速度的代价,就是插入或更新数据速度的降低。

  • 只有对那些要频繁地进行检索和排序的字段,才需要设置索引。

  • CRUD由以下四种操作的英文名称的首字母组成,即记录的插入(CREATE)、获取(REFER)、更新(UPDATE)、删除(DELETE)。

  • [插图]图8.14 能够进行CRUD操作的用户界面示例

  • 事务控制

  • [插图]图8.16 事务的开始、提交和回滚

  • 计算机是一种工具,它可以执行输入、计算、输出三种操作,并可以通过这一系列的操作处理某种数据。

  • 虽然在实际项目开发中使用的是Java,企业也希望新员工被分配到岗后就可以立刻用Java开始编写程序,但是作为第一门学习的编程语言,笔者并不推荐Java。理由源于最近的一种趋势,那就是与过去相比,立志成为程序员的新人们在编程方面的背景知识越来越少,甚至少到令人惊讶。

  • ●Java隐藏了算法和数据结构

  • 让缺乏计算机构造和编程方面知识的新人学习Java会怎样呢?Java是一种在屏蔽了计算机构造的框架中使用的编程语言。虽然使用了Java就可以进行面向对象编程,但这却是一种不用考虑计算机底层状况的编程方法。只要使用了Java提供的类库(代码的集合),不需要考虑算法和数据结构就能解决问题。举例来说,Java的程序员在使用栈这种数据结构时,只需要调用类名为Stack的类就可以轻易地实现功能,因为该类为程序员提供了栈结构本身以及入栈(Push)和出栈(Pop)方法。程序员完全可以无视栈顶指针[插图]的存在。

  • ● 先精通C语言再学习Java语言比较好笔者并不是讨厌Java。在Java的框架之上,若是进行面向对象编程,既可以高效地开发大规模的程序,又可以使其处于易于维护的状态之中。但是这些优点只体现在编写实际的业务程序上。对于缺乏计算机基础知识的新人而言,笔者大力推荐C语言。因为它既能够使程序员感知到计算机的构造,又迫使程序员殚精竭虑地去思考如何才能亲手实现算法和数据结构。以C语言为基础发展出了C++语言,而Java又是以C++为基础开发出的编程语言。如果精通了C语言,不但能够理解Java的优点(高效开发和易于维护),而且能够平滑地过渡到Java的语法结构上。虽然在新人培训上也是有时间限制的,但正所谓欲速则不达。笔者总觉得通过花费与培养一个Java程序员相同的时间,是可以培养出一个熟练掌握C语言的程序员的。然而对于后者,他的经验在Java上也是可以发挥作用的。

  • ● 若想在短时间内就让新人体会到编程的乐趣就使用Visual Basic吧

第9章 通过七个简单的实验理解TCP/IP 网络

  • LAN是Local Area Network(局域网)的缩略语。

  • TCP/IP 是 Transmission Control Protocol/InternetProtocol(传输控制协议和网际协议)的缩略语。

  • 所谓MAC地址就是能够标识网卡的编号。

  • 通常把在一栋建筑物内或是一间办公室里的那种小规模网络称作LAN。与此相对,把互联网那样的大规模网络称作WAN(Wide AreaNetwork,广域网)。

  • 乎所有的网卡都会在上市前被分配一个不可变更的MAC地址。

  • 人们把通过连接多台计算机所组成的、可用于交换信息的系统称为“网络”(Network)。

  • 因为信息可以以电信号的形式在网线中传播,所以计算机彼此之间就能够进行信息交换。但为了交换信息,还必须在发送者和接收者之间事先确定发送方式。这种对信息发送方式的规定或约束就称为“协议”(Protocol)。小到公司内部的网络,大到互联网,TCP/IP(Transmission ControlProtocol/Internet Protocol)协议族已然成为了现行的标准。

  • “集线器”(Hub)是负责把各台计算机的网线相互连接在一起的集线设备。“路由器”(Router)是负责把公司内的网络和互联网连接起来的设备。

  • [插图]图9.1 作为实验对象的网络环境

  • 通常把像这样部署在一间办公室内的小规模网络称作LAN;把像互联网那样将企业和企业联结起来的大规模网络称作WAN。

  • [插图]图9.2 CSMA/CD的工作方式

  • 在每一块网卡所带有的ROM(Read Only Memory,只读存储器)中,都预先烧录了一个唯一的MAC地址。网卡的制造厂商负责确定这个MAC地址是什么。因为MAC地址是由制造厂商的编号和产品编号两部分组成的,所以世界上的每一个MAC地址都是独一无二的。

  • MAC地址虽然可以在硬件层面上标识网卡,可是如果只有MAC地址也很不方便。因为企业或组织需要对计算机分组管理,但是他们却没有办法把MAC地址前面的若干位统一起来。而且在互联网那种把全世界的计算机都连接在一起的大型网络中,又必须要有一种机制能够把数据的发送目的地像邮政编码那样整理并标识出来。假如在互联网中只能使用MAC地址,那么会发生什么呢?在接入互联网的数量众多的计算机中,只有尚未进行任何分组处理的编号(MAC地址)。这样的话,仅仅是寻找信息的发送目的地就要花费大量的时间。因此,在TCP/IP网络中,除了硬件上的MAC地址,还需要为每台计算机设定一个软件上的编号。这个编号就是众所周知的IP地址。

  • 通常把设定了IP地址的计算机称为“主机”(Host)。因为路由器也算是计算机的一种,所以它们也有IP地址。在TCP/IP网络中,传输的数据都会携带MAC地址和IP地址两个地址。IP地址是一个32比特的整数,每8比特为一组,组间用“.”分隔,分成4段表示。8比特所表示的整数换算成十进制后范围是0~255,因此可用作IP地址的整数是0.0.0.0~255.255.255.255,共计4294967296个。通过IP地址就可以轻松地对计算机进行分组管理了。比如用IP地址中第1段到第3段的数值代表公司,用第4段的数值代表公司内部的计算机。例如,在AAA.BBB.CCC这个公司内,如果有一台计算机的编号是×××,那么它的IP地址就是AAA.BBB.CCC.XXX。而看到了AAA.BBB.CCC.YYY这样一个IP地址,就能知道它是这个公司内的另一台计算机。通常把IP地址中表示分组(即LAN)的部分称作“网络地址”、表示各台计算机(即主机)的部分称为“主机地址”。在本例中,AAA.BBB.CCC这一部分是网络地址,而XXX或YYY的部分是主机地址。下面进入实验,请诸位查看各自计算机上配置的IP地址。与之前相同,还是使用如下的命令。

  • DHCP的全称是Dynamic Host Configuration Protocol(动态主机设置协议)

  • 路由器是数据传输过程中的指路人

  • 路由器的工作原理就是查看附加到数据上的IP地址中的网络地址部分,只要发现这个数据不是发送给LAN内计算机的,就把它发送到LAN外,即互联网的世界中。

  • 路由器虽然看起来就是个小盒子,可实际上是一台神奇的计算机。

  • [插图]图9.6 路由器是互联网中的指路人

  • 通常把这种数据经过路由器转发的过程称为“路由”(Routing)。

  • 在命令提示符窗口中执行tracert命令后,就可以查看路由的过程了。执行时需要在tracert的后面指定一个主机名(或计算机名),作为数据的发送目的地。

  • tracert www.grapecity.com

  • DNS服务器可以把主机名解析成IP地址

  • 诸位的计算机都有一个主机名,每个LAN也都有一个域名。举例来说,笔者所使用的计算机的主机名是ma50j(源于这台计算机的型号),所在的LAN的域名是yzw.co.jp。把主机名和域名组合起来所形成的ma50j.yze.co.jp,就是能够标识笔者这台计算机的一个世界范围内独一无二的名字,这个名字与IP地址的作用是等价的。通常把这种由主机名和域名组合起来形成的名字称作FQDN(Fully Qualified Domain Name,完整限定域名)。

  • 在互联网中,难以记忆的IP地址使用起来很麻烦。于是人们就发明出了DNS服务器,这样只需要使用FQDN, DNS服务器就可以自动地把它解析为IP地址了(这个过程叫作“域名解析”)。

  • 在互联网的世界中,到处传输的都是附带了IP地址的数据。但是能够标识作为数据最终接收者的网卡的,还是MAC地址。于是在计算机中就加入了一种程序,用于实现由IP地址到MAC地址的转换,这种功能被称作ARP(AddressResolution Protocol,地址解析协议)。

  • ARP的工作方式很有意思。它会对LAN中的所有计算机提问:“有谁的IP地址是210.160.205.80吗?有的话请把你的MAC地址告诉我。”通常把这种同时向所有LAN内的计算机发送数据的过程称作“广播”(Broadcast)。通过广播询问,如果有某台计算机回复了MAC地址,那么这台计算机的IP地址和MAC地址的对应关系也就明确了。ARP的工作流程也是自动进行的,诸位并不会意识到。

  • IP协议用于指定数据发送目的地的IP地址以及通过路由器转发数据。

  • TCP协议则用于通过数据发送者和接收者相互回应对方发来的确认信号,可靠地传输数据。通常把像这样的数据传送方式称作“握手”(Handshake)(如图9.13所示)。TCP协议中还规定,发送者要先把原始的大数据分割成以“包”(Packet)为单位的数据单元,然后再发送,而接收者要把收到的包拼装在一起还原出原始数据。

  • [插图]图9.13 TCP的握手过程

  • 诸位敲打键盘输入的电子邮件正文等数据,并不是原封不动地发送出去的,而是先通过实现了TCP协议的程序附加上遵守TCP约束所需的信息,然后再通过实现了IP协议的程序,进一步附加上遵守IP约束所需的信息。实际上计算机发送的是以包为单位的、附加了各种各样信息的数据(如图9.14所示)。

  • [插图]图9.14 附加了各种各样信息的数据包

  • [插图]图9.15 实现了TCP/IP网络的程序的层级

  • 在深入学习的阶段,如果有条件进行实验,那么请务必动手做一做。因为通过实验学到的知识,人们往往会掌握得更扎实、记忆得更牢靠。

第10章 试着加密数据吧

  • 通常把这种未经加密的文本数据称为“明文”。

  • 数据一旦以明文的方式在网络中传输,就会有被盗取滥用的危险,因此要对明文进行加密,将它转换成为“密文”。

  • 虽然存在各种各样的加密技术,但是其中的基本手段无外乎还是字符编码的变换,即将构成明文的每个字符的编码分别变换成其他的数值。通过反转这种变换过程,加密后的文本数据就可以还原。通常把密文还原成明文的过程(即解读密码的过程)称为“解密”。

  • 代码清单10.1中,列出了一段用于加密的示例程序。在该程序中,使用了如下加密方法:将文本数据中每个字符所对应的字符编码一律向后错三个,即给原字符编码的值加上3。请把这段程序保存到以.vbs为扩展名的文本文件中,例如cipher1.vbs,然后把该文件保存到合适的文件夹中。接下来只需双击cipher1.vbs的图标即可运行这段程序。请试着在最初弹出的窗口中输入要加密的文本数据(明文),例如就输入NIKKEI吧,然后单击OK按钮。在接下来弹出的窗口中会显示出加密后的文本数据(密文)。因为每个字符的编码都向后错了三个,所以得到的是QLNNHL。这样的话,即便是被人偷偷地看到了,那个人也无法理解这个字符串的意义(如图10.1所示)。

  • 用给字符编码加上3的方法加密

  • 因为加密时使用的是将字符编码向后错三个的方法,所以只要再将字符编码向前挪三个就可以实现解密。

  • 也就是说,加上3就是加密,减去3就是解密。因此通常把像3这样用于加密和解密的数字称为“密钥”。如果事先就把3这个密钥作为只有数据的发送者和接受者才知道的秘密,那么不知道这个密钥的人,就无法对加密过的数据进行解密。

  • XOR运算(eXclusive OR,逻辑异或运算

  • 请诸位确认通过翻转和再翻转还原出字母N的过程:N的字符编码先和3做XOR运算,结果是字母M的字符编码。M的字符编码再和3做XOR运算,结果就又回到了N的字符编码。

  • [插图]图10.4 通过翻转和再翻转即可还原

  • 如果仅用一位数作为密钥,那么只需要从0到9尝试十次就能破解密文。但是如果是用三位数的密钥,那么就有从000到999的1000种可能。如果更进一步把密钥的位数增长到十位,结果会怎样呢?那样的话,破解者就需要尝试10的10次方 = 100亿次。就算使用了一秒钟可以进行100万次尝试的计算机,破解密文也还是需要花费100亿÷100万次/秒 = 10000秒≈2.78小时,坏人说不定就会因此放弃破解。密钥每增长一位,破解所花费的时间就会翻10倍。密钥再进一步增长到16位的话,破解时间就是2.78小时×1000000≈317年,从所需的时间上来看,可以说破解是不可能的。

  • 对称密钥加密技术

  • 这种加密技术的特征是在加密和解密的过程中使用数值相同的密钥。

  • 发送者如何才能把密钥悄悄地告诉接收者呢?

  • 互联网的存在应该意味着用户可以实时地与世界各地的人们交换信息。因此对称密钥加密技术不适合在互联网中使用。

  • 公开密钥加密技术

  • 在公开密钥加密技术中,用于加密的密钥可以公开给全世界,因此称为“公钥”,而用于解密的密钥是只有自己才知道的秘密,因此称为“私钥”。

  • [插图]图10.7 对称密钥加密技术和公开密钥加密技术

  • RSA算法

  • [插图]图10.9 用公钥加密,用私钥解密

  • 数字签名

  • 步骤中所提及的“信息摘要”(Message Digest)可以理解为就是一个数值,通过对构成明文的所有字符的编码进行某种运算就能得出该数值。

  • 其一是发送者承认文件的内容是完整有效的;其二是文件确实是由发送者本人发送的

  • 合理的密钥应该满足如下条件:长短适中、可以反复使用、可以通过某种通信手段交给接收者,并且通信双方以外的其他人难以用它来解密。公开密钥加密技术就完全满足上述条件,笔者在这里要对发明了这项技术的工程师们表达由衷的敬意。

第11章 XML究竟是什么

  • HTML和XML的区别是什么?

  • 中级问题:HTML是用于编写网页的标记语言。XML是用于定义任意标记语言的元语言。

  • DOM(Document Object Model,文档对象模型)。

  • 所谓标记语言,就是可以用标签为数据赋予意义的语言

  • 通常把通过添加标签为数据赋予意义的行为称为“标记”。为这种给数据赋予意义的行为定义规则的语言就是“标记语言”。

  • XML是一种标记语言。XML文件的扩展名一般是.xml (使用别的也可以)。

  • XML本身并不会限定标签的种类,反倒是允许XML的使用者随心所欲地创建标签。也就是说,在“<”和“>”中的单词可以是任意的。这就是所谓的“可扩展”。在HTML中,我们只能使用由HTML定义出的那若干种标签,因此HTML是固定的标记语言。与此相对,XML是可扩展的标记语言。

  • XML并没有限定标签的使用方式,使用什么样的标签都可以。可以说XML仅仅限定了进行标记时标签的书写格式(书写风格)。也就是说通过定义要使用的标签种类,就可以创造出一门新的标记语言。通常把这种用于创造语言的语言称作“元语言”。

  • XML中的主要约束[插图]

  • 通常把遵循XML约束、正确标记了的文档称作“格式良好的XML文档”(Well-formed XML Document)。

  • 只要能通过XML解析器的解析,就是格式良好的XML文档。

  • 在自己手动创建XML文档的时候,也可以利用Web浏览器带有的XML解析器,检查XML文档的格式是否正确。

  • HTML的用途就仅限于信息的可视化了,自始至终都用于展现网页。

  • XML的主要用途是为在互联网上交换的信息赋予意义(如图11.9所示)。

  • 人们使用XML这种元语言,又定义出了新的网页标记语言XHTML(Extensible HypertextMarkup Language,可扩展超文本标记语言),该语言也于2000年成为了W3C推荐标准。早晚有一天,XHTML会取代现行的HTML(HTML 4.0),成为编写网页的主流标记语言[插图]。

  • [插图]图11.9 HTML是给人看的,XML是给计算机看的

  • W3C的推荐标准是不依赖于特定厂商的通用规范。因此可以认为成为W3C推荐标准的XML是一种通用的数据交换格式。也就是说,如果某家厂商的某个应用程序把数据保存到了XML文件中,那么其他厂商的另一个应用程序就应该可以通过加载这个XML文件来使用数据。除此之外,XML也可以在同一个厂商的不同应用程序之间交换数据。

  • 在计算机行业,长久以来一直把CSV(Comma Separated Value,逗号分隔值)作为通用数据交换格式沿用至今。

  • CSV也是仅由字符构成的纯文本文件。

  • 在CSV文件中,记录的是经过“, ”(半角逗号)分割后的信息。

  • 在CSV中,只记录了信息本身,而并没有为各个信息赋予意义。

  • 请浏览一下图11.11所示的XML文件,里面使用了标签来描述购物网站中所需的信息。对比刚才的CSV文件,诸位有什么发现吗?只是瞥一眼,就能够看出来在XML文件中,因为标签为信息赋予了意义,所以分析起来更方便。但是,另一方面,文件的尺寸也变大了。刚才的CSV文件的大小不过50字节,而这个XML文件的大小是280字节,竟比CSV文件的5倍还多。文件尺寸增大,就意味着会占用更多的存储空间、需要更长的传输及处理时间。

  • XML文档并非互联网专用,但是XML确实是一种主要通过互联网在全世界的计算机之间交换数据时使用的数据格式。

  • [插图]图11.13 在大千世界中人们会为相同的标签赋予不同的意义

  • XML命名空间(Namespace inXML),旨在防止这种同形异义带来的混乱。所谓命名空间,通常是一个能代表企业或个人的字符串,用于修饰限定标签的名字。在XML文档中,通过把“xmlns=“命名空间的名字””作为标签的一个属性记述,就可以为标签设定命名空间。xmlns即XML NameSpace(命名空间)的缩写。通常用全世界唯一的标识符作为命名空间的名称。

  • 除了之前讲解过的“格式良好的XML文档”,还有一个词叫作“有效的XML文档”(Valid XML document)。所谓有效的XML文档是指在XML文档中写有DTD(Document Type Definition,文档类型描述)信息。

  • 完整的XML文档包括XML声明、XML实例和DTD三个部分。所谓XML声明,就是写在XML文档开头的、形如<? xml version=“1.0"encoding=“Shift_JIS”? >的部分。XML实例是文档中通过标签被标记的部分。而DTD的作用是定义XML实例的结构。

  • 用“<! DOCTYPE”和“]>”括起来的部分就是DTD。DTD定义了在标签中至少有一个标签;在标签中必须包含

    标签。

  • XMLSchema是为了XML新近研发的技术,因此它可以对XML文档执行更严格地检查,例如检查数据类型或数字位数等。

  • 的确存在着用于处理XML文档的程序组件。比如已成为W3C标准的DOM(Document Object Model,文档对象模型)以及由XML-dev社区开发的SAX(Simple API for XML)。其实无论是DOM还是SAX,都只是组件的规范,实际的组件是由某个厂商或社区提供的。

  • 如果使用的是Windows,那么就应该已经安装了一个由微软提供的、遵循了DOM规范的组件(一个名为msxml3.dll的DLL文件)。

  • 用XML定义的标记语言示例[插图]

  • SOAP(Simple Object Access Protocol,简单对象访问协议)可用于分布式计算。所谓分布式计算,就是把程序分散部署在用网络连接起来的多台计算机上,使这些计算机相互协作,充分发挥计算机整体的计算能力。简单地说,SOAP就是使运行在A公司计算机中的A程序,可以调用运行在B公司计算机中的B程序。

  • 无论是调用程序时所需的参数信息,还是程序执行后的返回结果,都可以用通用的数据格式XML表示(如图11.17所示)。

  • SOAP收发数据时所使用的传输协议并不固定,凡是能够收发XML数据的协议均可使用。一般情况下使用的是HTTP或SMTP协议。可以说SOAP的诞生使得人们可以更加轻松地构建分布式计算环境了。

  • [插图]图11.17 通过SOAP进行分布式计算

  • 请不要认为这等同于“今后所有的数据都应该是XML格式的”。

  • XML只有在充当通用数据格式时才有价值。

  • 只有在像互联网那样的环境中,运行在不同机器中的不同应用程序相互联结,XML才会大有作为。只有一台独立的计算机,或者只在一家公司内部的话,使用XML格式存储数据反而体现不出优势,仅仅是文件的尺寸变大从而浪费存储空间罢了。

  • 在分布式计算中,如果是由不同种类的机器互联组成的系统,那么使用基于XML的SOAP才是有意义的。

  • 如果环境中的机器和应用程序全部来自同一厂商,那么使用厂商自己定制的格式而并非基于XML的格式,反而可以更加快捷地处理信息。XML是通用的,但它不是万能的。笔者会把XML中的X看作是eXchangable(可交换的)而并非是eXtensible(可扩展的),诸位赞同这种看法吗?

第12章 SE负责监管计算机系统的构建

  • 软件开发过程的模型有“瀑布模型”“原型模型”“螺旋模型”等。

  • 瀑布模型

  • SE是自始至终参与系统开发过程的工程师

  • SE需要具备从硬件结构、软件的构建方法乃至横跨整个业务的广泛知识以及项目管理的经验。

  • SE就是自始至终参与系统开发过程的工程师,而不是只负责编程的程序员。所谓系统,就是“由多个要素相互发生关联,结合而成的带有一定功能的整体”。将各种各样的硬件和软件组合起来构建而成的系统就是计算机系统。

  • SE所需的技能和程序员所需的技能[插图]

  • 可以说SE是一种更接近“管理者”的职业,负责管理技术人员。若以建设房屋为例,程序员就相当于木匠,而SE则相当于木匠师傅或是现场监理。但是请不要误解,SE未必比程序员的职务高。从职业规划上来说,也不是所有的程序员将来都会成为SE。

  • [插图]图12.1 瀑布模型中的7个阶段

  • 在瀑布模型中,每完成一个阶段,都要书写文档(报告)并进行审核。进行审核时还需要召开会议,在会上由SE为开发团队的成员、上司以及客户讲解文档的内容。若审核通过了,就可以从上司或客户那里得到批准,继续进入后续的开发阶段。若审核没有通过,则不能进入后续的阶段。一旦进入了后续的阶段,就不能回退到之前的阶段。为了避免回退到上一阶段,一是要力求完美地完成每一个阶段的工作,二是要彻底地执行审核过程,这些就是瀑布模型的特征。这种开发过程之所以被称为“瀑布模型”,是因为开发流程宛如瀑布,一级一级地自上而下流动,永不后退。如图12.2所示,开发过程就好像是开发团队乘着小船,一边克服着一个又一个的瀑布(通过审核),一边从上流顺流而下漂向下游。而坐在船头的人当然就是SE了。

  • [插图]图12.2 瀑布模型的示意图

  • 表12.2 各个阶段所要书写的文档[插图]

  • 把从用户的角度看到的东西称为“外部”,把从开发者的角度看到的东西称为“内部”。

  • SE要分别书写“外部设计文档”“内部设计文档”和“程序设计文档”。

  • 到了“测试”阶段,测试人员要根据测试计划文档的内容确认程序的功能。在最后编写的“测试报告”中,还必须定量地(用数字)标示出测试结果。如果只记录了一些含糊的测试结果,比如“已测试”或是“没问题”,那么就难以判断系统是否合格了。

  • [插图]图12.3 把小的模块组装起来,构成大的计算机系统

  • 表12.3 具有代表性的程序设计方法[插图]

  • 原则1:计算机只能够做输入、运算、输出三种操作原则2:程序是指令和数据的集合原则3:计算机有自己的处理方法

  • 引进计算机系统的目的是通过用计算机替代靠手工作业进行的业务,来提升工作效率。因此在设计时,要使手工作业的业务顺应计算机的处理方式来进行替换,这一点也值得注意。

  • 所谓“对象”(Object),就是把指令和数据归拢到具有一定意义的组中而形成的整体。

  • [插图]图12.4 若应用了面向对象法,系统维护起来就简单了

  • SE所要具备的能力是多种多样的。这些能力大体上可以分为两类——技术能力(Technical Skill)和沟通能力(CommunicationSkill)。所谓技术能力,是指灵活运用硬件、软件、网络、数据库等技术的能力。而所谓沟通能力,是指和他人交换信息的能力,而且这里要求的是双向的信息交换能力。一个方向是从客户到SE,即SE倾听客户等的需求;另一个方向是从SE到客户,即SE向客户等人传达信息。SE必须同时具备技术能力和沟通能力。为此,首先就要牢牢地掌握这两种能力的基础知识,这点尤为重要。

  • 所谓技术能力的基础知识,就是从第1章开始一路讲解过来的内容,这里不再赘述。而所谓沟通能力的基础知识到底指的是什么呢?能够规规矩矩地打招呼、能够用正确的中文书写文档、能够声音洪亮地讲话……当然这些都很重要。因此可以说作为一般社会成员所需的常识,就是沟通能力的基础知识。在此之上,身为SE的社会成员还必须具备这一身份所特有的常识,那就是“懂得什么是IT”。

  • 作为SE站在客户的面前,客户就会把SE看作是了解IT的人(如图12.5所示)。

  • [插图]图12.5 SE的定位 = 懂IT的人

  • 因为客户最关心的是使用计算机解决眼前的问题,而并不是引进什么样的计算机系统。因此SE应该首先询问客户:“您遇到什么困难了吗?”倾听客户的难处,给出解决对策即IT解决方案,这才是SE的职责。

  • IT是Information Technology(信息技术)的缩写,也许翻译成“充分运用信息的技术”会更加容易理解。

  • [插图]图12.6 为了解决手工作业中的信息化问题而引进计算机

  • 在这个案例中,1台个人计算机+1台打印机+Windows+市场上出售的通信录软件(贺年卡软件等)这样的一套计算机系统就足够了(如图12.7所示)。

  • [插图]图12.7 市面产品也一样能构成出色的计算机系统

  • 为了即使硬盘出现了故障也不至于造成太大的损失,我们还要建议客户定期备份。

  • “您的信息的价值,是这些维护费所不能替代的”——若能这样劝说客户的话,客户就应该能接受建议。

  • 处于正常运转状态的比率叫作“设备利用率”。

  • [插图]图12.8 设备利用率的计算公式

  • 将计算机系统的构成要素设成多机备份,可以出乎意料地大幅度提升设备利用率。

  • 问题是若要立志成为计算机行业的专家,就不能仅仅关注技术了。虽然又懂技术又懂计算机确实让人感到兴奋,但如果只是这样的话,早晚有一天工作就会变得没那么有意思了。有些人在30岁左右就会选择离开计算机行业,不是因为他们追赶不上技术前进的步伐,而是因为他们感到工作变得无聊了。专家也好普通人也罢,只有为社会做出了贡献才能有成就感,才会觉得工作有意义。

  • 我们要让计算机技术服务于社会。如果能有这样的决心,就应该能作为一生的事业和计算机愉快地相处下去了吧。