《冒号课堂》
Icon could not be loaded
46 min read
#notes#Programming

Computers are useless. They can only give you answers. — Pablo Picasso

TOC

重要范式

编程范式

命令式编程

命令式编程(imperative programming)。用命令式编写的 程序由命令序列组成,即一系列祈使句:‘先做这,再做那’,强调‘怎么 做’。更学术点说,命令式编程是电脑——准确地讲,是冯·诺伊曼机 (von Neumann machine)——运行机制的抽象,即依序从内存中获取指 令和数据,然后去执行。从范式的角度看,其世界观是:程序是由若 干行动指令组成的有序列表。其方法论是:用变量来存储数据,用语 句来执行指令。

命令式编程是行动导向的,因而算法是显性而结果是隐性的;声明式编程是目标驱动的,因而目标是显性而算法是隐性的

结构化编程

结构化编程(structured programming或简称SP),它是在过程 式编程的基础上发展起来的。其本质是一种编程原则,提倡代码应具有 清晰的逻辑结构,以保证程序易于读写、测试、维护和优化。

典型语言:Pascal

声明式编程

主要包括函数式编程和逻辑式编程。

函数式语言典型语言:Lisp、Haskll、Scheme 逻辑式语言典型语言:Prolog

使用不同编程范式实现阶乘运算

int factorial(int n) {
	int f = 1;
	for (; n > 1; --n) f *= n;
 
	return f;
}
defun factorial(n) (
	if (= n 0) 1
	(* n (factorial(- n 1)))
)
factorial(0, 1).
factorial(N, F) :- M is N-1, factorial(M, Fm), F is N * Fm.

不同:C明确给出了阶乘的迭代算法,而Lisp仅描述了阶乘的递归定义,Prolog则陈述了两个关于阶乘的断言

迭代和递归的区别:

  1. 迭代比递归更符合命令式的思维模式,因前者贴近机器语言而后者贴近数学语言
  2. 除了尾递归(tail recursion)外,一般递归比迭代开销大
  3. 声明式语言提倡递归而不支持迭代
    1. 就语法而言,它不允许迭代中的循环变量
    2. 就视角而言,迭代着眼于微观过程而递归着眼于宏观过程

image.png

范式程序输入输出程序设计程序运行
命令式自动机初始状态最终状态设计指令命令执行
函数式数学函数自变量因变量设计函数表达式转换
逻辑式逻辑证明题设结论设计命题逻辑推理

对象范式(OOP)

函数是被动的实体,对象是主动的实体 过程式程序的世界是君主制的;OO程序是民主制的 封装使得公民拥有个体身份;继承使得公民拥有家庭身份;多态使得公民拥有社会身份 函数式、命令式、逻辑式互相平行,而OOP与他们正交 与其说OOP更具重用性,倒不如说更具易用性

并发范式(合作与竞争)

并发式编程以进程为导向,以任务为中心将系统模块化 并发式编程以资源共享与竞争为主线 程序设计将围绕进程的划分与调度、进程之间的通信与同步来展开

常用范式

泛型范式 Generic Programming

泛型编程是算法导向的,即以算法为起点和中心点,逐渐将所涉及的概念内涵模糊化、外延扩大化,将所涉及的运算抽象化、一般化,从而扩展算法的适用范围

STL有3要素: 算法、容器和和迭代器。算法是一系列可行的步骤;容器是数据的集合,是抽象化的数组;迭代器是算法与容器之间的接口,是抽象化的指针。算法串联数据,数据实化算法。

泛型编程不仅能泛化算法中涉及的概念(数据类型),还能泛 化行为(函数、方法、运算)。

对指定集合中满足指定条件的元素进行指定处理。

通过模板,泛化了容器———可以是数组、列表、集合、映射、队列、栈、字符串等等;泛化了元素——可以是任何数据类型,泛化了处理方法和限定条件——可以是任何函数。

这里的处理方法和限定条件不限于函数,还可以是函子(functor)——自带状态的函数对象;另外,迭代器也被泛化了——可以从前往后移动,,可以来回移动,可以随机移动,可以按任意预定义的规律移动。

泛型编程是算法导向的,以算法为中心,逐渐将其所涉及的概 念内涵模糊化、外延扩大化,并将其所涉及的运算抽象化、一般化,从 而提高算法的可重用性。

元编程 Meta Programming

元编程:关于程序的程序,或者说是编写、操纵程序的程序

