框架与工具包最大的差别在截然相反的设计理念上: 库和工具包是为程序员带来自由的,框架是为程序员带来约束的。具体地说,库和工具包是为程序员提供武器装备的,框架则利用控制反转(IoC)机制实现对各模块的统一调度,从而剥夺了程序员对全局的掌控权,使他们成为手执编程武器、 随时听候调遣的士兵。
框架是在语言的语法规则之外施加于程序员的又一层枷锁,但没有规矩不成方圆。正如行军打仗,讲究排兵布阵,程序员就是那兵,框架就是那阵。
离开 IDE 就无法编写、编译或调试的程序员,如同卸盔下马后便失去战斗力 的武士,是残缺和孱弱的。
追星族倾向于盲目追捧,唯恐落伍,他们信奉新潮的、流行的就是好的;守旧派倾向于本能 抗拒,回避求新,他们认为经典的、传统的才是好的。
抽象是前提,分解是方式,模块化是结果。
单靠记忆只能触及知识之表,单靠练习只能深入知识之里,唯有培养方能渗透知识之根。
学会适度地容忍无知。请注意,容忍无知不是放任无知,而是一种学习的技巧,让无知成为求知的动力而不是障碍。容忍无知能是我们既不沮丧气馁,也不急于求成。在学习时不妨略过一些细节或难点,先概览全貌以获取感性认识,然后逐步积累中升华为理性认识。
不仅需要强调钻劲和深度的“钉子精神”,还需要强调磨功和广度的“刨子精神”。
编程语言的语法、语义等都是编程范式的树根衍生而出的枝叶,把握了这种脉络和节奏,代码才会如音乐舞蹈般韵律有致。
每种范式擅长的问题领域不尽相同,只有博闻广识,方可扬长避短,程序才会如行云流水般流畅自然。
程序员更习惯机器风格的过程式思维和现实风格的 OOP 思维,不容易接纳数学风格的函数式思维。
学习如打仗,除了要有直线式的纵深攻击,还要有曲线式的迂回包抄。
每种范式都代表着一套独特而有效地解决问题的思想和方法。
武功练至化境,一定是博采众长,就像杨过融合了东邪、西毒、南帝、北丐、中神通等各派武功,才能随心所欲地打出黯然销魂掌来。
过程式与函数式的差别同时也是机器思维与数学思维的差别。
没有副作用的函数是引用透明的(referential transparency),即一个表达式随时可以用它的值来替换,正如数学中的函数一样,保证了数学思维的贯彻和运用。
函数式编程不仅有许多独特的概念和方法,还有很深的数学背景--lambda-演算(lambda calculus)。(lambda演算,被证明是图灵完备的。)
评价代码的复杂度,长短只是一个因素。程序员不是打字员,花在思考上的时间和精力远远超过花在键盘上。
算法=逻辑+控制。其中逻辑是算法的核心,控制主要用于改进算法的效率。在逻辑式编程中,程序员只需表达逻辑,而控制交给编程语言的解释器或编译器去管理。
用数学逻辑的话来说,事实与规则是公理,查询就是特证的定理。
相对于命令式,逻辑式更简洁、更抽象、更少副作用,不仅能提高生产效率,还能用于快速原型开发。但在运行效率、可掌控性、语言成熟度等方面有所欠缺。另外,因其思维方式独特而鲜为人用,适合基于规则而非基于状态的应用。
设计模式好比组合套路,能在一些特定场合下克敌制胜;编程范式则好比武功门派,博大精深而自成体系。
因其长而容己,因其短而容他,此万物之理也。语言为形,范式为神。若能以神导形、以形传神,则看似平白无趣的程序也能写出诗画般的意境。
同样的思想用在整体系统的结构设计上,则称为架构模式;用在局部模块的细节实现上,则称为设计模式;用在引导编程实践上,则称为编程范式。
一种编程范式之所以能独树一帜,关键在于它突破了原有的编程方式的某些限制,带来革命性的新思维和新方法,进一步解放了程序员的劳动力。这便是范式的核心价值所在。
理论是认生的孩童,多陪他玩玩,自会活泼起来。
可以这么理解(闭包):所谓包,指函数与其周围的环境变量捆绑打包;所谓闭,指这些变量是封闭的,只能为该函数所专用。
合理地使用闭包能使代码更加简洁清晰,散发出函数式特有的优雅气质。每种编程范式都能在生活中找到它的应用,它们本来就是人类思维方式的投影。
编程水平的提升之道是:在实战中演练招法,在招法中领会心法,心法反过来提升招法,进而提高实战水平,如此循环往复呈螺旋式上升。正所谓熟能生巧,巧能生通。
无论干哪一行,要想胜任愉快,离不开四样东西:才能、兴趣、方法和努力。没有才能则难以胜任,没有兴趣则难以愉快,没有方法则事倍功半,没有努力则一事无成。我相信你好的方法最终能激发人的才能、兴趣和努力。
编程范式绝非中看不中用的屠龙之书,它有助于我们更快速地掌握、更深刻地理解、更纯熟地运用编程语言,故有心法之谓。其次,心法只有通过招法才能落到实处,也只有通过招法才能融会贯通。
迭代学习法:即在具体知识与抽象理论之间进行折返式学习。
网页的迷人之处就在于,能够用精美的画皮来包裹冗长低效的代码。
规则如裤带,过于宽松和过于束缚都不好。
数据类型包含两个要素:一个是允许取值的集合,一个是允许参与的运算。
数据类型既有针对机器的物理意义,又有针对人的逻辑意义。前者用于进行底层的内存分配和数值运算等,后者用于表达高层的逻辑概念。既然类型如此重要,类型检查就必不可少了。所谓动态类型语言,正是指类型检查发生在运行期间的语言。静态类型语言是类型检查发生在运行之前(将“编译期间”改为“运行之前”,否则容易让人误解为静态类型语言一定是编译型语言)的语言。
Duck类型的哲学:名义不重要,重要的是能力。
静态类型检查类似“疑罪从有”的有罪推定制——在被证明合法之前是非法的,动态类型检查类似“疑罪从无”的无罪推定制——在被证明非法之前是合法的。
‘Static Typing Where Possible, Dynamic Typing When Needed’
尽可能守规则,必要时求变通。
类型的动静以类型的绑定时间来划分,类型的强弱以类型的约束强度来划分,它们之间没有必然联系。
动态语言秉承的一个理念是:优化人的时间而不是机器的时间。为提高人的生产率,宁肯牺牲部分的程序性能或者购买更高配置的硬件。由于硬件相对于人件一直在贬值,该理念便有了合理的现实基础。
A script is what you give the actors, a program is what you give the audience.
换言之,程序是为终端用户服务的,而脚本是为程序员服务的。
不妨这么理解,脚本语言以语言的实际用途为标志,动态语言以语言的语法特征为标志。
程序员本就是双重人格的。
待静态语言披盔戴甲、备马抬抢之际,动态语言已衣袂飘飘,长剑出手了。不过,如果是应付强敌的长期大规模作战,静态语言还是有优势的。
程序员只要保持严谨的作风和开放的心态,既有稳如泰山的马步,又有一跃凌空的飞腿,静如处子,动如脱兔,如履平地般游走于高高的梅花桩上,绝无跌落之虞。
如果说编程范式是一种文化,那么编程语言更像是一种宗教——尽管它本不该是。每种流行的语言都有大批忠实的信徒,随时可能与其他的信徒发生宗教战争。
对待一门语言的态度应该是:与其抱怨争执,不如扬长避短。
架构师并没有多么神秘,他们也是从程序员过来的。也不要以为架构师就不关心语言了,相反需要对语言有更广博、更深刻的认识。理想的架构师应当如文学大师,既有恢弘大气的构思,又有细腻深刻的笔法;应当如统军大帅,既有运筹帷幄的韬略,又有冲锋陷阵的武功。那些在语言与低级之间、设计与高级之间毫不犹豫地划等号的人,多半高不成低不就,既不懂语言,也不懂设计。
从心理学上分析,一个人的某种观点形成后,会通过自我的暗示和倾向性的证据不断地强化这种观点,并对其他观点本能地选择性失明。这背后折射的其实是一种懒人心态——认定自己掌握的语言是最好的,便不必费事再学其他语言了;这更是一种弱者心态——无论是耿耿忠心的铁杆卫士,还是振振有词的辩护律师,一旦丧失自我批判的勇气和精神,声嘶力竭的挞伐只能反证他们的偏狭浅薄与自信缺失。
每种语言都有天然设计上的不同,当你用得很别扭时,恰恰说明没有真正掌握。正如刀法以劈为主,枪法以扎为主,你若反其道而行之,刀扎枪劈,能不别扭吗?如果愣拿双节棍当单节棍使,恐怕没砸到别人倒先砸晕自己了。可见不是兵器问题,而是招法问题。
There are only two kinds of languages: the ones people complain about and the ones nobody uses.
从另一个角度看,发明一种语言也是对先前语言的一种最高赞美。C++之于C,Java之于C++,C#之于Java,都是后者对前者的一种承认,哪怕是极不情愿的承认。批判与赞美,继承与发展,谓之扬弃。
过分拔高一种语言与抹煞语言之间的差别是两种极端,皆为秕言谬说。对一个程序员而言,编程语言乃立身之本。(这里谈的语言,自然不是孤立的语法和用法,也包括背后的编程范式和设计思想。)
程序员这门职业,你选择,你担当。
《The C Programming Language》
如果你因为是他的粉丝而后悔学C++,那就是为他人的偏执买单,不管那人名气有多大。
一种语言不够复杂是因为它还不够成熟。
能力越大,责任越大,风险越大。
既然系统语言主要为底层系统的开发服务,这就决定了它们的理念是:优化机器的时间而不是人的时间,优化机器的记忆而不是人的记忆;假设编译器是愚蠢的而程序员是聪明的,因此赋予程序员更多的权利、义务和责任。
学好C一本书足矣,学好C++即使推荐十本仍有遗珠之憾。可以说C++是苦了编程者,填了著书人。开个小书单:初级——《C++ Primer》和《Thinking in C++》;中级——《The C++ Programming Language》和《Effective C++》系列;高级——《The C++ In-Depth》系列。这里还要特别推荐一下《The Design and Evolution of C++》,从中你可以看到C++的设计和演变的来龙去脉,极具启发性。其他的C++精品书籍还有不少,恕不一一列举了。C++是匹无鞍的野马,看似桀骜不驯,若能顺性而御,必能足踏飞燕,行千里而不劳。
程序的性能和效率永远是重要的。
编程语言绝非象牙塔中之物,实乃技术和商业合力推动的结果。
Java的目的是让一种语言在多种平台上运行,而C#(.NET)的目的是让多种语言在一种平台上运行。Java重在语言,让语言向平台扩散;C#重在平台,让平台来凝聚语言。
其实JavaScript作为一门动态语言,集过程式、对象式、函数式、事件驱动式、元编程等于一身,其强大和优雅绝对是超乎想象的。它宛如一只神奇的魔袋,乍看平淡无奇,却总能变出意想不到的宝贝。
待友之道,贵在放大其优点而缩小其缺点,对待语言亦当如是。这既是一种态度,也是一种境界。
得道者化腐朽为神奇,离道者化神奇为腐朽。凡事皆然。VB最大的优点就是易学易用、上手快开发快,对非计算机专业人士尤其具有吸引力。当然简单易用不是没有代价的,随便拖放一个控件,就生成了一大堆代码,程序员的发挥余地自然受限,代码效率也难以保证。不过VB多用于前台的可视化开发,对运行效率要求相对较低,因此还是很有市场的。另外,VB进化到VB.NET后,从单纯的事件驱动式的编程范式发展为对象式、泛型式和函数式等多种范式,越来越强大、越来越严格,也越来越复杂,不再是人们戏称的‘玩具语言’了。
Delphi又称 Object Pascal,前身是教学语言 Pascal,VB的前身是入门语言 BASIC(Beginner’s All-purpose Symbolic Instruction Code),故而均具有易学易用的特点。
引用Computerworld的评论:‘Delphi让复杂的事情变得简单,VB让简单的事情变得更简单’。
然大道相通,难者亦易,易者亦难。JavaScript本身是一门功能齐全、强大而优美的语言,只要严肃地对待它,它就是一门严肃的语言。
语言的发展趋势一定是动静结合、刚柔并济。Perl凝练晦涩,Python优雅明晰,Ruby精巧灵动,PHP简明单纯。
或许优雅正是来自对细节和规范的重视吧。
Ruby背后最具特色的理念是:关注程序员使用语言时的感受超过语言本身的功能。通俗地说,兵器的称手比锋利更重要;文雅地说,应给予程序员更多的人文关怀。
我们看到,每种编程语言都有其独特的惯例用法和哲学理念,它们与编程范式一道形成了语言的编程风格。体悟愈深者编程预感愈强,思维与语言愈交融无碍,渐从必然王国走向自由王国。
(抽象)不妨概括为:去粗取精以化繁为简;由表及里以异中求同。再精简些是作减法和除法。
没有文档的 API 如同没有说明书的产品,除了实现者本人,其他用户是不敢轻易调用的。
防御性编程采取的是“先小人后君子”的策略;契约式设计采取的是“先君子后小人”的策略。
过滤缺点,抽取优点,西施就是这样炼成的。
首先,抽象有角度之分。相同的实体(entity)经过不同的角度抽象,得到的模型(model)也会不同。就拿人这个实体来说,在拓扑学家眼里是三维连通集合,在理论力学家眼里是质点,在化学家眼里是碳水化合物。其次,抽象还有程度之别。抽象程度越高,细节越少,普适性越强。典型的例子如:从矩形到多边形,从多边形到一般形状。
模型是抽象的结晶。
它们看起来很基本很普通,但平淡之中见真功。
抽象本身并不能解决问题,但却是解决问题的必经之路。通过抽象能简化和分解问题,使之更容易理解和控制,相应的解法也更具稳定性、普适性、重用性和可维护性。
我感觉你在把程序当烟抽——光有烟嘴的接口,没有香烟的实现,的确不太过瘾。
接口是纲,实现是目。纲若不举,目无以张。
Programming to an Interface, not an Implementation.
接口与实现的分离,有利于开发时间的分离及开发人员的分离。
抽象——尤其是数据抽象——才是OOP的核心和起源,尽管它们并非OOP的专利。没有抽象作基础,封装、继承和多态尽皆无本之木。只有贯彻ADT思想,设计出来的类才会是‘万人迷’:有优雅的外形——抽象,有丰富的内涵——数据,有鲜明的个性——类型。
分析阶段(OOA)以对象(而不是过程)为中心,设计阶段(OOD)以接口(而不是实现)为中心,实现阶段(OOP)以数据(而不是算法)为中心。
信息隐藏是一种原则,而封装是实现这种原则的一种方式。
在融会贯通者的眼里,知识是大同小异的;在一知半解者的眼里,知识是小同大异的。此所谓:通,则大处圆融合一而小处各具其妙;不通,则大处千变万化而小处无所分别。
访问控制不仅是一种语法限制,也是一种语义限制,也是一种语义规范——标有public的是接口,标有private的是实现,泾渭分明。
如果一个方法返回了一个可变(mutable)域对象(field object)的引用,无异于前门紧闭而后门洞开。如何解决这个问题呢?这就需要用到前面提到的防御性编程,更具体地说是防御性复制(defensive copying),即返回对象的一个复制品,以免授人以柄(handle)。
首先,普通的做法不代表是正确的。事实上,恕我直言:普通的程序员是不合格的,合格的程序员是不普通的。
第一:设计很重要也很不容易——需要研究精神;第二,任何人都会犯错误,包括权威在内——需要批判精神。
软件之软,体现在适应变化的能力。
外行人常以为数学定理最重要,其实数学思想才是数学的精髓。
编程的难点有两个主要方面:逻辑的复杂性和需求的变化性。
过度设计会带来不必要的复杂和效率损失。
应变能力强不等于代码不改变。能保证代码永远不变的软件有更好的归宿:要么退化为硬件,要么进化为人件——看看你的脑袋,有谁修改过它的源代码啊?不照样能随机应变吗?
开闭原则(open/closed principle,简称 OCP),所谓开,指对扩展开放;所谓闭,指对修改封闭。也就是说,软件应该在模块的基础上进行扩展而不是修改。换言之,严格遵循开闭原则的软件,不应该修改老代码,只能增加新代码。
软件的变化主要来自两个方面:一个以改善软件质量为目的的内在结构性变化;一个是以满足客户需要为目的的外在功能性变化。
初级程序员的理想是为所欲为——能用编程解决一切问题;中级程序员的理想是尽善而为——追求最佳解决方案;高级程序员的理想是有所为有所不为——重在整体设计的选择,能抵制局部技巧的诱惑;最高理想是无为而无不为——无论宏观设计还是微观实战,均非刻意选择,却自然合度。
敢于怀疑权威是可贵的,但如果没有研究精神作为基础,批判精神只会沦为牢骚“精神”。
客户意识对一个程序员的重要性,丝毫不亚于对一个企业的重要性。
千万不要为追求廉价的重用而轻易扩大接口范围,莫以自身之便而致客户之不便,莫以一时之便而致长期之不便。
没有足够应变力的模块,不够资格被重用。
如果意识到访问控制是对静态代码的控制,而不是对动态对象的控制,就不难理解它应该是以代码而非对象为边界的了。
谨记一点:轻诺者,必寡信。每一个 public 类、每一个非 private 成员,都是一份承诺。在没有明确职责、没有准备承担变更后果之前,请采用最严格的访问控制。有了客户意识,才有接口责任感。
作为服务的提供者,最重要的是讲诚信。首先,服务要有可靠性,不能阳奉阴违——即接口必须履行它的承诺;其次,服务要有稳定性,不能朝令夕改——即接口一经公开,不得随意变更。
作为服务的享受者,最重要的是守规矩。
Unix有一个哲学:‘一个程序只做一件事,但要做好’。用在 OOP 上,则是:‘一个类只提供一套服务,但要完善’。
编程毕竟是门实践活儿,完美无缺的设计如梦中佳人,可以追求却难以企及。
访问控制只是个玻璃罩,能防止乱动的双手,却防止不了偷窥的双眼。它至多只能维护语法上的封装和信息隐藏,而语义上的封装只有靠规范来维护。对程序员而言,前者是一种须要学习的知识,后者是一种须要培养的素质。
OOP 系统是对现实社会的模拟,这不仅反映在表现形式上,还反映在行为准则上。它们都是由一些相互交流、相互作用的个体对象组成,个体的品性、行为、能力等参差不齐。为实现合作共赢,所有个体都应恪守‘严以律己、宽以待人’的原则:作为服务的主题,应保证服务的信誉和品质;作为服务的客体,应遵守服务的规则和规范。唯其如此,无论个体对象还是整体系统,方能保持和谐而不失活力、稳定而不惧变化。
不要问系统能为你做什么,而要问你能为系统做些什么。
要求只能更少,承诺只能更多。
知识是水,越近源头越是鲜活。
编程是这样一种游戏,既要进攻得力——保证有意而为之的正面效应,又要防守得当——控制无意而随之的负面效应。
软件设计的四字要诀:外静内动。
外静指保持外部的接口不变,内动指允许内部的实现变动。大到库、框架、架构等的设计,小到具体的函数、类等的实现,概莫能外。在规范抽象中,静的是功能规范,动的是实现细节;在数据抽象中,静的是 API 接口,动的是接口实现;在多态抽象中,静的是 interface 接口,动的是实现类;在非虚接口模式中,静的是非多态的对外接口,动的是多态的对内挂钩。(挂钩就是 hook,也就是 callback 回调。借助多态,处于底层的父类调用了处于高层的子类的方法,不正是一种回调么?)
关于继承的用法,可概括为十二字方针:‘提倡接口继承,慎用实现继承’。
实现继承至少有3处用武之地:希望访问基础类的 protected 成员;希望覆盖基础类的方法,并且难以用合成来变通;希望成为基础类的子类型。
更实际的建议是,每当为继承与否而举棋不定之时,问自己两个问题:采用合成是否会遇到无法克服的困难?基础类是否专门为继承而设计?
继承是一种静态、显性的关系,合成是一种动态、隐形的关系。
《冒号课堂》
最后编辑于 :
©著作权归作者所有,转载或内容合作请联系作者
- 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
- 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
- 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
推荐阅读更多精彩内容
- Swift的编程范式 编程范式是程序语言背后的思想。代表了程序语言的设计者认为程序应该如何被构建和执行。常见的编程...
- 今天的分享是芭芭拉·奥克利所著的《学习之道》。 1、张弛有度 作者在书中讲到:“计划什么时候放下手中的活,和计划什...