计算机中的静态成员函数数是什么?

热门排序 |
来自子话题:
来自子话题:
弊端是,没有人还记得面向对象原本要解决的问题是什么。&br&&br&1、面向对象原本要解决什么(或者说有什么优良特性)&br&似乎很简单,但实际又很不简单:面向对象三要素&b&封装、继承、多态&/b&&br&&br&(&b&警告&/b&:事实上,从业界如此总结出这面向对象三要素的一刹那开始,就已经开始犯错了!)。&br&&br&&b&封装&/b&:封装的意义,在于明确标识出允许外部使用的所有成员函数和数据项,或者叫&b&接口&/b&。&br&&br&有了封装,就可以明确区分&b&内外&/b&,使得类实现者可以修改封装&b&内&/b&的东西而不影响&b&外&/b&部调用者;而外部调用者也可以知道自己不可以碰哪里。这就提供一个良好的合作基础——或者说,只要&b&接口&/b&这个基础约定不变,则代码改变不足为虑。&br&&br&&br&&br&&b&继承+多态&/b&:继承和多态必须一起说。一旦割裂,就说明理解上已经误入歧途了。&br&&br&先说&b&继承&/b&:继承同时具有两种含义:其一是继承基类的方法,并做出自己的扩展——号称解决了代码重用问题;其二是&b&声明&/b&某个子类&b&兼容&/b&于某基类(或者说,接口上完全&b&兼容&/b&于基类),外部调用者可无需关注其差别(内部机制会自动把请求派发[dispatch]到合适的逻辑)。&br&&br&再说&b&多态&/b&:基于对象所属类的不同,外部对同一个方法的调用,实际执行的逻辑不同。&br&&br&很显然,多态实际上是&b&依附于继承的第二种含义&/b&的。让它与封装、继承这两个概念并列,是&b&不符合逻辑&/b&的。不假思索的就把它们当作可并列概念使用的人,显然是从一开始就被误导了。&br&&br&&br&实践中,继承的第一种含义(实现继承)意义并不很大,甚至常常是有害的。因为它使得子类与基类出现强耦合。&br&&br&继承的第二种含义非常重要。它又叫“接口继承”。&br&&b&接口继承&/b&实质上是要求“做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的所有对象”——这在程序设计上,叫做&b&归一化&/b&。&br&&br&&br&&b&归一化&/b&使得外部使用者可以不加区分的处理所有接口兼容的对象集合——就好象linux的泛文件概念一样,所有东西都可以当文件处理,不必关心它是内存、磁盘、网络还是屏幕(当然,如果你需要,当然也可以区分出“字符设备”和“块设备”,然后做出针对性的设计:细致到什么程度,视需求而定)。&br&&br&&b&归一化&/b&的实例:&br&a、一切对象都可以序列化/toString&br&b、一切UI对象都是个window,都可以响应窗口事件。&br&&br&——必须注意,是一切(符合xx条件的)对象皆可以做什么,而不是“一切皆对象”。后者毫无意义。&br&&br&&br&显然,&b&归一化&/b&可以大大简化使用者的处理逻辑:这和带兵打仗是类似的,班长需要知道每个战士的姓名/性格/特长,否则就不知道该派谁去对付对面山坡上的狙击手;而连长呢,只需知道自己手下哪个班/排擅长什么就行了,然后安排他们各自去守一段战线;到了师长/军长那里,他更关注战场形势的转变及预期……没有这种层层简化、而是必须直接指挥到每个人的话,累死军长都没法指挥哪怕只是一场形势明朗的冲突——光一个个打完电话就能把他累成哑巴。&br&&br&&br&软件设计同样。比如说,消息循环在派发消息时,只需知道所有UI对象都是个window,都可以响应窗口消息就足够了;它没必要知道每个UI对象究竟是什么——该对象自己知道收到消息该怎么做。&br&&br&合理划分功能层级、适时砍掉不必要的繁杂信息,一层层向上提供简洁却又完备的信息/接口,高层模块才不会被累死——KISS是最难也是最优的软件设计方法,没有之一。&br&&br&&br&&br&&b&总结&/b&:面向对象的好处实际就这么两点。&br&一是通过封装明确定义了何谓接口、何谓接口内部实现、何谓接口的外部调用者,使得大家各司其职,不得越界;&br&二是通过继承+多态这种内置机制,在语言的层面支持&b&归一化&/b&的设计,并使得内行可以从代码本身看到这个设计——但,注意仅仅只是&b&支持&/b&归一化的设计。不懂如何做出这种设计的外行仍然不可能从瞎胡闹的设计中得到任何好处。&br&&br&&br&显然,不用面向对象语言、不用class,一样可以做归一化的设计(如老掉牙的泛文件概念、游戏行业的一切皆精灵),一样可以封装(通过定义模块和接口),只是用面向对象语言可以直接用语言元素显式声明这些而已;&br&而用了面向对象语言,满篇都是class,并不等于就有了归一化的设计。甚至,因为被这些花哨的东西迷惑,反而更加不知道什么才是设计。&br&&br&&br&2、人们以为面向对象是什么、以及因此制造出的悲剧以及闹剧&br&&br&误解一、&b&面向对象语言支持用语言元素直接声明封装性和接口兼容性,所以用面向对象语言写出来的东西一定更清晰、易懂&/b&。&br&&br&事实上,既然class意味着声明了封装、继承意味着声明了接口兼容,那么错误的类设计显然就是错误的声明、盲目定义的类就是无意义的喋喋不休。而&b&错误的声明比没有声明更糟;通篇毫无意义的喋喋不休还不如错误的声明&/b&。&br&&br&除非你真正做出了漂亮的设计,然后用面向对象的语法把这个设计声明出来——仅仅声明真正有设计、真正需要人们注意的地方,而不是到处瞎叫唤——否则不可能得到任何好处。&br&&br&&b&一切皆对象实质上是在鼓励堆砌毫无意义的喋喋不休&/b&。大部分人——注意,不是个别人——甚至被这种无意义的喋喋不休搞出了神经质,以至于非要在喋喋不休中找出意义:没错,我说的就是设计模式驱动编程,以及&a href=&///?target=http%3A///articles/8745.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&如此理解面向对象编程&i class=&icon-external&&&/i&&/a&。&br&&br&&br&&br&误解二、&b&面向对象三要素是封装、继承、多态,所以只要是面向对象语言写的程序,就一定“继承”了语言的这三个优良特性&/b&。&br&&br&事实上,如前所述,封装、继承、多态只是语言层面对良好设计的支持,并不能导向良好的设计。&br&如果你的设计做不出真正的封装性、不懂得何谓归一化,那它用什么写出来都是垃圾。&br&&br&&br&&br&误解三、&b&把软件写成面向对象的至少是无害的&/b&。&br&&br&要了解事实上是什么,需要先科普几个概念。&br&&br&什么是真正的&b&封装&/b&?&br&&br&——回答我,封装是不是等于“把不想让别人看到、以后可能修改的东西用private隐藏起来”?&br&&br&显然&b&不是&/b&。&br&如果功能得不到满足、或者未曾预料到真正发生的需求变更,那么你怎么把一个成员变量/函数放到private里面的,将来就必须怎么把它挪出来。&br&&br&你越瞎搞,越去搞某些华而不实的“灵活性”——比如某种设计模式——真正的需求来临时,你要动的地方就越多。&br&&br&&b&真正的封装是,经过深入的思考,做出良好的抽象,给出“完整且最小”的接口,并使得内部细节可以对外透明(注意:对外透明&/b&的意思是&b&,&/b&外部调用者可以顺利的得到自己想要的任何功能,&b&完全意识不到内部细节的存在;&/b&而不是外部调用者为了完成某个功能、却被碍手碍脚的private声明弄得火冒三丈;最终只能通过怪异、复杂甚至奇葩的机制,才能更改他必须关注的细节——而且这种访问往往被实现的如此复杂,以至于稍不注意就会酿成大祸)。&br&&br&一个设计,只有达到了这个高度,才能真正做到所谓的“封装性”,才能真正杜绝对内部细节的访问。&br&&br&否则,生硬放进private里面的东西,最后还得生硬的被拖出来——当然,这种东西经常会被美化成“访问函数”之类渣渣(不是说访问函数是渣渣,而是说因为设计不良、不得不以访问函数之类玩意儿在封装上到处挖洞洞这种行为是渣渣)。&br&&br&&br&&br&一个典型的例子,就是C++的new和过于灵活的内存使用方式之间的耦合。&br&这个耦合就导致了new[]/delete[]、placement new/placement delete之类怪异的东西:这些东西必须成对使用,怎么分配就必须怎么释放,任何错误搭配都可能导致程序崩溃——这是为了兼容C、以及得到更高执行效率的无奈之举;但,它更是“抽象层次过于复杂,以至于无法做出真正透明的设计”的典型案例:只能说,c++设计者是真正的大师,如此复杂的东西在他手里,才仅仅付出了如此之小的代价。&br&&br&(更准确点说,是new/delete和c++的其它语言元素之间是非正交的;于是当同时使用这些语言元素时,就不可避免的出现了彼此扯淡的现象。即new/delete这个操作对其它语言元素非透明:在c++的设计里,是通过把new/delete分成两层,一是内存分配、二是在分配的内存上初始化,然后暴露这个分层细节,从而在最大程度上实现了封装——但比之其它真正能彼此透明的语言元素间的关系,new/delete显然过于复杂了)&br&&br&这个案例,可以非常直观的说明“设计出真正对外透明的封装”究竟会有多难。&br&&br&&br&&br&&br&&br&&b&接口继承&/b&真正的好处是什么?是用了继承就显得比较高大上吗?&br&&br&显然不是。&br&&br&接口继承没有任何好处。它只是声明某些对象在某些场景下,可以用归一化的方式处理而已。&br&&br&换句话说,如果不存在“需要不加区分的处理类似的一系列对象”的场合,那么继承不过是在装X罢了。&br&&br&&br&&br&封装可应付需求变更、归一化可简化(类的使用者的)设计:以上,就是面向对象最最基本的好处。&br&——其它一切,都不过是在这两个基础上的衍生而已。&br&&br&&br&换言之,如果得不到这两个基本好处,那么也就没有任何衍生好处——应付需求变更/简化设计并不是打打嘴炮就能做到的。&br&&br&&br&了解了如上两点,那么,很显然:&br&1、如果你没有做出好的抽象、甚至完全不知道需要做好的抽象就忙着去“封装”,那么你只是在“封”和“装”而已。&br&这种“封”和“装”的行为只会制造累赘和虚假的承诺;这些累赘以及必然会变卦的承诺,必然会为未来的维护带来更多的麻烦,甚至拖垮整个项目。&br&&br&正是这种累赘和虚假的承诺的拖累,而不是所谓的为了应付“需求改变”所必需的“灵活性”,才是大多数面向对象项目代码量暴增的元凶。&br&&br&2、没有真正的抓到一类事物(&b&在当前应用场景下&/b&)的根本,就去设计继承结构,是必不会有所得的。&br&&br&不仅如此,请注意我强调了&b&在当前应用场景下&/b&。&br&这是因为,分类是一个极其主观的东西,&b&不存在普适的分类法&/b&。&br&&br&举例来说,我要研究种族歧视,那么必然以肤色分类;换到法医学,那就按死因分类;生物学呢,则搞门科目属种……&br&&br&想象下,需求是“时尚女装”,你却按“窒息死亡/溺水死亡/中毒死亡之体征”来了个分类……你说后面这软件还能写吗?&br&&br&&br&&br&类似的,我遇到过写游戏的却去纠结“武器装备该不该从游戏角色继承”的神人。你觉得呢?&br&&br&事实上,游戏界真正的抽象方法之一是:一切都是个有位置能感受时间流逝的精灵;而某个“感受到时间流逝显示不同图片的对象”,其实就是游戏主角;而“当收到碰撞事件时,改变主角下一轮显示的图片组的”,就是游戏逻辑。&br&&br&&br&看看它和“武器装备该不该从游戏角色继承”能差多远。想想到得后来,以游戏角色为基类的方案会变成什么样子?为什么会这样?&br&&br&&br&&br&&br&&br&最具重量级的炸弹则是:正方形是不是一个矩形?它该不该从矩形继承?如果可以从矩形继承,那么什么是正方形的长和宽?在这个设计里,如果我修改了正方形的长,那么这个正方形类还能不能叫正方形?它不应该自然转换成长方形吗?如果我有两个List,一个存长方形,一个存正方形,自动转换后的对象能否自动迁移到合适的list?什么语言能提供这种机制?如果不能,“一视同仁的处理某个容器中的所有元素”岂不变成了一句屁话?&br&&br&造成这颗炸弹的根本原因是,面向对象中的“类”,和我们日常语言乃至数学语言中的“类”根本就不是一码事。&br&&br&面向对象中的“类”,意思是“接口上兼容的一系列对象”,关注的只不过是接口的兼容性而已(可搜索 里氏代换);关键放在“可一视同仁的处理”上(学术上叫is-a)。&br&&br&显然,这个定义完全是且只是为了应付归一化的需要。&br&&br&这个定义经常和我们日常对话中提到的类概念上重合;但,如前所述,根本上却彻彻底底是八杆子打不着的两码事。&br&&br&就着生活经验滥用“类”这个术语,甚至依靠这种粗浅认识去做设计,必然会导致出现各种各样的偏差。这种设计实质上就是在胡说八道。&br&就着这种胡说八道来写程序——有人觉得这种人能有好结果吗?&br&&br&——但,几乎所有的面向对象语言、差不多所有的面向对象方法论,却就是在鼓励大家都这么做,完全没有意识到它们的理论基础有多么的不牢靠。&br&——如此作死,焉能不死?!&br&&br&&br&——你还敢说面向对象无害吗?&br&&br&——在真正明白何谓封装、何谓归一化之前,每一次写下class,就在错误的道路上又多走了一步。&br&——设计真正需要关注的核心其实很简单,就是封装和归一化。&b&一个项目开始的时候,“class”写的越早,就离这个核心越远&/b&。&br&——过去鼓吹的各种面向对象方法论、甚至某些语言本身,恰恰正是在怂恿甚至逼迫开发者尽可能早、尽可能多的写class。&br&&br&&br&&br&误解四、只有面向对象语言写的程序才是面向对象的。&br&&br&事实上,unix系统提出泛文件概念时,面向对象语言根本就不存在;游戏界的精灵这个基础抽象,最初是用C甚至汇编写的;……。&br&&br&面向对象其实是汲取以上各种成功设计的经验才提出来的。&br&&br&所以,面向对象的设计,不必非要c++/java之类支持面向对象的语言才能实现;它们不过是在你做出了面向对象的设计之后,能让你写得更惬意一些罢了——但,如果一个项目无需或无法做出面向对象的设计,某些面向对象语言反而会让你很难受。&br&&br&&b&用面向对象语言写程序,和一个程序的设计是面向对象的,两者是八杆子打不着的两码事&/b&。纯C写的linux kernel事实上比c++/java之类语言搞出来的大多数项目更加面向对象——只是绝大部分人都自以为自己到处瞎写class的面条代码才是面向对象的正统、而死脑筋的linus搞的泛文件抽象不过是过程式思维搞出来的老古董。&br&&br&——这个误解之深,甚至达到连wiki词条里面,都把OOP定义为“用支持面向对象的语言写程序”的程度。&br&——恐怕这也是没有人说泛文件设计思想是个骗局、而面向对象却被业界大牛们严厉抨击的根本原因了:真正的封装、归一化精髓被抛弃,浮于表面的、喋喋不休的class/设计模式却成了”正统“!&br&&br&借用楼下PeytonCai朋友的链接:&br&&a href=&///?target=http%3A///405/.shtml& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&名家吐槽:面向对象编程从骨子里就有问题&i class=&icon-external&&&/i&&/a&&br&&br&————————————————————————————&br&&br&总结: 面向对象其实是对过去成功的设计经验的总结。但那些成功的设计,不是因为用了封装/归一化而成功,而是&b&切合自己面对的问题,给出了恰到好处的设计&/b&。&br&&br&让一个初学者知道自己应该向封装/归一化这个方向前进,是好的;用一个面向对象的条条框框把他们框在里面、甚至使得他们以为写下class是完全无需思索的、真正应该追求的是设计模式,则是罪恶的。&br&&br&事实上,class写的越随意,才越需要设计模式;就着错误的实现写得越多、特性用得越多,它就越发的死板,以至于必须更加多得多的特性、模式、甚至语法hack,才能勉强完成需求。&br&&br&只有经过真正的深思熟虑,才有可能做到KISS。&br&&br&&br&到处鼓噪的面向对象编程的最大弊端,是把软件设计工作偷换概念,变成了“就着class及相关教条瞎胡闹,不管有没有好处先插一杠子”,甚至使得人们忘记去关注“抽象是否真正简化了面对的问题”。&br&&br&&b&一言以蔽之:没有银弹。&/b&任何寄希望于靠着某种“高大上”的技术——无论是面向对象、数据驱动、消息驱动还是lambda、协程等等等等——就能一劳永逸的使得任何现实问题“迎刃而解”的企图都是注定要失败的,都不过是外行的意淫而已;靠意淫来做设计,不掉沟里才怪。&br&&br&想要做出KISS的方案,就必须对面对的问题有透彻的了解,有足够的经验和能力,并经过深思熟虑,这才能做出简洁的抽象:至于最终的抽象是面向对象的、面向过程的还是数据驱动/消息驱动的,甚至是大杂烩的,都是可能的。只要这个设计能做到最重要、也是最难的KISS,那它就是个好设计。&br&&br&的确有成功的经验、正确/合理的方向:技术无罪,但,没有银弹。
弊端是,没有人还记得面向对象原本要解决的问题是什么。1、面向对象原本要解决什么(或者说有什么优良特性)似乎很简单,但实际又很不简单:面向对象三要素封装、继承、多态(警告:事实上,从业界如此总结出这面向对象三要素的一刹那开始,就已经开始犯错…
可能我的看法有些简单粗暴。但是我希望表达一下我的「一家之言」。&br&&br&面向对象编程有很多用途,很多用法,但是我们会发现「设计模式驱动」成为了非常有代表性的流派。有时我们会疑惑,为什么会有设计模式,为什么面向对象编程会出现如此恐怖的这么复杂的类关系。&br&&br&我认为这是为了满足「增量式开发」。它假定几个前提:&br&1,将一份代码测试调试稳定所需要花的时间,远远大于撰写代码的时间。&br&2,已经通过测试的旧代码永远不修改,只对新代码进行新增,是最可靠的开发方式。&br&3,你经常需要重用没有源代码的库,并且扩展和修改其功能。&br&&br&很多面向对象的设计,其实是为了满足一个很基本的目的:不修改旧代码,只新增代码。代码只增不改,所以才会出现「继承」这种东西。因为你不需要原有的源代码也不需要修改原有的类,而是派生一个类增加新的方法。「继承」的本来目的看起来就是为了解决这个问题。&br&&br&因此,很多类层次关系的设计,不是为了更高的效率,不是为了代码看起来更清晰,而是为了「保证旧代码不需要被修改」这个目的。&br&&br&不过,这是不是现实呢?在某些公司,这是事实,在很多公司,以上的假定不是现实。&br&1,很多代码并没有经过长时间的充分的测试,因而没有必要为了不浪费原有测试资源而拒绝修改旧代码。&br&2,修改旧代码在大多数公司是不可避免的。&br&3,很多时候我们提倡读懂库的源代码,而不会盲目使用无源代码的库。&br&4,现在流行的敏捷开发模型中宣传要拥抱变化。在敏捷模型中,测试案例是被固化的,而代码与架构都可以经常被修改,换句话说,不修改旧代码,以及通过类层次关系设计系统架构,这些特性对敏捷来说都不重要,甚至背道而驰。&br&&br&我的看法与 酷壳「&b&&a href=&///?target=http%3A///articles/8745.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&如此理解面向对象编程」&i class=&icon-external&&&/i&&/a&&/b&恰恰相反,他认为过度 OO 设计是敏捷带来的后果,而我认为敏捷开发恰恰使得过度 OO 设计变得毫无意义。——那些过度 OO 设计的人并没有真正的懂敏捷。&u&真正的敏捷是在保证测试案例通过的前提下,直接简单粗暴的修改源代码,而不是为了避免修改三行源代码制造出十多个类&/u&。&br&&br&如果我们的开发基于「旧代码就是需要被经常修改的」,那么面向对象中的一部分特性其实变成了毫无意义的累赘。当然这并意味着 OOP 完全无用,在这个前提下,其实我们需要重新审视面向对象的程序设计,取其精华去其糟粕。&br&&br&封装性,继承性,多态性,这些特性在敏捷开发的情况下还那么重要吗?&br&&br&封装性:在所有代码都公开,随时可能被修改被重构的情况下,封装性意义没有想象的那么大,虽然它仍然有价值。它的价值在于使程序行为更可预期。&br&&br&继承性:继承的本目的是在不修改代码的情况下扩展代码的功能,但如果我们能够自由修改代码,那继承在绝大多数时候并不是最佳选择。事实上,巨大的类设计开销往往都是继承性带来的。继承性在今天不但没有太大的价值,反而常常有害。(这里所说的继承性指的是 invalid s 所说的第一种情况,对他说的第二种情况,我不认为那算作继承性,而应该被算作多态性。)&br&&br&多态性:多态性要求一个函数在接受不同的类型对象作为参数时自动表现出不同的行为。或者说要求不同类型的对象都可以调用相同名字的方法,并且自己处理应该有的不同行为。——这个特性在今天依然重要,而且是最重要的特性,它的存在意味着 OOP 的价值仍然存在——即便对于非 OOP 的程序设计语言。只要应用了多态性思想,那么其实它就是 OOP 的。考虑一下 Linux 中的「一切皆文件」的概念,其实换句话说就是对一切对象皆可调用用于文件的那些方法,这妥妥的是「多态性」。图形界面的编程 API 中,一切对象皆可 Paint(),一切对象皆可 toString(),这妥妥的也是「多态性」。&br&&br&从某种意义上说,「模板」这个一般认为不属于 OOP 的特性,却非常好的体现了多态性。模板允许同名函数接收不同类型的参数自动表现出不同的行为(虽然编译器实际上是生成了多个函数,但从编程的角度来讲它其实是一个函数)。因此我认为懂得模板技术在 OOP 设计中也非常重要,他用更小的开销更好的可读性实现了 OOP 思想。&br&&br&结语:很多时候,巨大的类关系设计开销是为了避免修改旧代码而不是为了使代码更可读。而在修改旧代码不可避免或者根本不需要避免的时候,很多类关系设计开销是不必要的,我们在使用面向对象设计方法时,最好是取其精华去其糟粕,避免那些额外的不必要的类设计开销,以提升代码的可读性为目标来进行设计,而不要以避免修改代码为目标进行设计,在这种情况下 OOP 仍然有其存在的价值。
可能我的看法有些简单粗暴。但是我希望表达一下我的「一家之言」。面向对象编程有很多用途,很多用法,但是我们会发现「设计模式驱动」成为了非常有代表性的流派。有时我们会疑惑,为什么会有设计模式,为什么面向对象编程会出现如此恐怖的这么复杂的类关系…
作为C原教旨教徒和C++一生黑以及从不用类的Python党怒答。有函数有数据结构就可以了,面向对象就是个骗局………
作为C原教旨教徒和C++一生黑以及从不用类的Python党怒答。有函数有数据结构就可以了,面向对象就是个骗局………
来自子话题:
来自子话题:
&p&日凌晨更新:&/p&&br&&p&归纳题主的问题:&/p&&p&这个世界上有各种各样的框架,设计这些五花八门框架的初衷到底是什么?我们该不该学习框架,该如何学习使用这些框架?&/p&&br&&p&回答题主的问题:&/p&&p&&b&一、首先,到底什么是框架?&/b&&/p&&p&想要回答这个问题,我们要慢慢来。&/p&&br&&p&&b&①&br&首先从DRY原则开始说起&/b&&/p&&p&Don't Repeat Yourself,不要重复你的代码。&/p&&p&DRY原则的重要性怎么提都不过分,很多人说编程是种机械性的工作,而有很多程序员也自嘲为码农,意为编程成了一种没有技术含量的体力性工作。如果不想沦为这个境界,首先需要的就是将DRY原则融入你的血液,在今后的编码工作中加以运用。&/p&&br&&p&1)最初级的DRY:语法级别&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&System.out.println(1);
System.out.println(2);
System.out.println(10);
&/code&&/pre&&/div&&p&我想只要学过基础语法,都会采用下面的形式。&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&for (int i = 1; i &= 10; i++) {
System.out.println(i);
&/code&&/pre&&/div&&p&如果发现有任何人采用上面一种形式的编码形式,那么不用怀疑,他对于编程绝对还没有入门。&/p&&p&我们当然会选择省力的做法,这种做法不但省力,还会有利于我们后续修改或扩展这组代码,如:&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&for (int i = 1; i &= 10; i++) {
System.out.println(i * 2 + 1);
&/code&&/pre&&/div&&p&我们进行这样的修改,只需要修改一处,而上面的形式却需要修改10处,当然会更麻烦且更容易出错,所以请记住能不重复就不重复。&/p&&br&&p&2)进阶的DRY原则:方法级别&/p&&p&当我们经常写一些重复性代码时,我们就要注意看能否将其抽取出来成为一个方法,如:&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
&/code&&/pre&&/div&&p&让我们将其抽取到一个方法 threadSleep() 中,这样我们只需要调用 threadSleep() 就可以实现原来的功能,不但所需敲击的代码更少,而且代码看起来更加清楚明白。而为了增加这个方法的复用性,我们还可以将其中固定的数字抽取成为参数,如:&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&private static void threadSleep(int millis) {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
&/code&&/pre&&/div&&p&这样我们就可以利用这个方法实现不同时间的sleep了。要注意提高代码的复用性也是实践DRY原则的一个重要方法,在后面我们也可以看到框架为了提高所谓的灵活性进行的一些设计,如在适当的位置增加扩展点。&/p&&br&&p&3)继续进阶的DRY原则:类型级别&/p&&p&现在我们看一个类&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&public class Person {
// Setter & Getter ...
&/code&&/pre&&/div&&p&我们新建一些Person类实例,并进行一些操作:&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&Person person = new Person();
person.setName(&jack&);
person.setAge(18);
Person person2 = new Person();
person2.setName(&rose&);
person2.setAge(17);
System.out.printf(&Name: %s, Age:%d\n&, person.getName(), person.getAge());
System.out.printf(&Name: %s, Age:%d\n&, person2.getName(), person2.getAge());
&/code&&/pre&&/div&&p&观察这些代码,其实有很大的DRY改造空间,首先可以添加一个构造方法&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&public Person(String name, int age) {
this.name =
this.age =
&/code&&/pre&&/div&&p&其次,可以添加一个toString()方法&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&public String toString() {
return String.format(&Name: %s, Age: %d&, name, age);
&/code&&/pre&&/div&&p&这样的话,上面的代码就可以改成下面的形式。&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&Person person = new Person(&jack&, 18);
Person person2 = new Person(&rose&, 17);
System.out.println(person.toString());
System.out.println(person2.toString());
&/code&&/pre&&/div&&br&&p&4)继续继续进阶的DRY原则:多个类组合级别&/p&&p&上面的代码我们其实还是有改善空间,就是利用容器类&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&List&Person& list = new ArrayList&&();
list.add(new Person(&jack&, 18));
list.add(new Person(&rose&, 17));
list.forEach(p -& System.out.println(p));
&/code&&/pre&&/div&&p&这里利用JDK8的Stream API以及Lambda表达式输出,其实可以进一步简化为 &/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&list.forEach(System.out::println);
&/code&&/pre&&/div&&p&这里我们可以看到,基本上我们写代码只写有变化的代码,而尽量不写机械性重复性的代码,其实后面我们就会知道,这就叫专注于业务逻辑,所谓业务逻辑就是你这个项目中,与别的项目都不一样的地方,必须由你亲自去编写实现的部分。&/p&&p&其实容器类很大程度上也是为了帮助我们编写代码而被设计出来的,首先让我们不必为每一个对象起名字(省去了person,person2,...等变量),然后又为批量操作提供了可能性。像是这样一系列有用的类组合起来可以称之为类库。常用的类库有Commons-Lang包等,为我们提供了一大批实用方法,我之所以提到类库,也是因为框架其实也是一种特殊的类库,但是却与一般的类库有着本质的不同。&/p&&br&&p&&b&②&br&设计模式,更高层级的DRY应用&/b&&/p&&p&上面我讲到了DRY原则的几个层次,一般情况下大家也早就这样使用了,属于入门之后很容易自己就想到得一些层次。但是设计模式不一样,设计模式是经过长时间编码之后,经过系统性的总结所提出的针对某一类问题的最佳解决方案,又称之为最佳实践。&/p&&p&而在小规模的编码工作中,其实并不需要什么设计模式,只有大型程序才有设计模式发挥的空间,所以我们需要借助一些特定领域有足够规模的问题来了解一下设计模式存在的必要性。&/p&&br&&p&1)连接数据库,进行一些操作,并安全释放数据库连接。&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&public static boolean updatePassword(String username, String password, String newpassword) {
Connection conn =
PreparedStatement stmt =
ResultSet rs =
boolean success =
conn = beginTransaction();
stmt = conn.prepareStatement(&select id, password from user where username = ?&);
stmt.setString(1, username);
rs = stmt.executeQuery();
if (rs.next()) {
if (rs.getString(&password&).equals(password)) {
PreparedStatement stmt2 =
stmt2 = conn.prepareStatement(&update user set password = ? where id = ?&);
stmt2.setString(1, newpassword);
stmt2.setLong(2, rs.getLong(&id&));
success = stmt2.executeUpdate() & 0;
} finally {
safeClose(stmt2);
commitTransaction(conn);
} catch (SQLException e) {
rollbackTransaction(conn);
throw new RuntimeException(e);
} finally {
safeClose(rs);
safeClose(stmt);
safeClose(conn);
&/code&&/pre&&/div&&p&上面是一个简单的数据库事务,虽然只有一个查询和一个更新,但是想要将其继续简化却并不容易,虽然其中有关于业务逻辑的部分只是少量几行代码,但是初始化,异常,提交,回滚操作让我们很难抽取出一个合适的方法来。虽然我们已经抽取出了 begin,commit,rollback,safeClose等方法,但是仍嫌繁琐。&/p&&p&我们发现之所以我们难以抽取方法,主要是因为流程,因为里面牵扯到流程控制,而流程控制一般是由我们程序员来控制的,所以也就必然需要我们手动编码来完成。难道真的就不能继续简化了吗?这就是需要设计模式的时候了。&/p&&br&&p&2)应用设计模式「模板方法模式」&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&public static boolean updatePassword(String username, String password, String newpassword) {
return connection(conn -& statement(conn, &select id, password from user where username = ?&, stmt -& {
stmt.setString(1, username);
return resultSet(stmt, rs -& {
if (rs.next()) {
if (rs.getString(&password&).equals(password)) {
long id = rs.getLong(&id&);
return statement(conn, &update user set password = ? where id = ?&, stmt2 -& {
stmt2.setString(1, newpassword);
stmt2.setLong(2, id);
return stmt2.executeUpdate() == 1;
&/code&&/pre&&/div&&p&可以看到,所有的conn,stmt,rs的开启和关闭,事务的提交和回滚都不用自己手动编写代码进行操作了,之所以可以达到这个效果,就是因为使用了模板方法设计模式,核心就是通过回调方法传递想对资源进行的操作,然后将控制权交给另一个方法,让这个方法掌握流程控制,然后适当的时候回调我们的代码(也就是我们自己写的业务逻辑相关的代码)。&/p&&p&这是需要额外写的几个方法&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&public interface ConnectionCallback&T& {
T doConnection(Connection conn) throws SQLE
public interface StatementCallback&T& {
T doStatement(PreparedStatement stmt) throws SQLE
public interface ResultSetCallback&T& {
T doResultSet(ResultSet rs) throws SQLE
public static &T& T connection(ConnectionCallback&T& callback) {
Connection conn =
T result =
conn = beginTransaction();
result = callback.doConnection(conn);
commitTransaction(conn);
} catch (SQLException e) {
rollbackTransaction(conn);
throw new RuntimeException(e);
} finally {
safeClose(conn);
public static &T& T statement(Connection conn, String sql, StatementCallback&T& callback) throws SQLException {
PreparedStatement stmt =
T result =
stmt = conn.prepareStatement(sql);
result = callback.doStatement(stmt);
} finally {
safeClose(stmt);
public static &T& T resultSet(PreparedStatement stmt, ResultSetCallback&T& callback) throws SQLException {
ResultSet rs =
T result =
rs = stmt.executeQuery();
result = callback.doResultSet(rs);
} finally {
safeClose(rs);
&/code&&/pre&&/div&&p&你们可能会疑惑,这些代码加上我们写的业务逻辑的代码,比原来的代码还要长,有什么必要使用这个设计模式。这正是我前面已经指出的一个问题,那就是要你的程序规模足够大才有必要应用设计模式,试想如果你有上百个乃至上千个数据库操作方法需要写,那么是不是写这几个额外的方法,就不算什么了呢。&/p&&p&其实这正是DRY原则在更高层次上的应用,即结合设计模式来达到更高层次的代码复用效果,进而应用DRY原则。而想要在这个层次继续向上攀升,那就必须是结合众多设计模式以及一些高层架构设计,能够帮助我们实现这一目的的就是框架。&/p&&br&&p&3)框架,是设计模式的集大成者,是DRY原则的最高应用&/p&&p&先让我们来看一下,使用框架会是什么样的一种体验?&/p&&p&这里以Hibernate + Spring声明式事务为例&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&@Transactional
public boolean updatePassword(String username, String password, String newpassword) {
User user = (User) session().createQuery(&from User where username = :username&)
.setString(&username&, username)
.uniqueResult();
if (user != null && user.getPassword().equals(password)) {
user.setPassword(newpassword);
&/code&&/pre&&/div&&p&可以发现令人惊讶的简洁,而且代码逻辑异常清晰,完全不需要考虑conn,stmt,rs等资源的释放,以及事务的提交和回滚,但是这些事情其实框架已经默默的帮我们做到了。这才叫真正的专注于业务逻辑,尽最大可能的只写与业务逻辑有关的代码。&/p&&p&当然这些框架的效果虽然神奇,其实只要细细探究其内部原理,是完全可以理解并掌握的。&/p&&br&&p&&b&二、那么问题就来了,框架到底是什么?要不要学,怎么学?&/b&&/p&&p&上面我说过了,框架其实就是一个或一组特殊的类库,特殊在什么地方?特殊在控制权转移!&/p&&p&框架与一般类库不同的地方是,我们调用类库,而框架调用我们。也就是说框架掌握整个程序的控制权,我们必须一定程度上把程序流程的控制权交给框架,这样框架才能更好的帮助我们。&/p&&p&下面以JavaWeb开发为例再进行一些说明,并顺便简单介绍一下JavaWeb的一些脉络。&/p&&br&&p&①&br&静态网页时代&/p&&p&本来网站都是一个个静态HTML组成的,或许这些网页还是用Dreamweaver写的,但是这样的静态页面显然不能满足我们,很快我们就迎来了动态网页的时代。&/p&&br&&p&②&br&Servlet时代&/p&&p&如果熟悉HTTP协议的话,我们就知道其实访问网页的过程不过是一次TCP连接罢了。浏览器发起TCP连接到服务器,服务器接受请求,然后返回HTML代码作为响应。那么我们完全可以等到接受到请求之后,再动态生成HTML代码返回给客户端。&/p&&p&Servlet就是这么做的,其主要代码不过是利用out.write()一点一点的输出HTML代码罢了。当然我们可以在其中掺杂一点动态的东西,如返回当前的时间。&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&out.write(&&!DOCTYPE html&\r\n&);
out.write(&&html&\r\n&);
out.write(&&head&\r\n&);
out.write(&&title&Index Page&/title&\r\n&);
out.write(&&/head&\r\n&);
out.write(&&body&\r\n&);
out.write(&Hello, & + new Date() + &\r\n&);
out.write(&&/body&\r\n&);
out.write(&&/html&\r\n&);
&/code&&/pre&&/div&&br&&p&③ JSP包打天下的时代&/p&&p&纯粹的Servlet很是丑陋,给前端程序员理解和修改这样的代码带来了很多困难。因此JSP技术被发明了出来,原理也不复杂,就是不直接写Servlet,而是先写好JSP文件,再由服务器将JSP文件编译成Servlet。而JSP中是以常见的HTML标签为主,这样前端程序员就能方便的修改这些代码了。&/p&&br&&div class=&highlight&&&pre&&code class=&language-text&&&!DOCTYPE html&
&title&Index Page&/title&
Hello, &%=new Date()%&
&/code&&/pre&&/div&&p&PS:由只使用 Servlet到使用JSP,虽然是一个简单的变化,但这迎合了前后端专业分工的大趋势,让前段人员只需要懂得HTML/CSS/JavaScrip代码就可以开始工作,而不需要学习Servlet那枯燥无味的用法,因此借着JSP技术的东风,JavaWeb技术迅速的扩展开来了。&/p&&br&&p&④ Servlet + JSP 时代&/p&&p&随着JSP技术的发展,用它写成的网站也越来越大,业务逻辑也越来越复杂。开发人员渐渐发现整个网站渐渐的再次变成了一团乱麻,不仅仅是JSP中夹杂了大量的Java代码,页面之间的耦合关系也越来越紧密。&/p&&p&即便是要修改一个简单的按钮文本,或者是引入一段静态的内容,也需要打开越来越庞大的JSP页面,艰难到找到需要修改的部分,有时还不仅仅是一处,这种修改是有很大的风险的,完全有可能引入新的错误。&/p&&p&这时候开发者渐渐意识到,仅仅使用JSP是不行的,JSP承担了太多的责任。这时人们又想起了Servlet,Servlet中主要使用Java代码,处理业务逻辑非常轻松。如果JSP只使用HTML代码,而将业务逻辑的代码转移到Servlet中,就可以大大的减轻JSP的负担,并且让前后端分工更加明确。&/p&&br&&p&⑤&br&MVC模式时代&/p&&p&在&br&Servlet + JSP模式的基础上,Java阵营进一步发展出了一种适合JavaWeb应用的设计模式,MVC设计模式,即将程序分为显示层(Viewer),控制层(Controller),模型层(Model)。如下图所示:&/p&&p&&img src=&/3cc75d6efa23b6269acb_b.png& data-rawwidth=&744& data-rawheight=&325& class=&origin_image zh-lightbox-thumb& width=&744& data-original=&/3cc75d6efa23b6269acb_r.png&&一次典型的访问是这样的流程:&/p&&p&1. 用户输入网址或点击链接或提交表单,浏览器发起请求&/p&&p&2. --& 通过互联网,通过HTTP协议 --&&/p&&p&3. Tomcat接受到HTTP请求,生成HttpSerlvetRequest对象,根据Web.xml的配置,调用开发者编写的HttpSerlvet,HttpServlet根据请求内容,调用JavaBean获取数据,JavaBean从数据库获取数据,返回HttpServlet,HttpServlet将数据转发给JSP,JSP负责将数据渲染为HTML,由Tomcat负责将HTML转化为HTTP响应,返回客户端。&/p&&p&4. --& 通过互联网,通过HTTP协议 --&&/p&&p&5. 客户端浏览器接收到HTTP响应,浏览器将HTML渲染为页面,并运行其中可能存在的JavaScript进一步调整界面。&/p&&br&&p&整个流程必须由开发者精确设计才能运作流畅,其中客户端HTML和JavaScript属于前端设计,服务器运行的其他内容属于后端设计。虽然符合J2EE规范的Tomcat等应用服务器已经帮我们实现了最复杂的一块,即HTTP协议部分,还给我们提供了JSP这个模板引擎,以及自定义标签等手段。但是在控制层,在模型层,J2EE能给我们的帮助少之甚少。&/p&&br&&p&就拿用户提交一个表单为例,而我们在Servlet中获取参数为例,虽然不用我们解析HTTP报文,应该已经是要谢天谢地了,但是我们要做的事情仍然很多,分析一下:&/p&&br&&p&1. 客户端传过来的数据全是文本,而我们需要的是Java对象。&/p&&p&2. 凡是文本就有编码问题,而这需要前后端配合解决。&/p&&p&3. 客户端的输入是不可信的,我们必须校验参数的合法性。&/p&&p&4. 我们还必须将校验结果反馈给客户,并且最好不要让客户全部重新输入。&/p&&p&5. 我们往往不是只有一个参数需要,而是有几个甚至更多参数,要妥善的处理各种情况组合。&br&&/p&&br&&p&这些事情几乎全部都需要我们手动编码来完成,几乎每一个 Servlet 都充斥着这样的代码,设置编码,获取参数,校验参数,校验通不过返回错误信息,校验通过则进行业务处理。而更重要的是,获取参数仅仅是整个流程中的一小步,我们的Servlet中存在着大量的重复性,机械性代码,而处理业务逻辑的代码可能只有一两行。&/p&&br&&p&⑥&br&JavaWeb框架&/p&&p&既然存在着大量的重复,我们当然不能忍,必须请出DRY大法。显然JavaWeb应用是一个规模庞大,流程复杂的应用,我们正需要JavaWeb框架的帮助。以Struts2框架为例,他能给我们什么帮助呢?&/p&&br&&p&1. 在控制层,由Struts2的核心控制器接管控制权,将本来在Web.xml进行配置的一些工作,转移到自定义的struts.xml文件中,这个文件的配置形式更友好。&/p&&p&2. Struts2封装了Serlvet Api,使用POJO对象作为控制器(Action),大量使用反射,不要求继承特定类,有利于复用及单元测试。提供ActionSupport类,结合struts2标签,能很方面实现的校验信息的收集及反馈。&br&&/p&&p&3. 提供国际化支持,在显示层有国际化相关的标签,在控制层由国际化相关的API。提供基于配置的校验及JS生成技术。智能化的参数类型转换,支持自定义转换器。提供Action拦截器,方便实现AOP模式。&/p&&p&4. 提供了基于OGNL表达式的数据共享模式,前后端数据交流更简单,提供了Struts2标签库,简单好用,支持多种模板,如FreeMarker,支持各种插件,如JSON,支持整合多种框架,如Spring。总之一句话,能在各方各面给我们强大的帮助。&/p&&br&&p&⑦&br&所以当然要学框架,要用框架,那么要怎么学?&/p&&p&1. 用框架要知其然,还要知其所以然,要大体明白框架实现一个功能特性的原理,不能只是会用,只是觉得很神奇就可以了。就拿前面的Hibernate + Spring声明式事务为例,要弄明白框架这部分是怎么实现的。&/p&&p&2. 首先要夯实你的语言基础,如JavaSE基础,语法掌握,用法掌握,有些同学语法还不熟练就开始学框架,等于地基没打就起高楼,你可能会快一步,但是迟早要遇到瓶颈,甚至摔跟头。&/p&&p&3. 那么何时开始学习框架?我不建议新手一开始就直接使用框架。&/p&&p&就好像一开始学习编程语言,大家都不推荐直接使用IDE,一定要用命令行自己编译运行几个文件之后,了解清楚了之后才可以使用IDE,要不然对于底层原理不了解,遇到问题没法自己手动排查。&/p&&p&4. 使用框架也是一样,如果不是自己写多了重复性的代码,就很难理解框架为什么要这么设计。如果不尝试几种不同的实现,就很难理解框架为了灵活性而做出的设计和扩展点。如果不写几十个权限检查语句,就很难理解AOP到底有什么好处。&/p&&p&5. 框架这么好,我该全部使用框架吗?首先只有在规模以上的程序中,才有应用框架的必要,一个简单的程序没必要使用框架,当然如果你很熟练,使用也无所谓。&/p&&p&6. 要学习一下框架的核心源代码,要为扩展框架做好准备,因为虽然框架基本上还算灵活,但是面对错综复杂的业务需求,永远不可能面面俱到,而你不了解框架的话,可能会给你实现业务需求造成麻烦。这也是有些人坚持使用Servlet+JSP原生开发,而不是用框架的理由。&/p&&p&7. 只要程序大了,归根究底还是要使用框架的,不是用别人写好的,就是自己写一套。这里我不建议自己写,不要重复造轮子,总有专业造轮子的。你草草写就的往往不如别人已经千锤百炼的代码。除非你是为了学习与研究的目的,自己写,那就是一件很好的事情。&/p&&br&&p&三、待更新的事项&/p&&p&① AOP模式分析&/p&&p&② Spring容器分析&/p&&p&③ Spring代理分析&/p&&p&④ Spring声明式事务分析&/p&&p&⑤ Struts2标签及OGNL表达式分析&/p&&p&⑤ Hibernate瞬时态,持久态,托管态分析&/p&
日凌晨更新:归纳题主的问题:这个世界上有各种各样的框架,设计这些五花八门框架的初衷到底是什么?我们该不该学习框架,该如何学习使用这些框架?回答题主的问题:一、首先,到底什么是框架?想要回答这个问题,我们要慢慢来。①首先从DRY原…
先回答题主的问题: 框架的初衷。简言之就是: &br&&blockquote&&b&抽象出重复,专注于类似,以标准化高质量的代码品质降低广大码农的工作量,拯救IT行业的颈椎,杜绝青光眼,维系夫妻和睦,恋人如胶似漆,单身养肾,下载种子长留。&/b&&/blockquote&&br&当我们做开发时,绝大部分流程都是相同或者至少类似的,迥异的只不过是我们各个项目的业务逻辑。那么每次我们都要把那些相同的类似的代码敲一遍,或者复制一遍,或者复制后小修一番,这就造成了大部分double work。&br&&br&复制过来的代码又总是多少会有些别扭,质量也是高高低低,命名也是风格多样,&b&子曾经曰过,懒~是人类进步的阶梯!&/b&于是乎那些牛到爆懒到家的码农便琢磨着将那些大家都会重复的过程搞出来,然后进行标准化,有个高大上的名词叫“&b&抽象&/b&”,然后要对这个被抽象出来的东西起个名字吧?如何做到名称白富美还能接地气呢?&br&&br&码农嘛,纯朴的民工!联想到跟自己建房子时候差不太多呀,这里承重墙,那里抠个OO,下面打个XX,各家房子框架都这样啊,不同的是这里你刷个白漆,那里你挂个陀思妥耶夫思密达sir,对吧?那好吧,我们也叫~~~框架&br&&br&这里就实现了标准化和流水线分工对不对?&br&&ul&&li&写框架的专注于框架,越来越稳定性能越来越强悍;&br&&/li&&li&妹纸专注于给房子化妆,整了个洋名儿:UI(有爱)或者UEX(无前任),部门名取决于妹纸是否专注于失恋;&br&&/li&&li&大老爷们儿还是负责搬砖,这里抽油烟机,那里抽水马桶,美其名曰射机湿,&br&&/li&&/ul&你会发现以前装马桶要长宽高,要糊水泥,有时候还要配个皮揣子,尼玛,有够闹心不?现在你只需要写马桶,然后马桶就通了,源代码里甚至还有TOTO字样,有木有,太方便了,妈妈再也不用担心我熬夜设计马桶了,速度,质量,舒适度,只需3分钟,通通ISO9001,通通通~~~&br&&br&这里,你应该发现了框架的优势与弱势:&br&1)框架的初心是抽象出那些重复度高的代码,言外之意就是如果你的项目足够简单,简单到裸着写都没有什么重复代码,那么你就不需要框架。框架是用来建房子的,铺个草坪要毛框架啊???框架是“牛刀”,杀只鸡要毛牛刀啊?有木有?
【&b&框架有其自身的适用场景,是瑞士军刀,非妇孺皆宜】&/b&&br&&br&2)一旦你使用了框架,那么你这个项目里面有相当一部分的代码质量是稳定健壮的,比自己写的代码好太多了,我们也不好意思让那么烂的代码伴随框架,所以要多花点时间锤炼自己的代码,与框架同行。 &b&【框架可以让你在保证代码质量的基础上,拥有更多时间来关注自身的业务逻辑与代码】&/b&&br&&br&3)自己建的房子松松垮垮,但手熟;别人建的房子坚固,但是不熟悉,你需要有个熟悉的过程,所谓学习曲线就是它了,使用框架,这是无数人,啊,应该是无数牛人智慧的凝聚,理解牛人的思维,怪癖都是一种修炼,需要时间,需要耐心。 &b&【框架有学习曲线,有时候,框架学习的难度比手写代码自己实现的难度要高不少】&/b&&br&&br&------------------------&br&&br&再来回答题主的另外一个问题: 框架各自的优势:&br&&br&你应该去各个框架的官网仔细研究它的readme,而不是在这里发问,各个框架都有无数大V的blog有深浅皆宜的使用,请围观。&br&&blockquote&&b&切记,玉和兄长不可兼得。&/b&&br&&b&如果追求开发速度快,ROR&/b&&br&&b&如果追求方便Full Stack, Django&/b&&br&&b&如果追求学习简单上手, PHP框架任选&/b&&br&&b&如果直接瞄准FB,TB,BAT,追求性能至上,大团队开发,企业铂金镶钻级开发,Java框架任选&/b&&/blockquote&------------------------&br&最后回答题主的另外一个问题:框架具体做哪些业务?&br&&br&&blockquote&&b&框架的业务,喔,就是做框架啊,还能有什么呢?至于区别嘛,你说说万科,招商地产,保利,绿地他们造的框架有什么区别?&/b&&/blockquote&&br&这就涉及到题主最开始的一句话了,如何学习?&br&我的建议是:&br&&b&初学者,不要学习任何框架,裸写,一定要裸写.&/b&&br&&br&&br&&b&不裸写几万行代码,你怎么知道哪些代码是重复的?不把Ctrl + C/V摁到掉漆,你怎么能感同身受那些框架射机湿不是吃饱了撑着?裸写代码不被Bug折腾到头发掉了一地,你怎么能理解代码健壮性的可贵?裸写代码不被别人SQL注入拖库,你怎么会感谢Django的ORM?网页不慢到吃顿火锅才能打开你怎么忆起Tornado非阻塞的柔情似水?&/b&&br&&br&&br&&b&不裸写代码到颈椎痛,腰椎间盘突出,800度近视,夫妻分床,恋人冷战,BT硬盘坏道,苍老师隐退,你怎么能直接用框架,你怎么好意思叫程序猿?&/b&
先回答题主的问题: 框架的初衷。简言之就是: 抽象出重复,专注于类似,以标准化高质量的代码品质降低广大码农的工作量,拯救IT行业的颈椎,杜绝青光眼,维系夫妻和睦,恋人如胶似漆,单身养肾,下载种子长留。当我们做开发时,绝大部分流程都是相同或者至…
推荐你先看一下Wikipedia上的&a href=&///?target=http%3A//en.wikipedia.org/wiki/Software_framework& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Software framework&i class=&icon-external&&&/i&&/a&。&br&&br&里面提到Framework和普通库的几点区别里,我认为最重要的一点是inversion of control。Framework有一点像“凡客体”,“写作者”只需要把相应的词填进去即可。&b&规定了开发者写哪些代码/不写哪些代码,怎么写代码——这就是框架主要解决的问题。&/b&如果要说具体解决哪些问题,不同的框架不一样,可以参考&a href=&///?target=http%3A//en.wikipedia.org/wiki/Comparison_of_web_application_frameworks& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Comparison of web application frameworks&i class=&icon-external&&&/i&&/a&。&br&&br&Web开发框架里我用过Django、Express、Ember(CakePHP和Rails只是几年前做过小的试验项目,就不说了)。&br&&br&Django是功能很齐全的框架,测试框架、邮件、表单验证、缓存、甚至输出PDF,做网站大多数用到的功能都包含了,它考虑到的各种Scenarios相当细致,奉行的Battery included原则不是吹的。Django社区也很活跃,有许多好用的app、扩展,比如south(Django 1.7 已经集成db migration)、django-reversion、celery、django-cms、easy-thumbnails、&strong&&a href=&///?target=https%3A///tomchristie/django-rest-framework& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&django-rest-framework&i class=&icon-external&&&/i&&/a&&/strong&,再加上丰富的Python资源,极大地减轻开发负担。&br&&br&如果你想试验一些API,可以用Django的Console(Rails等也有相同的功能)。如果你想定期或不定期地执行一些任务,比如清除过期的API tokens、批量添加用户,可以写管理脚本。&br&&br&Django的文档也很好,使用内置的东西和自己扩展都很方便。&br&&br&Express是轻量的框架,只提供最基本的功能,当然同样有很多扩展可以用。Node平台吸引我的是Yeoman这样的工具,跟Rails和Django比,在自动化各种mundane tasks上进了一大步。当然也可以将Grunt引入Django项目,不过不是一站式的体验了。Jade模版写起来也很舒服。&br&&br&Express也有很多库可以解决它没解决的问题,比如Django自带的reverse url功能,在Express里可以用&a href=&///?target=https%3A///web-napopa/node-reversable-router& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&web-napopa/node-reversable-router · GitHub&i class=&icon-external&&&/i&&/a& 代替。这回倒是轮到Express没有提供“一站式体验”了。&br&&br&现在不少人始倾向于用RESTful API + 前端MVC框架了。前端MVC框架可以提供类似桌面应用的体验。推荐看看Ember。
推荐你先看一下Wikipedia上的。里面提到Framework和普通库的几点区别里,我认为最重要的一点是inversion of control。Framework有一点像“凡客体”,“写作者”只需要把相应的词填进去即可。规定了开发者写哪些代码/不写哪些代码,怎么…
来自子话题:
大家应该都能说几个自己常用的模式出来,我倒觉得没有必要纠结一个设计模式的列表,不如说说设计模式的学习过程,了解了这个过程,你会发现哪些模式比较常见可能并非最重要的问题。&br&&br&&b&第一阶段:手中无剑,心中无剑&/b&&br&&br&&br&初步掌握一门面向对象编程语言,了解了类、对象、接口等概念。听别人说到“设计模式”感觉特别高大上,自己不太懂,也用不太起来。&br&&br&&b&第二阶段:手中有剑,心中无剑&/b&&br&&br&既然不太懂,那就学呗。四人帮经典23式开始一招一招比划。但此时手中虽有了重剑,却不太能舞起来。如果说某人第一次看《设计模式》,就看得通透的话,那我真是佩服得五体投地。&br&&br&主要原因是,刚开始学习设计模式的人,开发经验一般相对较少,此时我们还缺乏实际经历的应用场景去做参考,看着书中的例子很难感同身受,比如我们看到“封装XX变化性”之类的描述很难引起共鸣。而且很有可能模式的描述和场景压根就不太明白。&br&&br&但是没关系,此时我们也能像模像样喊出几个招式了,也基本有个感性认识,虽然实际工作中还不太有感觉。&br&&br&&b&第三阶段:手中有剑,心中有剑&/b&&br&&br&&br&随着自己的学习和开发经历的丰富,你已经对设计模式有一定理解了。在阅读开源代码,或者身边高手的代码时,你经常可以发现可以对应于某模式,因为类命名会出现设计模式中的术语,Factory, Builder,Strategy等。你逐渐明白了某个模式用在某个地方能起到什么作用,你经常会有一种&Wow&的赶脚。&br&&br&这时你在编程过程中,能够照猫画虎的用设计模式了。而且你开始学习面向对象设计原则,比如SOLID原则也有了一定的理解。你跟别人讨论时,可以大声地说:让我们使用一个XX模式来支持更好的扩展性吧。你时不时会回头去《设计模式》,除了能够提供参考,此时你会有一些新的体会了。&br&&br&&b&第四阶段:手中无剑,心中有剑&/b&&br&&br&你已经有设计和架构的概念了,这时你已经开始面对一个几万行甚至几十万行的项目,有一天你发现代码已经很难快速响应需求变化了,又没办法推倒重来(虽然推倒重来是码农最爱),所以来重构吧,重构的过程中,一个个设计模式把代码维护中的一个个痛点逐个击破,你对代码的“smell”有了更深刻的理解,你开始对copy-paste代码深恶痛绝,你发现原来重构和设计模式竟然是孪生兄弟。&br&&br&你开始明白一个长期维护和演化的项目,设计如此之重要。你不自觉地就会主动去翻《重构》、《浮现式设计》、《领域驱动设计》、Bob大叔的《敏捷软件开发》、《企业应用架构模式》等等,虽然你之前可能看过,但此时你看他们的心境和底蕴已经不一样了,你会发现这帮家伙确实很会总结。&br&&br&最终,你回本溯源,你还是拿起了当初那本《设计模式》,你终于深深地体会到了里面闪烁的智慧,“面向接口编程”,“组合优先继承”,多么朴素的文字,或许很多人都能说出来,但是你确定你现在的理解更加深刻了。&br&&br&你会发现,此时你手中已经无剑了,你不会想着什么模式列表了,不会想着某个模式重要了。进行一个复杂设计时,不自觉地就从软件开发的终极目标“高内聚低耦合”出发,“面向接口编程”,“组合优先继承”,“创建与使用分离”等认知成为体液反应,结合需求场景,每定义下一个类都有“开放封闭”等各种面向对象设计原则护体,随着项目演进,你能敏锐地嗅到各种代码smell,知道如何考虑现有资源去减少该死的技术债,随时进行着重构。&br&&br&有一天,你的小弟问你,怎么State模式和Strategy模式的类图是一样的? 你装逼地笑了笑,说到:这些不重要,心中有剑就能无往不胜。
大家应该都能说几个自己常用的模式出来,我倒觉得没有必要纠结一个设计模式的列表,不如说说设计模式的学习过程,了解了这个过程,你会发现哪些模式比较常见可能并非最重要的问题。第一阶段:手中无剑,心中无剑初步掌握一门面向对象编程语言,了解了类、对…
&a href=&///?target=http%3A///blog/23303& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&论面向组合子程序设计方法 之 创世纪&i class=&icon-external&&&/i&&/a&&br&&a href=&///?target=http%3A///blog/23305& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&论面向组合子程序设计方法 之 失乐园 之补充&i class=&icon-external&&&/i&&/a&&br&&a href=&///?target=http%3A///blog/23306& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&论面向组合子程序设计方法 之 燃烧的荆棘&i class=&icon-external&&&/i&&/a&&br&&a href=&///?target=http%3A///blog/23307& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&论面向组合子程序设计方法 之 新约&i class=&icon-external&&&/i&&/a&&br&&a href=&///?target=http%3A///blog/23309& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&论面向组合子程序设计方法 之 oracle&i class=&icon-external&&&/i&&/a&&br&&a href=&///?target=http%3A///blog/23314& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&论面向组合子程序设计方法 之 重构&i class=&icon-external&&&/i&&/a&&br&&a href=&///?target=http%3A///blog/23317& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&论面向组合子程序设计方法 之 monad&i class=&icon-external&&&/i&&/a&&br&&a href=&///?target=http%3A///blog/23326& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&论面向组合子程序设计方法 之 南无阿弥陀佛&i class=&icon-external&&&/i&&/a&&br&&a href=&///?target=http%3A///blog/23329& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&论面向组合子程序设计方法 之 重构2&i class=&icon-external&&&/i&&/a&&br&&a href=&///?target=http%3A///blog/23681& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&论面向组合子程序设计方法 之 微步毂纹生&i class=&icon-external&&&/i&&/a&&br&&br&熟读并背诵,每个月默写一次
格式对齐。
格式对齐。
来自子话题:
来自子话题:
函数式编程语言不会时刻提醒你找不到对象。
函数式编程语言不会时刻提醒你找不到对象。
问题问的有问题。&br&&br&函数式编程和面向对象编程没有可比性,因为它们不是对立的概念。问题描述中的“Java 不够灵活、限制过多,并且说函数式编程更好”疑为误读。这个问题把多个不同领域的概念放在同一个框架下进行比较,当然无法得出有用的结论。&br&&br&可与函数式编程相比较的概念是过程式编程,主要区别在于2点:1.函数是否返回值;2.函数本身是否可以作为参数与返回值。就这两点来看,其实很多语言都有函数式编程特性,然而因为其笨拙的语法,无法有效利用函数式编程的强大能力,代表性的如C语言中的函数指针。&br&&br&评价一种编程语言可以有多个不同的维度,代码的表现力与使用难度是其中的重要方面。过程式语言(包括各种面向对象语言)通常较符合人的线性思维,也就是事物有明确的先后顺序,在大部分场景下都能很好的解决问题。但是一门编程语言除了基本原语和组合能力以外,还必须拥有一定的抽象能力,才能够完成更加复杂的任务。过程式语言的主要抽象能力仅限于过程,在有些任务中,过程式语言会显得非常捉襟见肘,而函数式语言的抽象能力就强大的多,因为通过将函数作为参数或返回值传递能够对已有的函数实现定制。C语言中的函数指针虽然也能够在一定程度上实现这种定制,但是语法非常难看而且容易出错,因此通常程序员更倾向于其它更繁琐的解决方案。lambda的存在进一步拉大了这种差距。要理解函数式语言的这种特性,可以参考面向对象语言中的控制反转和依赖倒置,它们实际上就是一种特殊的函数传递方式,只是直接传递函数代码要简短的多。&br&&br&Java也不是一个可以函数式语言并列比较的概念,因为Java只是一种语言的实现。不过针对提问,Java的部分特性可以作一点评价。Java语言中没有脱离对象存在的函数,这是一种保持一致性的设计,但是,任何时候都必须定义一个类常常造成不便。在一些场景中,须要回调的地方使用匿名类能够提供某种便利,但是与匿名函数(lambda)相比,实现相同功能的代码仍然多很多。也就是说Java语言的一大问题是代码量较大,而且杂乱。即便有这样的问题,Java仍然变成最流行的语言之一,很大程度上是以Eclipse为代表的IDE的功劳,繁琐的Java代码可以通过少数几个按键快速打出,部分的缓解了Java语言的这个问题。&br&&br&单纯的说函数式编程比过程式编程更好其实并不令人非常信服,很多函数式语言(如scheme)实际上支持过程。虽然纯粹的函数式在理论上比较诱人(总能保证相同的输入给出相同的输出),并且实作出了Haskell这种纯函数式语言,但是由于没有状态,在很多情况下会给开发过程造成极大的不便。&br&&br&函数式编程和面向对象编程的概念互不冲突,从一个事实可以看出来。大部分动态语言,如javascript, ruby, python都是同时支持这两者,而且可以充分利用这两方面的特性。因此这些语言在表现力,远远强于一些各种静态类型语言,不考虑库的情况下实现相同功能所需的代码量也少的多。只要没有很高的性能要求或平台限制,这些语言是很好的选择。
问题问的有问题。函数式编程和面向对象编程没有可比性,因为它们不是对立的概念。问题描述中的“Java 不够灵活、限制过多,并且说函数式编程更好”疑为误读。这个问题把多个不同领域的概念放在同一个框架下进行比较,当然无法得出有用的结论。可与函数式编…
某种程度上说,所谓的“面向对象”其实是解决了“聪明的码农不多”这个问题。。。而这也是FP唯一无法解决的问题。。。
某种程度上说,所谓的“面向对象”其实是解决了“聪明的码农不多”这个问题。。。而这也是FP唯一无法解决的问题。。。
来自子话题:
&p&贴出自己博客中的一篇文章&/p&&br&&p&&a href=&///?target=http%3A//yaocoder./8899& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&网络编程释疑之:同步,异步,阻塞,非阻塞&i class=&icon-external&&&/i&&/a&&br&&/p&&p&一讲到网络编程的I/O模型,总会涉及到这几个概念。问了很多人,没几个能清晰地讲出他们之间的区别联系,甚至在网络上也有很多不同的观点,也不知是中国文字释义的博大精深,还是本来这几个概念就是绕人不倦。今天我也来给大家讲解一下我对这几个概念的理解。&/p&&p&既然网络上众说纷纭,不如找个权威参考一下,这个权威就是&a href=&///?target=http%3A///subject/1500149/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&《UNIX网络编程:卷一》&i class=&icon-external&&&/i&&/a&第六章——I/O复用。书中向我们提及了5种类UNIX下可用的I/O模型:&/p&&ul&&li&&p&阻塞式I/O;&/p&&/li&&li&&p&非阻塞式I/O;&/p&&/li&&li&&p&I/O复用(select,poll,epoll...);&/p&&/li&&li&&p&信号驱动式I/O(SIGIO);&/p&&/li&&li&&p&异步I/O(POSIX的aio_系列函数);&/p&&/li&&/ul&&p&&strong&阻塞式I/O模型&/strong&:默认情况下,所有套接字都是阻塞的。怎么理解?先理解这么个流程,一个输入操作通常包括两个不同阶段:&/p&&p&(1)等待数据准备好;&br&(2)从内核向进程复制数据。&/p&&p&&br&对于一个套接字上的输入操作,第一步通常涉及等待数据从网络中到达。当所有等待分组到达时,它被复制到内核中的某个缓冲区。第二步就是把数据从内核缓冲区复制到应用程序缓冲区。 好,下面我们以阻塞套接字的recvfrom的的调用图来说明阻塞&/p&&img src=&/e83d68da03da2e8cb630edfd_b.jpg& data-rawwidth=&1058& data-rawheight=&556& class=&origin_image zh-lightbox-thumb& width=&1058& data-original=&/e83d68da03da2e8cb630edfd_r.jpg&&&br&&p&标红的这部分过程就是阻塞,直到阻塞结束recvfrom才能返回。&/p&&p&&strong&非阻塞式I/O&/strong&: 以下这句话很重要:进程把一个套接字设置成非阻塞是在通知内核,当所请求的I/O操作非得把本进程投入睡眠才能完成时,不要把进程投入睡眠,而是返回一个错误。看看非阻塞的套接字的recvfrom操作如何进行&/p&&img src=&/4bc31cab27a9a732ab7d1ba9e674ed64_b.jpg& data-rawwidth=&1064& data-rawheight=&631& class=&origin_image zh-lightbox-thumb& width=&1064& data-original=&/4bc31cab27a9a732ab7d1ba9e674ed64_r.jpg&&&br&&p&可以看出recvfrom总是立即返回。&/p&&p&&strong&I/O多路复用&/strong&:虽然I/O多路复用的函数也是阻塞的,但是其与以上两种还是有不同的,I/O多路复用是阻塞在select,epoll这样的系统调用之上,而没有阻塞在真正的I/O系统调用如recvfrom之上。如图&/p&&img src=&/b1ec6a4fd5a6a94cd7f8_b.jpg& data-rawwidth=&1136& data-rawheight=&732& class=&origin_image zh-lightbox-thumb& width=&1136& data-original=&/b1ec6a4fd5a6a94cd7f8_r.jpg&&&br&&p&&strong&信号驱动式I/O&/strong&:用的很少,就不做讲解了。直接上图&/p&&img src=&/f5c22eac6f0_b.jpg& data-rawwidth=&1139& data-rawheight=&711& class=&origin_image zh-lightbox-thumb& width=&1139& data-original=&/f5c22eac6f0_r.jpg&&&br&&p&&strong&异步I/O&/strong&:这类函数的工作机制是告知内核启动某个操作,并让内核在整个操作(包括将数据从内核拷贝到用户空间)完成后通知我们。如图:&/p&&img src=&/5819fd0fdff2bd4fdc9652291aca1831_b.jpg& data-rawwidth=&1109& data-rawheight=&603& class=&origin_image zh-lightbox-thumb& width=&1109& data-original=&/5819fd0fdff2bd4fdc9652291aca1831_r.jpg&&&br&&p&注意红线标记处说明在调用时就可以立马返回,等函数操作完成会通知我们。&/p&&p&等等,大家一定要问了,同步这个概念你怎么没涉及啊?别急,您先看总结。 其实前四种I/O模型都是同步I/O操作,他们的区别在于第一阶段,而他们的第二阶段是一样的:在数据从内核复制到应用缓冲区期间(用户空间),进程阻塞于recvfrom调用。相反,异步I/O模型在这两个阶段都要处理。&/p&&img src=&/eaf42d61bbff_b.jpg& data-rawwidth=&3200& data-rawheight=&1800& class=&origin_image zh-lightbox-thumb& width=&3200& data-original=&/eaf42d61bbff_r.jpg&&&br&&p&再看POSIX对这两个术语的定义:&/p&&ul&&li&&p&同步I/O操作:导致请求进程阻塞,直到I/O操作完成;&/p&&/li&&li&&p&异步I/O操作:不导致请求进程阻塞。&/p&&/li&&/ul&&p&好,下面我用我的语言来总结一下阻塞,非阻塞,同步,异步&/p&&ul&&li&&p&阻塞,非阻塞:&strong&进程/线程要访问的数据是否就绪,进程/线程是否需要等待;&/strong&&/p&&/li&&li&&p&同步,异步:&strong&访问数据的方式,同步需要主动读写数据,在读写数据的过程中还是会阻塞;异步只需要I/O操作完成的通知,并不主动读写数据,由操作系统内核完成数据的读写。&/strong&&/p&&/li&&/ul&
贴出自己博客中的一篇文章一讲到网络编程的I/O模型,总会涉及到这几个概念。问了很多人,没几个能清晰地讲出他们之间的区别联系,甚至在网络上也有很多不同的观点,也不知是中国文字释义的博大精深,还是本来这几…
“阻塞”与&非阻塞&与&同步&与“异步&不能简单的从字面理解,提供一个从分布式系统角度的回答。&br&&b&1.同步与异步&/b&&br&同步和异步关注的是&b&消息通信机制&/b& (synchronous communication/ asynchronous communication)&br&所谓同步,就是在发出一个*调用*时,在没有得到结果之前,该*调用*就不返回。但是一旦调用返回,就得到返回值了。&br&换句话说,就是由*调用者*主动等待这个*调用*的结果。&br&&br&而异步则是相反,&b&*调用*在发出之后&/b&&b&,这个调用就直接返回了,所以没有返回结果&/b&。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在*调用*发出后,*被调用者*通过状态、通知来通知调用者,或通过回调函数处理这个调用。&br&&br&典型的异步编程模型比如Node.js&br&&br&举个通俗的例子:&br&你打电话问书店老板有没有《分布式系统》这本书,如果是同步通信机制,书店老板会说,你稍等,”我查一下&,然后开始查啊查,等查好了(可能是5秒,也可能是一天)告诉你结果(返回结果)。&br&而异步通信机制,书店老板直接告诉你我查一下啊,查好了打电话给你,然后直接挂电话了(不返回结果)。然后查好了,他会主动打电话给你。在这里老板通过“回电”这种方式来回调。&br&&br&2. 阻塞与非阻塞&br&阻塞和非阻塞关注的是&b&程序在等待调用结果(&/b&&b&消息,&/b&&b&返回值)时的状态.&/b&&br&&br&阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。&br&非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。&br&&br&还是上面的例子,&br&你打电话问书店老板有没有《分布式系统》这本书,你如果是阻塞式调用,你会一直把自己“挂起”,直到得到这本书有没有的结果,如果是非阻塞式调用,你不管老板有没有告诉你,你自己先一边去玩了, 当然你也要偶尔过几分钟check一下老板有没有返回结果。&br&在这里阻塞与非阻塞与是否同步异步无关。跟老板通过什么方式回答你结果无关。&br&&br&&br&如果是关心blocking IO/ asynchronous IO, 参考
Unix Network Programming &a href=&///?target=http%3A//english.tebyan.net/newindex.aspx%3Fpid%3D31159%26BookID%3D23760%26PageIndex%3D92%26Language%3D3& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&View Book&i class=&icon-external&&&/i&&/a&
“阻塞”与"非阻塞"与"同步"与“异步"不能简单的从字面理解,提供一个从分布式系统角度的回答。1.同步与异步同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication)所谓同步,就是在发出一个*调用*时,在没有得到结果之…
老张爱喝茶,废话不说,煮开水。&br&出场人物:老张,水壶两把(普通水壶,简称水壶;会响的水壶,简称响水壶)。&br&1 老张把水壶放到火上,立等水开。(同步阻塞)&br&老张觉得自己有点傻&br&2 老张把水壶放到火上,去客厅看电视,时不时去厨房看看水开没有。(同步非阻塞)&br&老张还是觉得自己有点傻,于是变高端了,买了把会响笛的那种水壶。水开之后,能大声发出嘀~~~~的噪音。&br&3 老张把响水壶放到火上,立等水开。(异步阻塞)&br&老张觉得这样傻等意义不大&br&4 老张把响水壶放到火上,去客厅看电视,水壶响之前不再去看它了,响了再去拿壶。(异步非阻塞)&br&老张觉得自己聪明了。&br&&br&&br&所谓同步异步,只是对于水壶而言。&br&普通水壶,同步;响水壶,异步。&br&虽然都能干活,但响水壶可以在自己完工之后,提示老张水开了。这是普通水壶所不能及的。&br&同步只能让调用者去轮询自己(情况2中),造成老张效率的低下。&br&&br&所谓阻塞非阻塞,仅仅对于老张而言。&br&立等的老张,阻塞;看电视的老张,非阻塞。&br&情况1和情况3中老张就是阻塞的,媳妇喊他都不知道。虽然3中响水壶是异步的,可对于立等的老张没有太大的意义。所以一般异步是配合非阻塞使用的,这样才能发挥异步的效用。&br&&br&——来源网络,作者不明。
老张爱喝茶,废话不说,煮开水。出场人物:老张,水壶两把(普通水壶,简称水壶;会响的水壶,简称响水壶)。1 老张把水壶放到火上,立等水开。(同步阻塞)老张觉得自己有点傻2 老张把水壶放到火上,去客厅看电视,时不时去厨房看看水开没有。(同步非阻塞…
已有帐号?
社交帐号登录
无法登录?
社交帐号登录

我要回帖

更多关于 成员函数 的文章

 

随机推荐