元程序将程序作为数据来对待,能自我发现、自我赋权和自我升级,有着其他程序所不具备的自觉性、自适应性和智能性,可以说是一种最高级的程序。

// 元编程
template <int N>
struct factorial {
	enum { value: N * factorial<N - 1>::value };
};

template <> // 特化(speicialization)
struct factorial<0> { // 递归中止
	enum { value = 1 };
};

void main() {
	// 以下等价于 cout << 120 << end1;
	cout << factorial<5>::value << end1;
}

Lex Yacc ANTLR

编译器本身就是元编程的典型范例———把高级语言转化为汇编语言或机器语言的程序,不就是能写程序的程序吗?

产生式编程与静态元编程都能自动生成源代码。产生式编程强调代码的生成,元编程强调生成代码的可执行性。此外,动态元编程并不生成源代码,但能在运行期间修改程序。

切面范式(AOP)

从宏观角度看,太阳底下没有新鲜事——AOP无非是SoC原理和DRY原则的一种应用; 从微观角度看,太阳每天都是新的——AOP虽自OOP的土壤中长出,却脱离藩篱自成一体。

OOP 只能沿着继承树的纵向方向重用,AOP就是在管道上钻一些孔,在每个孔中注入新的代码流

抽象与分解的原则:单一化、正交化。每个模块职责明确专一;模块之间相互独立,即高内聚低耦合(high cohesion & low coupling)。此原则相当普适,是分析复杂事物的一种基本方法,在数学和物理中应用尤为广泛,如质因式分解、正交分解、谱分解等等。

OOP

A script is what you give the actors, a program is what you give the audience 脚本是给演员看的,节目是给观众看的。

RAII(Resource Acquisition Is Initialization): 资源获取即初始化,准确称为RRIF(Resource Release Is Finalization,资源释放即终结化) 将资源的取放于某一生命周期绑定,初始化对象时获取资源,终结化时释放资源,用户不再直接管理资源,只需控制相应的对象即可。

DTO(Data Transfer Object或DTO):数据传输对象,不含业务逻辑,仅作为简单数据容器,实际上也属于具体数据类型

Programming to an interface, not an Implementation 通过接口而非实现来编程

抽象:将一类模型最本质最不易变化的部分提炼出来 封装:将信息隐藏,即是将非本质、容易变化的部分隐藏起来,从而将一个类划分为阴阳两面

OCP(Open/Close Principle): 开闭原则,对扩展开放,对修改封闭

笔记

第1课 开班导言

对于一个软件开发者来说,这意味着4个阶段:学会(知其所然)——掌握一些具体编程知识的初级程序员。会学(知所以然)——能快速而深刻地理解技术并举一反三的程序员。会用(人为我用)——能将所学灵活运用到实际编程设计之中的高级程序员。被用(我为人用)——能设计出广为人用的应用程序(application)、库(library)、工具包(toolkit)、框架(framework)等的系统分析师和

架构师。至于被用的更高层次,如发明出主流的设计模式、算法、语言,乃至理论等,则可称得上计算机专家了

如果知识是水,我们要挖掘最先涌动的泉眼;如果知识是火,我们要捕捉起初点燃的火花。如果知识是树,其树大根深,不究立固之本则无以知过去;其枝繁叶茂,不握支撑之干则无以知当下;其蓬勃旺盛,不察生长之点则无以知将来。这里的问题不一定是预设的,结论不一定是终极的,甚至不一定是正确的,但一定是有的放矢、发人深思的。由此决定了这里的学习方式将是开放多元、双向互动的。

1.2 首轮提问——什么语言好?

一个优秀的程序员,除了要迅速掌握知识、善于领悟思想外,还必须具备务实与研究精神、独立与合作精神、批判与自省精神

1.3 语言选择——合适的就是好的

所以好的语言就是适合编程者和解决对象的语言

高级语言好比长兵器,威力强大却难免滞重,长于大型应用,可谓‘一寸长,一寸强’;低级语言好比短兵器,轻便灵活却难免风险,长于底层应用,可谓‘一寸短,一寸险’。

1.4 初识范式——程序王国中的世界观与方法论

评判语言优劣,不能离开使用语言的主体和对象。好的语言就是适合编程者和解决对象的语言

计算机语言按其发展历程分为5代,依次为:机器语言、汇编语言、高级语言、面向问题语言和人工智能语言

