当前位置: 首页 > 科技观察

你是七段程序员吗?

时间:2023-03-14 14:30:57 科技观察

前言程序员的编程能力会随着经验的积累而逐渐提高。我认为编程能力可以分为一些等级。下面通过两个维度来讨论编程能力层次模型。一个维度是编程技能水平,另一个维度是领域知识水平。编程技能水平编程技能水平是指程序员设计和编写程序的能力。这是程序员的根本。Segment0—Non-Programmer:初学程序员遇到问题完全一头雾水,不知道如何编程解决问题。也就是说,他还是门外汉,还称不上“程序员”。电脑在他面前依旧是一个神秘的黑匣子。Stage1—BasicProgrammer:经过一段时间的编程学习,接到任务后可以编写程序完成任务。写的代码在正常情况下是可以工作的,但是在实际运行中,遇到一些特殊情况就会出现各种BUG。也就是说,如果你有开发demo软件的能力,但是开发出来的软件实际交付给客户,你可能会被客户骂死。程序员的程序写好了,但是为什么有时能用,有时不能用,程序员不知道。如果在运行中遇到bug,或者需求发生变化,需要修改或增加代码,程序很快就会变得混乱,代码膨胀,bug多。很快,就连最初的开发人员自己也不愿意接手维护该程序。Paragraph2—Datastructure:经过一段时间的编程实践,程序员会体会到“数据结构+算法=程序”这句古话的含义。他们使用算法来解决问题。更进一步,他们会意识到算法本质上是依附于数据结构的,一旦设计出了好的数据结构,那么好的算法也会应运而生。设计一个错误的数据结构是不可能发展出一个好的算法的。记得国外某位圣人曾说过:“Showmeyourdatastructure!”Paragraph3-Object-oriented:之后,程序员就会体会到面向对象编程的强大威力。大多数现代编程语言都支持面向对象。但这并不意味着如果你用面向对象的编程语言编程,你使用类,甚至继承类,你就是在编写面向对象的代码。我见过很多用Java、Python、Ruby编写的面向过程的代码。只有掌握了接口,掌握了多态,掌握了类与类、对象与对象的关系,才能真正掌握面向对象编程技术。即使你使用不支持面向对象的传统编程语言,只要你心中有“对象”,你仍然可以开发面向对象的程序。比如,当我用C语言编程时,我会有意识地使用面向对象的技术来编写和设计程序。用struct模拟类,把相同概念的函数放在一起模拟类。如果你怀疑自己能不能用C写出面向对象的代码,你可以看看Linux内核,它是用C写的,但你也能从它的源代码的字里行间看出浓浓的“对象”的味道。真正掌握面向对象编程技术并不容易。在我的技术生涯中,有两道坎最让我头疼。一个障碍是在从Dos到Windows开发的过渡过程中,框架的概念很长一段时间都没有被理解。在DOS时代,都是调用函数库,你的程序主动调用函数。在Windows时代,它被一个框架取代了。甚至你的主程序实际上也是被框架调用的。UI线程将从操作系统获取消息并将它们发送到您的程序进行处理。Java程序员熟悉的Spring框架也是这样一个反向调用框架。现在因为“框架”这个词看起来很高大上,很多“类库”/“函数库”都自称“框架”。在我看来,这都是对名称的滥用。“类库”/“函数库”是我写的调用它们的代码。“框架”就是我向框架注册回调函数,框架调用我写的函数。另一个障碍是面向对象。很长一段时间,我不知道如何设计类与类之间的关系,也不能很好地设计类层次结构。记得当时看过一本外国专家写的书。他讲了一个非常简单实用的面向对象设计技巧:“描述问题。然后把里面的名词找出来,用来建类。把里面的动词找出来,用来建类的方法”。这个技巧虽然很好用,但是太草根了,没有理论基础,也不严谨。如果问题陈述很差,那么由此产生的类系统就会有问题。掌握面向对象的思想应该有很多方法。我从关系数据库中得到启发,理解和掌握面向对象的设计思想。在我看来,关系数据库中的一张表其实就是一个类,每一行记录都是一个类的实例,也就是一个对象。表之间的关系就是类之间的关系。O-Rmapping技术(如Hibernate)用于从面向对象代码到数据库表的映射,这也说明类和表在逻辑上确实是等价的。由于数据库设计和类设计是等价的,设计面向对象系统只需要关系数据库的设计技术。关系型数据库的表结构设计很简单:1.确定表与表之间的关系,即类与类之间的关系。是一对一、一对多、多对一还是多对多。这就是类之间的关系。2.识别表的字段。当然,一个对象有无数的属性(比如人:身高、体重、性别、年龄、姓名、身份证号、驾照号、银行卡号、护照号、港澳护照号、工号、病史、婚史等),我们在写程序的时候需要记录的只是我们关心的属性。这些关注的属性就是表的字段,也就是类的属性。“弱水三千,我来一勺喝”!第四段——设计模式:我曾经在网上看到这样一句话:“如果你没有十万行代码,就别跟我谈设计模式”。我认为这是理所当然的。记得第一次看Gof的设计模式这本书的时候,我发现虽然之前不知道设计模式,但是在实际的编程过程中,其实是有意识地使用了一些设计模式。设计模式是编程的客观规律,不是任何人发明的,而是一些早期的资深程序员最先发现的。您可以在没有设计模式的情况下编写满足您需求的程序。但是,一旦后续需求发生变化,你的方案就不够灵活,难以为继。但真正的程序交付给客户后,还会有进一步的需求反馈。后续版本的开发肯定会增加需求。这是程序员无法逃避的现实。在编写UI程序时,无论是Web、Desktop、Mobile,还是Game,都必须使用MVC设计模式。否则,面对后续UI需求的变化,你的程序将难以为继。设计模式,最重要的思想就是解耦,通过接口来解耦。这样以后如果需求有变化,只需要提供一个新的实现类就可以了。主要的设计模式实际上是面向对象的。因此,设计模式可以被认为是面向对象的高级阶段。只有掌握了设计模式,才算真正掌握了面向对象的设计技巧。当我学习一门新的语言(包括非面向对象的语言,比如函数式编程语言)时,我总是在理解了它的语法之后,看看各种设计模式是如何在这门语言中实现的。这也是学习编程语言的一个技巧。Paragraph5–LanguageExpert:程序员经过一段时间的编程实践,对一门常用的编程语言已经相当熟练。也有人成为了“语言律师”,擅长向其他程序员解释语言的用法和各种陷阱。这个阶段的程序员往往是自己语言的忠实信徒,经常在社区和论坛上与其他语言的用户争论到底哪种语言是最好的编程语言。他们认为他们使用的语言是世界上最好的编程语言,没有之一。他们认为他们使用的编程语言适用于所有场景。他们眼里只有锤子,所以他们把每件事都当作钉子来对待。Segment6–MultilingualExpert:这个阶段的程序员因为工作关系或者纯粹因为对技术的兴趣,学习并掌握了几种编程语言。体验过不同编程语言的不同设计思想,更了解每种语言的优缺点。他们现在认为编程语言不是最重要的,编程语言只是基本功。他们现在会根据不同的任务需求或不同的资源选择不同的编程语言来解决问题,他们不会再抱怨没有使用喜欢的编程语言进行开发。编程语言有很多流派和思想,有些编程语言同时支持多种编程范式。静态类型编程范式采用静态类型编程范式的编程语言,其变量需要明确类型化。代表语言:C、C++、Pascal、Objective-C、Java、C#、VB.NET、Swif、Golang。这样做的好处是:1.编译器可以在编译时发现类型错误。2.当编译器在编译时知道了类型信息,可以提高性能。这种范式认为程序员必须知道变量的类型。如果你不知道变量的类型,那就不要搞砸了!编译时,程序会报错。Swift和Go语言都是静态类型编程语言,但它们都不需要显式指定类型,而是由编译器通过推理自动确定。动态类型编程范式采用静态类型编程范式的编程语言,其变量不需要显式类型化。任何变量都可以指向任何类型的对象。代表语言:Python、Ruby、JavaScript。动态类型的哲学可以用鸭子类型的概念来概括。JamesWhitcombRiley提出的鸭子测试可以表述如下:“当一只鸟走路像鸭子、游泳像鸭子、叫声像鸭子时,那么这只鸟就可以称为鸭子。”该范式认为程序人员必须知道变量的类型及其支持的方法和属性。如果你不知道变量的类型,那就不要搞砸了!程序运行时会崩溃!谁该为程序崩溃负责?怪自己,你不是一个合格的程序员!动态类型的优点是不需要显式定义接口和抽象类型。只要类型支持所需的方法和属性,就可以了。该程序将非常灵活和简单。被C++、Java、C#视为生命线的接口/基类,在动态语言中算不了什么!缺点是:1.如果类型错误,编译器无法发现错误,但程序在运行时会崩溃。2.因为编译器不知道变量的类型,所以无法优化性能。面向对象编程范式面向对象编程范式在20世纪70年代后期开始出现。它支持类和类的实例作为封装代码的模块。代表语言:Smalltalk、C++、Objective-C、Java、C#、VB.NET、Swift、Go、Python、Ruby、ActionScritp、OCaml。早期的编程语言都是面向过程的。它是构成函数的顺序、条件和循环。随着代码规模的增大,人们发现有必要对代码进行模块化。将一个概念对应的代码放在一个文件中,方便并发开发和代码管理。人们还发现了“程序=数据结构+算法”的规律。因此,一个概念对应的数据结构和函数应该放在一个文件中。这就是类的概念。面向对象的编程范式确实大大提高了生产效率,因此得到了广泛的应用。因此,在语言层面支持面向对象编程范式的语言有很多。虽然C语言在语言层面上不支持面向对象的编程范式,但是现代C语言的发展会应用面向对象的模块化思想,将同类型的数据结构和函数放在一个文件中,并采用相似的命名方式.毕竟C语言在语言层面是不支持面向对象的,所以很多程序员都想给C语言增加面向对象的支持。代表是C++和Objective-C。C++是一门新的语言,但是大部分语言元素都兼容C。Objective-C完全兼容C。Objective-C在C的基础上加了一层薄薄的语法糖来支持接口(也就是其他语言中的类)和协议(即其他语言的界面)。甚至,Objective-C的最初实现是一个C语言预编译器。坦白说,Objective-C除了加入的语法不符合C流之外,其实它的面向对象系统设计还是相当精妙的。早年,乔布斯眼光独到,获得了Objective-C。因为在Apple/NextStep系统中是封闭的,所以很少有人知道。随着iOs系统的流行,这几年Objective-C已经名扬天下。函数式编程范式函数式编程范式是一些数学家发明的一种编程语言。他们认为程序是数学函数。代表语言:Lisp、Erlang、JavaScript、OCaml、Prog。有很多大牛都大力提倡函数式编程语言并认为它们极具革命性。但我认为他们高估了函数式编程范式的威力,我并不认为函数式编程范式优于面向对象编程范式。函数式编程语言的核心是函数,它们没有Class的概念。但它的功能不是传统的面向过程的语言功能,它的功能支持“闭包”的概念。在我看来,函数式编程语言的功能,也就是“闭包”,说白了其实就是“类”。当今编程语言的发展要求模块化,即“数据结构”与“算法”的结合。不管是什么语言,不把它们结合起来的编程方式是没有出路的。面向对象的编程语言,用类来结合“数据结构”和“算法”。类的核心是“数据结构”,即它的“属性”,而不是“算法”,它的“功能”。在类中,它是附加到属性的函数。在函数式编程语言中,闭包用于组合“数据结构”和“算法”。它是可以抓取外部字段的功能。它是附加在“功能”上的“属性”。“类”本质上等同于“闭包”。现在很多面向对象的编程语言都增加了对闭包的支持。观察它的代码,我们可以发现它们实际上是用“类”来实现“闭包”。谁更容易使用,“类”还是“闭包”?显然是“阶级”。而“闭包”更为简洁,所以在面向对象的编程语言中,常使用“闭包”来代替匿名类。只有一个功能的类作为一个类来写太麻烦了。最好写成闭包,这样更简洁。投诉OCaml语言。它的前身,Caml语言本身,就是一门非常优秀的函数式语言。它硬生生地加入了一套完整的面向对象机制,同时支持面向对象和函数式编程范式。像C++一样很容易脑裂。.也有很多面向对象的语言爱好者觉得JavaScript很烦人,总想给JavaScript添加面向对象的支持。ActionScript就是这样一种尝试。我用过,和Java真的没太大区别。再次取笑ExtJS。当初选择Web前端开发框架的时候,我对比过ExtJS和JQuery。ExtJS显然是由Java高手开发的。它使用JavaScript模仿Swing的设计思想,构建了一套UI库。JQuery的开发者显然领悟了JavaScript的函数式编程范式,根据JavaScript动态函数式编程语言的特点,创建了一套UI库,一下子干掉了ExtJS。从ExtJS和JQuery的故事中,我们可以看出多语言编程能力是多么重要。ExtJS的作者精通并热爱Java,所以他把JavaScript这把手术刀当成了Java的锤子,敲起来吃力不讨好。函数式编程语言,以及尾递归等一些技巧。尾递归不需要栈,防止递归调用时栈溢出。模板编程范式模板编程就是以类型为参数,一组函数可以支持任意数量的类型。代表语言:C++。模板编程的需求是在C++开发容器库时发明的。因为容器需要保存任何类型的对象,所以需要泛型。C++模板编程就是在编译时根据源代码中的用法创建对应类型的代码。除了C++,Java和C#也有类似的机制,称为“泛型”,但它们的实现与C++模板有很大不同。他们的编译器不生成新代码,而是使用强制转换来生成新代码。在没有模板/泛型的编程语言中,如何将对象存储在容器中?访问普通基类类型(Java、C#)的对象,或者void*指针(C),取出来时将类型强制转换为实际类型。在动态类型语言中,您不关心类型,也没有关系。只需将任何物体扔进容器中,取出即可直接使用。一些C++高手在模板的基础上想出了“模板元编程”。因为模板编程是由C++编译器完成的,模板元编程就是让编译器去计算,计算出编译后的结果。不知道这东西除了研究和炫耀技能还有什么用?总结一门语言值不值得学,我觉得有几个标准:1、不管你会不会用,要用就得学,所以毫无疑问。毕竟,我们都要吃饭。2、它的语言功能是否给你耳目一新的感觉?如果是的话,那是值得入场的。比如Go语言废除了异常,取而代之的是返回多个值。我认为这是理所当然的。事实上,多年来我一直自愿不使用异常。因为,我觉得既然C不支持异常,活得好好的,为什么还需要异常呢?发生错误,并返回错误代码。对于无法修复的错误,可以直接使用Abort程序!而且,异常实际上有悖于过程式编程的原则。一个函数应该只有一个入口和一个出口。抛出异常会创建更多退出。3.你擅长某个领域吗?如果你手里只有一把锤子,那么你只能把每一个任务都当成钉子来锤。但是如果工具箱里有多个工具,面对不同的任务就会得心应手很多。Paragraph7——架构设计也需要掌握架构设计的能力,才能设计出优秀的软件。架构设计有一些技巧:1.分层一个软件通常分为:表现层-UI部分界面层-后台服务通信接口部分服务层-实际服务部分存储层-持久化存储部分,存储在文件或数据库中。分层软件可以解耦各个模块,支持并行开发,易于修改,易于提升性能。2、SOA模块之间通过网络通信连接,松散耦合。每个模块都可以独立部署,可以增加部署实例来提升性能。每个模块都可以使用不同的语言和平台进行开发,并且可以复用之前开发的服务。SOA,常用的协议有WebService、REST、JSON-RPC等。3、性能瓶颈1)把同步变成异步。用内存队列(Redis)、工作流引擎(JBpm)等实现。内存队列容易丢数据,但是速度很快。工作流引擎会将请求保存到数据库中。通过把同步请求变成异步请求,基本上可以解决99.99%的性能问题。2)用独立的并行硬件处理。比如使用GPU、FPGA等硬件进行处理,提高性能。3)使用集群计算机进行处理。例如,Hadoop集群使用多台计算机并行处理数据。在您自己的软件堆栈中,您还可以部署模块的多个副本以进行并行处理。4)使用缓存来满足请求。对常用内容加热缓存后,大量用户请求只是从内存中读取数据,性能会得到很大的提升。缓存是上帝的算法,记住就好像它的性能只是低于最佳性能一样,就好像你是上帝并且可以预见未来。现在X86CPU遇到了主频的限制,提升CPU性能的主要途径就是增加高速Cache。4、大系统小工作遇到大系统不要慌张,分成多个模块,使用多个小程序,通过SOA协同解决。这遵循了Unix的设计理念。在Unix上开发了大量单一用途的小程序。提倡用户使用管道让多个小程序协同解决用户需求。当然,渠道沟通限制太多,不够灵活。因此,现在我们可以让多个程序通过URI和SOA进行协作。Android和iOS上的应用程序现在通过URI进行协作。这也算是Unix设计思想的现代发展了吧?!5、Sharding切片现在有一个趋势,就是去IOE。I-IBM大型机,O-Oracle数据库,E-EMC存储。以往大型系统往往采用IOE来架构。大型机上部署了一个Oracle数据库,Oracle数据库使用EMC来存储和存储数据。IOE是当今最强大的计算机、数据库和存储设备。但总有一天他们无法抵抗庞大的系统。Oracle数据库是Shareeverything,可以运行在计算机集群上(服务器节点数不能超过16个)。计算机集群都共享一个存储。走向IOE运动标志着ShareEverything模式的破产。一定要用ShareNothing,系统可以无限扩展。MySQL数据库可以处理任何大小的数据。前提是你会Sharding。把一个大系统分成几个小系统,再分成几个便宜的服务器和存储。更现代的,就是拆分成大量的虚拟机。比如铁道部的12306网站。我们知道火车票是属于某趟火车的。那么我们把每一个列车都划分为一个单元,我们就可以把12306这个网站划分成几千个模块。一个虚拟机可以托管多个模块。当某些列车成为性能瓶颈时,可以将它们迁移到单独的虚拟机中。即使列出的某些服务最终不可用,系统也不会完全不可用。12306网站只有一个全局部分,就是用户登录。这个可以交给第三方。例如,用户可以使用微信、微博、qq等账号登录。您也可以自己实现用户登录服务。或者使用切片为多个Redis服务器提供服务。Redis服务器存储了每个登录用户的sessionId和userId、角色、权限等信息。sessionId是随机生成的,可以选择它的一些位来标识它在哪个Redis服务器上。用户登录后,将sessionId发送给客户端。每次用户请求时,sessionId都会被发送回服务器。服务器将sessionId发送给Redis服务器,用于查询用户信息,处理用户请求。如果在redis服务器上没有找到sessionId,就让用户去登录。即使所有注册用户同时登录,也不需要太多内存。而且,当session内存过大时,可以删除最早登录用户的session,迫使他重新登录。同时活跃的用户数不会太多。领域知识级别之前的所有级别都侧重于编程本身的技能。说白了就是基本功,本身并不能产生多大的价值。但是太多的程序员在这些基础级别上浪费了太多时间。有些程序员特别喜欢钻研编程语言。每当有新的编程语言问世或老语言被炒作时,他们都会投入精力去研究它。我就是他们中的一员,在编程语言上浪费了大量的精力,在技巧和技巧上。我觉得C++语言是一个特别大的坑。它最初是作为面向对象的C开发的。后来,当模板编程被发现时,大力提倡模板编程和进一步的模板元编程。最近引入了C++11、C++14等新标准,增加了很多新东西,比如函数式编程、类型推断等。C++太复杂,坑太多,消耗了程序员的大量精力。我在用C++的时候,只用到面向对象部分和模板部分,其他太深的特性我都不用。计算机科学是一门非常广泛的学科。在我们编写有价值的程序之前,有许多知识领域需要并且值得我们深入研究。软件必须与行业相结合,必须落地才能有价值。在没有领域知识的情况下,仅仅通过学习编程技能是无法编写出有价值的程序的。