编程是为了解决问题,而解决问题可以有多种视角和思路,其中普适且行之有效的模式被归结为范式

由于着眼点和思维方式的不同,相应的范式自然各有侧重和倾向,因此一些范式常用‘oriented’来描

“按梦中情人的标准去找对象,具体目标未定但选择倾向已定,这就是一种导向,而且是对象导向。找到之后再约会,不就面向对象了吗?

每种范式都引导人们带着其特有的倾向和思路去分析和解决问题

1.5 开发技术——实用还是时髦?

Object-Oriented多译作“面向对象”,但不如“对象导向”贴切

如果把一门编程语言比作兵器,它的语法、工具和技巧等是招法,它采用的编程范式则是心法

范式的世界观体现在语言的核心概念之中,范式的方法论体现在语言的表达机制中

库和工具包是为程序员带来自由的,框架是为程序员带来约束的

设计模式是软件的战术思想,架构是软件的战略决策。

软件构筑了一个可重用的设计。与库和工具包不同之处在于前者侧重设计重用而后两者侧重代码重用

库和工具包是为程序员带来自由的,框架是为程序员带来约束的

至于架构,一般指一个软件系统的最高层次的整体结构和规划,一个架构可能包含多个框架,而一个框架可能包含多个设计模式

第2课 重要范式

从范式的角度看,其世界观是:程序是由若干行动指令组成的有序列表。其方法论是:用变量来存储数据,用语句来执行指令。

2.2 声明范式——目标决定行动

转化为指定的输出

命令式编程是行动导向(Action-Oriented)的,因而算法是显性而目标是隐性的;声明式编程是目标驱动(Goal-Driven)的,因而目标是显性而算法是隐性的

一则迭代比递归更符合命令式的思维模式,因为前者贴近机器语言而后者贴近数学语言;二则除尾递归(tail recursion)[10]外,一般递归比迭代的开销(overhead)大。相反,声明式语言提倡递归而不支持迭代[11]。就语法而言,它不允许迭代中的循环变量;就视角而言,

迭代着眼微观过程而递归着眼宏观规律

命令式把程序看作一个自动机,输入是初始状态,输出是最终状态,编程就是设计一系列指令,通过自动机执行以完成状态转变;函数式把程序看作一个数学函数,输入是自变量,输出是因变量,编程就是设计一系列函数,通过表达式变换以完成计算;逻辑式把程序看作一个逻辑证明,输入是题设,输出是结论,编程就是设计一系列命题,通过逻辑推理以完成证明

声明式编程专注问题的分析和表达而不是算法实现,不用指明执行顺序,一般没有或极少有副作用,也不存在内存管理问题。这些都大大降低了编程的复杂度,同时也非常适合于并发式计算

2.3 对象范式——民主制社会的编程法则

:自动机机制,通过设计指令完成从初始态到最终态的转变。函数式:数学变换机制,通过设计函数完成从自变量到因变量的计算。逻辑式:逻辑证明机制,通过逻辑推理完成从题设到结论的证明。

函数是被动的实体,对象是主动的实体

封装使得公民拥有个体身份,继承使得公民拥有家庭身份,多态使得公民拥有社会身份。

实软件设计最重要的并不是编程语言,甚至也不是编程范式,而是抽象思维

以数据为中心组织逻辑,将系统视为相互作用的对象集合,并利用继承与多态来增强可维护性、可扩展性和可重用性

2.4 并发范式——合作与竞争

并发式编程以进程为导向、以任务为中心将系统模块化

并发式编程以资源共享与竞争为主线

尾递归是一种特殊的递归,其递归调用出现在函数的最后一步运算(尾部)

第3课 常用范式

“Generic Programming,简称GP,其基本思想是:将算法与其作用的数据结构分离,并将后者尽可能泛化,最大限度地实现算法重用

STL有3要素:算法、容器和和迭代器。算法是一系列可行的步骤;容器是数据的集合,是抽象化的数组;迭代器是算法与容器之间的接口,是抽象化的指针。算法串联数据,数据实化算法

泛型编程不仅能泛化算法中涉及的概念(数据类型),还能泛化行为(函数、方法、运算)

3.2 超级范式——提升语言的级别

元编程(Metaprogramming),简称MP。此处的前缀‘meta-’常译作‘元’,其实就是‘超级’、‘行而上’的意思

有了编译器的存在,汇编语言升级为第3代高级语言;同样借助Yacc、ANTLR之类的元编程工具,第3代语言可以升级为第4代的DSL语言。

语言导向式编程[5](Language-Oriented Programming,简称LOP)

这种编程范式的思路是:在建立一套DSL体系之后,直接用它们来编写软件,尽量不用通用语言。

如果说OOP的关键在于构造对象的概念,那么LOP的关键在于构造语言的语法

—产生式编程(Generative Programming)[7]的范畴

元编程有诸多应用:许多开发工具、框架引擎之类的基础软件都有自动生成源代码的功能;创造DSL以便更高效地处理专门领域的业务;自动生成重复代码;动态改变程序的语句、函数,类,等等

3.3 切面范式——多角度看问题

如果一个程序是一个管道系统,AOP就是在管道上钻一些孔,在每个孔中注入新的代码流

抽象是前提,分解是方式,模块化是结果。

在调用某些对象的方法、读写某些对象的域、抛出某些异常等前后需要用到统一的业务逻辑,诸如日志输出、代码跟踪、性能监控、异常处理、安全检查、事务管理,等等。为解决此类问题,AOP应运而生

AOP实现的关键是将advice的代码嵌入到主体程序之中,术语称编织(weaving)

编织可分两种:一种是静态编织,通过修改源码或字节码(bytecode)在编译期(compile-time)、后编译期(post-compile)或加载期(load-time)嵌入代码

另一种是动态编织,通过代理(proxy)等技术在运行期(run-time)实现嵌入

如果把一个复杂的系统看作复合色的白光,经过第1个三棱镜——关注分离器,系统被分解为不同的切面,如同不同的单色的彩光。这些切面经过第2个三棱镜——编织器,再度合成为原系统

抽象与分解的原则是单一化和正交化,以保障软件系统符合“高内聚、低耦合”的要求

3.4 事件驱动——有事我叫你,没事别烦我

接入点是附加行为——建议(advice)的执行点,切入点(pointcut)是指定的接入点(join point)集合,这些接入点共享一段插入代码。切入点与建议组成了切面(aspect),是模块化的横切关注点

控制反转、依赖反转和依赖注射)的主题是控制与依赖,目的是解耦,方法是反转,而实现这一切的关键是抽象接口。

回调”强调的是行为方式——低层反调高层,而“抽象接口”强调的是实现方式——正是由于接口具有抽象性,低层才能在调用它时无须虑及高层的具体细节,从而实现控制反转

控制反转导致了事件驱动式编程的被动性

事件驱动式还具有异步性的特征,这是由事件的不可预测性与随机性决定的。

独立是异步的前提,耗时是异步的理由

发行/订阅模式正是观察者模式的别名,一方面可看作简化或退化的事件驱动式,另一方面可看作事件驱动式的核心思想

好莱坞经纪公司相当于一个背后运作的软件平台,艺人相当于一个callback,‘留下你的电话’就是注册callback,‘我们会打给你的’就是异步调用callback。

别打电话给我们’意味着经纪公司处于主导地位,艺人们处于受控状态,这便是控制反转(Inversion of Control,简称IoC)。”

“一般library中用到callback只是局部的控制反转,而framework将IoC机制用到全局

程序员牺牲了对应用程序流程的主导权,换来的是更简洁的代码和更高的生产效率

如果将编程譬比命题作文,不用framework的程序是一张可以自由写作的白纸,library是作文素材库;采用framework的程序是一篇成型的作文,作者只须填写空白的词语和段落即可。

“控制反转不仅增强了framework在代码和设计上的重用性,还极大地提高了framework的可扩展性。

依赖反转原则(Dependency-Inversion Principle,简称DIP)更加具体——高层模块不应依赖低层模块,它们都应依赖抽象;抽象不应依赖细节,细节应依赖抽象

依赖注射(Dependency Injection,简称DI)——动态地为一个软件组件提供外部依赖

回调函数的提法较为古老,多出现于过程式编程,抽象接口是更现代、更OO的说法

‘回调’强调的是行为方式——低层反调高层,而‘抽象接口’强调的是实现方式——正是由于接口具有抽象性,低层才能在调用它时无须虑及高层的具体细节,从而实现控制反转。

如何设计事件机制?其中,包括事件定义、事件触发、事件侦查、事件转化、事件合并、事件调度、事件传播、事件处理、事件连带(event cascade)[14]等一系列问题。

“软件的可伸缩性(scalability)一般指从容应对工作量增长的能力,常与性能(performance)等指标一并被考量

控制反转的主要作用是降低模块之间的依赖性,从而降低模块的耦合度和复杂度,提高软件的可重用性、柔韧性和可扩展性,但对可伸缩性并无太大帮助

独立是异步的前提,耗时是异步的理由。至于随机嘛,只是副产品,一个独立且耗时的子过程,通常结束时间也是不可预期的

事件处理器事先在关注的事件源上注册,后者不定期地发表事件对象,经过事件管理器的转化(translate)、合并(coalesce)、排队(enqueue)、分派(dispatch)等集中处理后,事件处理器接收到事件并对其进行相应处理

通过事件机制,事件源与事件处理器之间建立了松耦合的多对多关系:一个事件源可以有多个处理器,一个处理器可以监听多个事件源

事件处理器也能产生事件,实现处理器接口的事件源也能处理事件,它们可以角色换位,于是又演化为peer-to-peer模式

该模式省略了事件管理器部分,由事件源直接调用事件处理器的接口。这样更加简明易用,但威力有所削弱,缺少事件管理、事件连带等机制

事件是程序中令人关注的信息状态上的变化。在基于事件驱动的系统中,事件包括内建事件与用户自定义事件,其中内建事件又分为底层事件和语义事件。此外,事件还有自然事件与合成事件之分

异步过程在主程序中以非堵塞的机制运行,即主程序不必等待该过程的返回就能继续下一步。异步机制能减少随机因素造成的资源浪费,提高系统的性能和可伸缩性

第4课 重温范式

编程语言的语法、语义等都是从编程范式的树根衍生而出的枝叶,把握了这种脉络和节奏,代码才会如音乐舞蹈般韵律有致

容忍无知不是放任无知,而是一种学习的技巧,让无知成为求知的动力而不是障碍。容忍无知能使我们既不沮丧气馁,也不急于求成。在学习时不妨略过一些细节或难点,先概览全貌以获取感性认识,然后在逐步积累中升华为理性认识

如果你不了解编程范式,那么眼中的编程语言只是语法、语义、核心库、规范等组成的集合,写出的代码虽能编译、能工作,却会显得生硬、别扭

软件中的范式除了编程范式外,还有架构范式[1]、数据库范式[2]等

函数式还有一个重要特征:无副作用或尽量减少副作用[4]。所谓无副作用,是指一个函数在被调用前后保持程序的状态不变。无副作用的函数不会改变非局部变量的值,不会改变传入的参数,也没有I/O操作。

而函数式程序则是进行表达式变换,一般不会改变变量的值。其实函数式并非完全不改变内存,只不过改变的是栈内存(stack)罢了

首先,没有副作用的函数易于重构、调试和单元测试。其次,代码有效性与函数顺序无关,方便并发处理和优化处理

通常要计算f(g(x))的值,须要计算完g(x)后才能将所得值代入函数f。有了惰性求值机制,g(x)的计算完全由函数f的需求来驱动,避免做无用功。此乃其惰性之所在。

惰性求值不仅能节省有限的时间,还能超越无限的时间——g(x)甚至可以永不退出,从而可能产生无穷的输出结果集供函数f使用

没有副作用的函数是引用透明的(referential transparency),即一个表达式随时可以用它的值来替换[6],正如数学中的函数一样

4.2 逻辑范式——当算法失去了控制

逻辑式与过程式和函数式的一个不同之处是,它没有明显的输入、输出之分

:算法=逻辑+控制。其中逻辑是算法的核心,控制主要用于改进算法的效率。在逻辑式编程中,程序员只需表达逻辑,而控制交给编程语言的解释器或编译器去管理。

但缺点是运行效率偏低,可掌控性较差,与常规的过程式思维差异较大,更适合基于规则(rule-based)而不是基于状态(state-based)的应用[13]

逻辑式编程不仅适用于人工智能方面的学术领域,同样广泛适用于各种涉及知识管理、决策分析等方面的应用领域

4.3 汇总范式——一张五味俱全的大烙饼

“设计模式一般针对某一特定场景的问题,而编程范式针对的是广泛得多的问题领域,通常有一整套的思想和理论体系,具有全局性、系统性和渗透性,这一点在5大重要范式中显得尤为突出。因此,编程范式更普适更抽象,涉及的深度和广度也是设计模式难以比拟的

设计模式是遵循设计原则的一些具体技巧,以保证代码的可维护性、扩展性和可重用性为目的。它重在设计,对语言一般没有要求[15]。编程范式则不同,对语言往往有专门的要求

语言本来就是围绕其所倡导的核心范式来设计的

4.4 情景范式——餐馆里的编程范式

编程范式的核心价值在于:突破原有编程方式的某些限制,带来新思维和新方法,从而进一步解放程序员的劳动力

可以这么理解(闭包):所谓包,指函数与其周围的环境变量捆绑打包;所谓闭,指这些变量是封闭的,只能为该函数所专用

正向推理自底向上,利用推理规则从已有的事实数据推出更多的数据,直到达成目标;逆向推理正相反,自顶向下,从目标出发寻找满足结论的事实[21]。相比而言,正向推理适合针对不同输入作出不同反应,而逆向推理适合回答查询。

第5课 语言小谈

“软件工程中有个迭代开发法,本班则采用迭代学习法:即在具体知识与抽象理论之间进行折返式学习

5.2 数据类型——规则与变通

所谓迭代学习法,是指在具体知识与抽象理论之间进行增量式的循环学习

要想工作胜任愉快,才能、兴趣、方法和努力缺一不可。一套好的方法可以激发才能、兴趣和努力

数据类型包含两个要素:一个是允许取值的集合,一个是允许参与的运算

数据类型的意义何在?”句号回答:“限定一个变量的数据类型,就意味着限制了该变量的取值范围和所参与的运算,这从一定程度上保证了代码的安全性。”冒号追问:“还有吗?”句号略作思考后说:“用户自定义的数据类型,如C中的结构和Java中的类或接口,赋予数据以逻辑内涵,提高了代码的抽象性

“数据类型既有针对机器的物理意义,又有针对人的逻辑意义。前者用于进行底层的内存分配和数值运算等,后者用于表达高层的逻辑概念

所谓动态类型语言(dynamic typing language),正是指类型检查发生在运行期间(run-time)的语言。”“那静态类型语言(static typing language)自然是类型检查发生在编译期间(compile-time)的语言

类型的动静与强弱完全是正交的两个概念

前者以类型的绑定(binding)时间来划分,后者以类型的约束强度来划分

鸭子类型是动态类型的一种风格,允许非继承性多态,即一个对象的类型可以由其接口集合来确定,不须要通过显式继承。它有利于代码重用,但也可能造成误用和滥用

类型的动静以类型的绑定时间来划分,类型的强弱以类型的约束强度来划分,它们之间没有必然联系。弱类型语言允许类型的隐性转化,被认为是类型不安全的;而强类型语言则一般不允许这种转化,被认为是类型安全的

5.3 动态语言——披着彩衣飞舞的脚本语言

“脚本(script)的提法,是为了区别于一般的程序(program)

:‘A script is what you give the actors, a program is whatyou give the audience’。直译为:脚本是给演员看的,节目是给观众看的

脚本语言以语言的实际用途为标志,动态语言以语言的语法特征为标志

单从用途上看,一个脚本语言如果不再局限于命令行工具和粘合工具,从专用语言发展为通用语言,并能胜任复杂的应用开发,或许更有资格归为动态语言。

再从用法上看,动态语言能在运行中增加或改变数据结构、函数定义、对象行为或指令流程等。如果说动态类型语言的动态体现在类型上,动态语言的动态则体现在结构和功能上

➢脚本语言一般是解释型语言,不须要通过“编写-编译-链接-运行”的循环圈,便利快捷,加之简洁宽松的语法、面向字符的特性,以及较强的文本处理能力,尤其适合作为粘合语言,多用于系统管理和集成。

5.4 语言误区——语言的宗教情结

如果一种语言不能影响你对编程的看法,那么就不值得去了解

破除语言的宗教情结,保持自我批判的勇气和精神

每种语言都有其特到之处和不足之处,与其抱怨争执,不如扬长避短

第6课 语言简评

将资源的取放与某一对象的生命周期绑定,初始化对象时获取资源,终结化对象时释放资源。用户代码不再直接管理资源,只须控制相应的对象即可

专门提供了using语句来简化释放资源(IDisposable)的代码

6.3 前台语言——视觉与交互的艺术

JavaScript作为一门动态语言,集过程式、对象式、函数式、事件驱动式、元编程等于一身