文化

Notification js提醒插件

Notification js提醒插件,有很有炫的效果.demo: http://tympanus.net/Development/NotificationStyles [repo owner=”codrops” name=”NotificationStyles”]

设计模式原则总结

from:http://blog.prosight.me/blogs/984/名称解释0、单一职责原则(SRP)就一个类而言,应该仅有一个引起它变化的原因。一、”开放-封闭”原则(OCP)在软件设计模式中,这种不能修改,但可以扩展的思想也是最重要的一种设计原则。即软件实体(类、模板、函数等等)应该可以扩展,但是不可修改。【通俗】:设计的时候,时刻考虑,尽量让这个类是足够好,写好了就不要去修改了,如果新需求来,我们增加一些类就完事了,原来的代码能不动则不动。二、里氏代换原则(LSP)1.一个软件实体如果使用的是一个父类的话,那么一定适用于该子类,而且他觉察不出父类对象和子类对象的区别。也就是说,在软件里面,把父类都替换成它的子类,程序的行为没有变化。【一句话】:**子类型必须能够替换掉他们的父类型。**三、依赖倒置原则(DIP)1.高层模块不应该依赖于底层模块。两个都应该依赖抽象。2.抽象不应该依赖于细节,细节依赖于抽象(【白话】:针对接口编程,不要针对实现编程。四、接口隔离原则(ISP)1.使用多个专门的接口比使用单一的总接口总要好。换而言之,从一个客户类的角度来讲:一个类对另外一个类的依赖性应当是建立在最小接口上的。2.过于臃肿的接口是对接口的污染。不应该强迫客户依赖于它们不用的方法。五、合成/聚合复用原则(CARP)尽量使用合成/聚合,尽量不要使用类继承。【聚合】:表示一种弱的拥有关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分。【合成】:一种强的拥有关系,提现了严格的部分和整体的关系,部分和整体的生存周期一致。六、迪米特法则(LoD)最少知识原则强调类之间的松耦合。即:如果两个类不必彼此直接通信,那么着两个类就不应当发送直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。

一致性hash和solr千万级数据分布式搜索引擎中的应用

from: http://www.lanceyan.com/tech/arch/consistenthashing_and_solr.html互联网创业中大部分人都是草根创业,这个时候没有强劲的服务器,也没有钱去买很昂贵的海量数据库。在这样严峻的条件下,一批又一批的创业者从创业中获得成功,这个和当前的开源技术、海量数据架构有着必不可分的关系。比如我们使用mysql、nginx等开源软件,通过架构和低成本服务器也可以搭建千万级用户访问量的系统。新浪微博、淘宝网、腾讯等大型互联网公司都使用了很多开源免费系统搭建了他们的平台。所以,用什么没关系,只要能够在合理的情况下采用合理的解决方案。那怎么搭建一个好的系统架构呢?这个话题太大,这里主要说一下数据分流的方式。比如我们的数据库服务器只能存储200个数据,突然要搞一个活动预估达到600个数据。可以采用两种方式:横向扩展或者纵向扩展。纵向扩展是升级服务器的硬件资源。但是随着机器的性能配置越高,价格越高,这个代价对于一般的小公司是承担不起的。横向扩展是采用多个廉价的机器提供服务。这样一个机器只能处理200个数据、3个机器就可以处理600个数据了,如果以后业务量增加还可以快速配置增加。在大多数情况都选择横向扩展的方式。如下图:现在有个问题了,这600个数据如何路由到对应的机器。需要考虑如果均衡分配,假设我们600个数据都是统一的自增id数据,从1~600,分成3堆可以采用 id mod 3的方式。其实在真实环境可能不是这种id是字符串。需要把字符串转变为hashcode再进行取模。目前看起来是不是解决我们的问题了,所有数据都很好的分发并且没有达到系统的负载。但如果我们的数据需要存储、需要读取就没有这么容易了。业务增多怎么办,大家按照上面的横向扩展知道需要增加一台服务器。但是就是因为增加这一台服务器带来了一些问题。看下面这个例子,一共9个数,需要放到2台机器(1、2)上。各个机器存放为:1号机器存放1、3、5、7、9 ,2号机器存放 2、4、6、8。如果扩展一台机器3如何,数据就要发生大迁移,1号机器存放1、4、7, 2号机器存放2、5、8, 3号机器存放3、6、9。如图:从图中可以看出 1号机器的3、5、9迁移出去了、2好机器的4、6迁移出去了,按照新的秩序再重新分配了一遍。数据量小的话重新分配一遍代价并不大,但如果我们拥有上亿、上T级的数据这个操作成本是相当的高,少则几个小时多则数天。并且迁移的时候原数据库机器负载比较高,那大家就有疑问了,是不是这种水平扩展的架构方式不太合理?—————————–华丽分割线—————————————一致性hash就是在这种应用背景提出来的,现在被广泛应用于分布式缓存,比如memcached。下面简单介绍下一致性hash的基本原理。最早的版本 http://dl.acm.org/citation.cfm?id=258660。国内网上有很多文章都写的比较好。如: http://blog.csdn.net/x15594/article/details/6270242下面简单举个例子来说明一致性hash。准备:1、2、3 三台机器还有待分配的9个数 1、2、3、4、5、6、7、8、9一致性hash算法架构步骤一、构造出来 2的32次方 个虚拟节点出来,因为计算机里面是01的世界,进行划分时采用2的次方数据容易分配均衡。另 2的32次方是42亿,我们就算有超大量的服务器也不可能超过42亿台吧,扩展和均衡性都保证了。二、将三台机器分别取IP进行hashcode计算(这里也可以取hostname,只要能够唯一区别各个机器就可以了),然后映射到2的32次方上去。比如1号机器算出来的hashcode并且mod (2^32)为 123(这个是虚构的),2号机器算出来的值为 2300420,3号机器算出来为 90203920。这样三台机器就映射到了这个虚拟的42亿环形结构的节点上了。三、将数据(1-9)也用同样的方法算出hashcode并对42亿取模将其配置到环形节点上。假设这几个节点算出来的值为 1:10,2:23564,3:57,4:6984,5:5689632,6:86546845,7:122,8:3300689,9:135468。可以看出 1、3、7小于123, 2、4、9 小于 2300420 大于 123, 5、6、8 大于 2300420 小于90203920。从数据映射到的位置开始顺时针查找,将数据保存到找到的第一个Cache节点上。如果超过2^32仍然找不到Cache节点,就会保存到第一个Cache节点上。也就是1、3、7将分配到1号机器,2、4、9将分配到2号机器,5、6、8将分配到3号机器。这个时候大家可能会问,我到现在没有看见一致性hash带来任何好处,比传统的取模还增加了复杂度。现在马上来做一些关键性的处理,比如我们增加一台机器。按照原来我们需要把所有的数据重新分配到四台机器。一致性hash怎么做呢?现在4号机器加进来,他的hash值算出来取模后是12302012。 5、8 大于2300420 小于12302012 ,6 大于 12302012 小于90203920 。这样调整的只是把5、8从3号机器删除,4号机器中加入 5、8。同理,删除机器怎么做呢,假设2号机器挂掉,受影响的也只是2号机器上的数据被迁移到离它节点,上图为4号机器。大家应该明白一致性hash的基本原理了吧。不过这种算法还是有缺陷,比如在机器节点比较少、数据量大的时候,数据的分布可能不是很均衡,就会导致其中一台服务器的数据比其他机器多很多。为了解决这个问题,需要引入虚拟服务器节点的机制。如我们一共有只有三台机器,1、2、3。但是实际又不可能有这么多机器怎么解决呢?把 这些机器各自虚拟化出来3台机器,也就是 1a 1b 1c 2a 2b 2c 3a 3b 3c,这样就变成了9台机器。实际 1a 1b 1c 还是对应1。但是实际分布到环形节点就变成了9台机器。数据分布也就能够更分散一点。如图:写了这么多一致性hash,这个和分布式搜索有什么半点关系?我们现在使用solr4搭建了分布式搜索,测试了基于solrcloud的分布式平台提交20条数据居然需要几十秒,所以就废弃了solrcloud。采用自己hack solr平台,不用zookeeper做分布式一致性管理平台,自己管理数据的分发机制。既然需要自己管理数据的分发,就需要考虑到索引的创建,索引的更新。这样我们的一致性hash也就用上了。整体架构如下图:建立和更新需要维持机器的位置,能够根据数据的key找到对应的数据分发并更新。这里需要考虑的是如何高效、可靠的把数据建立、更新到索引里。备份服务器防止建立服务器挂掉,可以根据备份服务器快速恢复。读服务器主要做读写分离使用,防止写索引影响查询数据。集群管理服务器管理整个集群内的服务器状态、告警。整个集群随着业务增多还可以按照数据的类型划分,比如用户、微博等。每个类型按照上图架构搭建,就可以满足一般性能的分布式搜索。对于solr和分布式搜索的话题后续再聊。扩展阅读:java的hashmap随着数据量的增加也会出现map调整的问题,必要的时候就初始化足够大的size以防止容量不足对已有数据进行重新hash计算。疫苗:Java HashMap的死循环 http://coolshell.cn/articles/9606.html一致性哈希算法的优化—-关于如何保正在环中增加新节点时,命中率不受影响 (原拍拍同事scott)http://scottina.iteye.com/blog/650380语言实现:http://weblogs.java.net/blog/2007/11/27/consistent-hashing java 版本的例子http://blog.csdn.net/mayongzhan/archive/2009/06/25/4298834.aspx PHP 版的例子http://www.codeproject.com/KB/recipes/lib-conhash.aspx C语言版本例子

header的安全配置指南

from: http://drops.wooyun.org/tips/11660x00 背景在统计了Alexa top 100万网站的header安全分析之后(2012年11月 - 2013年3月 - 2013年11月),我们发现其实如何正确的设置一个header并不是一件容易的事情。尽管有数不胜数的网站会使用大量有关安全方面的header,但并没有一个像样的平台能够为开发者们提供必要的信息,以辨别那些常见的错误设置。或者说,即使这些安全方面的header设置正确了,也没有一个平台能够为开发者提供一个系统的测试方法,用来测试正确与否。这些header如果设置错误了不仅会产生安全的假象,甚至会对网站的安全产生威胁。veracode认为安全性header是网络防护中非常重要的一环,并且他希望让开发者们能够简捷、正确地设置站点。如果您对某一header或设置有任何疑问,我们有极好的资源能够追踪到浏览器支持情况。 0x01 细节1. X-XSS-Protection目的这个header主要是用来防止浏览器中的反射性xss。现在,只有IE,chrome和safari(webkit)支持这个header。正确的设置0 – 关闭对浏览器的xss防护  1 – 开启xss防护  1; mode=block – 开启xss防护并通知浏览器阻止而不是过滤用户注入的脚本。  1; report=http://site.com/report – 这个只有chrome和webkit内核的浏览器支持,这种模式告诉浏览器当发现疑似xss攻击的时候就将这部分数据post到指定地址。  通常不正确的设置0; mode=block; – 记住当配置为0的时候,即使加了mode=block选项也是没有效果的。需要指出的是,chrome在发现这种错误的配置后还是会开启xss防护。  1 mode=block; – 数字和选项之间必须是用分号分割,逗号和空格都是错误的。但是这种错误配置情况下,IE和chrome还是默认会清洗xss攻击,但是不会阻拦。如何检测如果过滤器检测或阻拦了一个反射性xss以后,IE会弹出一个对话框。当设置为1时,chrome会隐藏对反射性xss的输出。如果是设置为 1; mode=block ,那么chrome会直接将user-agent置为一个空值:, URL  这种形式。参考文献Post from Microsoft on the X-XSS-Protection HeaderChromium X-XSS-Protection Header Parsing SourceDiscussion of report format in WebKit bugzilla2. X-Content-Type-Options目的这个header主要用来防止在IE9、chrome和safari中的MIME类型混淆攻击。firefox目前对此还存在争议。通常浏览器可以通过嗅探内容本身的方法来决定它是什么类型,而不是看响应中的content-type值。通过设置 X-Content-Type-Options:如果content-type和期望的类型匹配,则不需要嗅探,只能从外部加载确定类型的资源。举个例子,如果加载了一个样式表,那么资源的MIME类型只能是text/css,对于IE中的脚本资源,以下的内容类型是有效的:application/ecmascript  application/javascript  application/x-javascript  text/ecmascript  text/javascript  text/jscript  text/x-javascript  text/vbs  text/vbscript  对于chrome,则支持下面的MIME 类型:text/javascript  text/ecmascript  application/javascript  application/ecmascript  application/x-javascript  text/javascript1.1  text/javascript1.2  text/javascript1.3  text/jscript  text/live script正确的设置nosniff – 这个是唯一正确的设置,必须这样。  通常不正确的设置‘nosniff’ – 引号是不允许的  : nosniff – 冒号也是错误的 如何检测在IE和chrome中打开开发者工具,在控制台中观察配置了nosniff和没有配置nosniff的输出有啥区别。参考文献Microsoft Post on Reducing MIME type security risksChromium Source for parsing nosniff from responseChromium Source list of JS MIME typesMIME Sniffing Living Standard3. X-Frame-Options目的这个header主要用来配置哪些网站可以通过frame来加载资源。它主要是用来防止UI redressing 补偿样式攻击。IE8和firefox 18以后的版本都开始支持ALLOW-FROM。chrome和safari都不支持ALLOW-FROM,但是WebKit已经在研究这个了。正确的设置DENY – 禁止所有的资源(本地或远程)试图通过frame来加载其他也支持X-Frame-Options 的资源。  SAMEORIGIN – 只允许遵守同源策略的资源(和站点同源)通过frame加载那些受保护的资源。  ALLOW-FROM http://www.example.com…

离站提示JS工具:Ouibounce

离站提示JS工具:Ouibounce,当离开网站时给出一个提醒,从jobbole看到的.tip:和bootstrap的model 结合时要在参数的callback中 手动 用$('#share').modal();触发,官网没说,这个要注意.demo如下:[repo owner=”carlsednaoui” name=”ouibounce”]

tabIndent.js让你在Textarea中也能用Tab键

很多程序员都会习惯性的在Textarea中按Tab键进行缩进,结果是——焦点移动到下一个控件去了。tabIndent.js就是一个专门用来解决这个问题的小巧脚本,只需要在页面中引用它,并调用tabIndent.renderAll();即可处理class为tabindent的Textarea。[repo owner=”julianlam” name=”tabIndent.js”]

Web 热图库 Heatmap.js

Heatmap.js用来生成基于用户自定义数据上的web 热图,内嵌html5 画布元素。可根据以下数据来源绘制热图:静态数据鼠标移动鼠标点击支持浏览器:Firefox 3.6+, Chrome 10, Safari 5, Opera 11 and IE 9+.[repo owner=”pa7” name=”heatmap.js”]

光影下的壮丽风景

【光影下的壮丽风景】Eric Hinesp拍摄照片基本都是自然风光,并且蕴含了壮丽的风云场景。在他的照片里大部分使用长曝光的技术手段,让云霞、水面或星空呈现一种柔软,恬淡的弱光氛围。

面向对象编程的弊端是什么?

from : http://zingson.com/72.html弊端是,没有人还记得面向对象原本要解决的问题是什么。1、面向对象原本要解决什么?(或者说有什么优良特性)似乎很简单,但实际又很不简单:面向对象三要素封装、继承、多态。(警告:事实上,从业界如此总结出这面向对象三要素的一刹那开始,就已经开始犯错了!)。封装:封装的意义,在于明确标识出会访问某个数据结构(用面向对象的术语来说就是 类成员变量)的所有接口。有了封装,就可以明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者;而外部调用者也可以知道自己不可以碰哪里。这就提供一个良好的合作基础——或者说,只要接口这个基础约定不变,则代码改变不足为虑。继承+多态:继承和多态必须一起说。一旦割裂,就说明理解上已经误入歧途了。先说继承:继承同时具有两种含义:其一是继承基类的方法,并做出自己的扩展——号称解决了代码重用问题;其二是声明某个子类兼容于某基类(或者说,接口上完全兼容于基类),外部调用者可无需关注其差别。再说多态:基于对象所属类的不同,外部对同一个方法的调用,实际执行的逻辑不同。很显然,多态实际上是依附于继承的第二种含义的。让它与封装、继承这两个概念并列,是不符合逻辑的。不假思索的就把它们当作可并列概念使用的人,显然是从一开始就被误导了。实践中,继承的第一种含义(实现继承)意义并不很大,甚至常常是有害的。因为它使得子类与基类出现强耦合。继承的第二种含义非常重要。它又叫“接口继承”。接口继承实质上是要求“做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的所有对象”——这在程序设计上,叫做归一化。归一化使得外部使用者可以不加区分的处理所有接口兼容的对象集合——就好象linux的泛文件概念一样,所有东西都可以当文件处理,不必关心它是内存、磁盘、网络还是屏幕(当然,如果你需要,当然也可以区分出“字符设备”和“块设备”,然后做出针对性的设计:细致到什么程度,视需求而定)。归一化的实例:a、一切对象都可以序列化/toStringb、一切UI对象都是个window,都可以响应窗口事件。——必须注意,是一切(符合xx条件的)对象皆可以做什么,而不是“一切皆对象”。后者毫无意义。显然,归一化可以大大简化使用者的处理逻辑:这和带兵打仗是类似的,班长需要知道每个战士的姓名/性格/特长,否则就不知道该派谁去对付对面山坡上的狙击手;而连长呢,只需知道自己手下哪个班/排擅长什么就行了,然后安排他们各自去守一段战线;到了师长/军长那里,他更关注战场形势的转变及预期……没有这种层层简化、而是必须直接指挥到每个人的话,累死军长都没法指挥哪怕只是一场形势明朗的冲突——光一个个打完电话就能把他累成哑巴。软件设计同样。比如说,消息循环在派发消息时,只需知道所有UI对象都是个window,都可以响应窗口消息就足够了;它没必要知道每个UI对象究竟是什么——该对象自己知道收到消息该怎么做。合理划分功能层级、适时砍掉不必要的繁杂信息,一层层向上提供简洁却又完备的信息/接口,高层模块才不会被累死——KISS是最难也是最优的软件设计方法,没有之一。总结:面向对象的好处实际就这么两点。一是通过封装明确定义了何谓接口、何谓接口内部实现、何谓接口的外部调用者,使得大家各司其职,不得越界;二是通过继承+多态这种内置机制,在语言的层面支持归一化的设计,并使得内行可以从代码本身看到这个设计——但,注意仅仅只是支持归一化的设计。不懂如何做出这种设计的外行仍然不可能从瞎胡闹的设计中得到任何好处。显然,不用面向对象语言、不用class,一样可以做归一化的设计(如老掉牙的泛文件概念、游戏行业的一切皆精灵),一样可以封装(通过定义模块和接口),只是用面向对象语言可以直接用语言元素显式声明这些而已;而用了面向对象语言,满篇都是class,并不等于就有了归一化的设计。甚至,因为被这些花哨的东西迷惑,反而更加不知道什么才是设计。2、人们以为面向对象是什么、以及因此制造出的悲剧以及闹剧误解一、面向对象语言支持用语言元素直接声明封装性和接口兼容性,所以用面向对象语言写出来的东西一定更清晰、易懂。事实上,既然class意味着声明了封装、继承意味着声明了接口兼容,那么错误的类设计显然就是错误的声明、盲目定义的类就是无意义的喋喋不休。而错误的声明比没有声明更糟;通篇毫无意义的喋喋不休还不如错误的声明。除非你真正做出了漂亮的设计,然后用面向对象的语法把这个设计声明出来——仅仅声明真正有设计、真正需要人们注意的地方,而不是到处瞎叫唤——否则不可能得到任何好处。一切皆对象实质上是在鼓励堆砌毫无意义的喋喋不休。大部分人——注意,不是个别人——甚至被这种无意义的喋喋不休搞出了神经质,以至于非要在喋喋不休中找出意义:没错,我说的就是设计模式驱动编程,以及如此理解面向对象编程。误解二、面向对象三要素是封装、继承、多态,所以只要是面向对象语言写的程序,就一定“继承”了语言的这三个优良特性。事实上,如前所述,封装、继承、多态只是语言层面对良好设计的支持,并不能导向良好的设计。如果你的设计做不出真正的封装性、不懂得何谓归一化,那它用什么写出来都是垃圾。误解三、把软件写成面向对象的至少是无害的。要了解事实上是什么,需要先科普几个概念。什么是真正的封装?——回答我,封装是不是等于“把不想让别人看到、以后可能修改的东西用private隐藏起来”?显然不是。如果功能得不到满足、或者未曾预料到真正发生的需求变更,那么你怎么把一个成员变量/函数放到private里面的,将来就必须怎么把它挪出来。你越瞎搞,越去搞某些华而不实的“灵活性”——比如某种设计模式——真正的需求来临时,你要动的地方就越多。真正的封装是,经过深入的思考,做出良好的抽象,给出“完整且最小”的接口,并使得内部细节可以对外透明(注意:对外透明的意思是,外部调用者可以顺利的得到自己想要的任何功能,完全意识不到内部细节的存在;而不是外部调用者为了完成某个功能、却被碍手碍脚的private声明弄得火冒三丈;最终只能通过怪异、复杂甚至奇葩的机制,才能更改他必须关注的细节——而且这种访问往往被实现的如此复杂,以至于稍不注意就会酿成大祸)。一个设计,只有达到了这个高度,才能真正做到所谓的“封装性”,才能真正杜绝对内部细节的访问。否则,生硬放进private里面的东西,最后还得生硬的被拖出来——当然,这种东西经常会被美化成“访问函数”之类渣渣(不是说访问函数是渣渣,而是说因为设计不良、不得不以访问函数之类玩意儿在封装上到处挖洞洞这种行为是渣渣)。一个典型的例子,就是C++的new和过于灵活的内存使用方式之间的耦合。这个耦合就导致了new[]/delete[]、placement new/placement delete之类怪异的东西:这些东西必须成对使用,怎么分配就必须怎么释放,任何错误搭配都可能导致程序崩溃——这是为了兼容C、以及得到更高执行效率的无奈之举;但,它更是“抽象层次过于复杂,以至于无法做出真正透明的设计”的典型案例:只能说,c++设计者是真正的大师,如此复杂的东西在他手里,才仅仅付出了如此之小的代价。(更准确点说,是new/delete和c++的其它语言元素之间是非正交的;于是当同时使用这些语言元素时,就不可避免的出现了彼此扯淡的现象。即new/delete这个操作对其它语言元素非透明:在c++的设计里,是通过把new/delete分成两层,一是内存分配、二是在分配的内存上初始化,然后暴露这个分层细节,从而在最大程度上实现了封装——但比之其它真正能彼此透明的语言元素间的关系,new/delete显然过于复杂了)。这个案例,可以非常直观的说明“设计出真正对外透明的封装”究竟会有多难。接口继承真正的好处是什么?是用了继承就显得比较高大上吗?显然不是。接口继承没有任何好处。它只是声明某些对象在某些场景下,可以用归一化的方式处理而已。换句话说,如果不存在“需要不加区分的处理类似的一系列对象”的场合,那么继承不过是在装X罢了。封装可应付需求变更、归一化可简化(类的使用者的)设计:以上,就是面向对象最最基本的好处。——其它一切,都不过是在这两个基础上的衍生而已。换言之,如果得不到这两个基本好处,那么也就没有任何衍生好处——应付需求变更/简化设计并不是打打嘴炮就能做到的。了解了如上两点,那么,很显然:1、如果你没有做出好的抽象、甚至完全不知道需要做好的抽象就忙着去“封装”,那么你只是在“封”和“装”而已。这种“封”和“装”的行为只会制造累赘和虚假的承诺;这些累赘以及必然会变卦的承诺,必然会为未来的维护带来更多的麻烦,甚至拖垮整个项目。正是这种累赘和虚假的承诺的拖累,而不是所谓的为了应付“需求改变”所必需的“灵活性”,才是大多数面向对象项目代码量暴增的元凶。2、没有真正的抓到一类事物(在当前应用场景下)的根本,就去设计继承结构,是必不会有所得的。不仅如此,请注意我强调了在当前应用场景下。这是因为,分类是一个极其主观的东西,不存在普适的分类法。举例来说,我要研究种族歧视,那么必然以肤色分类;换到法医学,那就按死因分类;生物学呢,则搞门科目属种…… 想象下,需求是“时尚女装”,你却按“窒息死亡/溺水死亡/中毒死亡之体征”来了个分类……你说后面这软件还能写吗?类似的,我遇到过写游戏的却去纠结“武器装备该不该从游戏角色继承”的神人。你觉得呢?事实上,游戏界真正的抽象方法之一是:一切都是个有位置能感受时间流逝的精灵;而某个“感受到时间流逝显示不同图片的对象”,其实就是游戏主角;而“当收到碰撞事件时,改变主角下一轮显示的图片组的”,就是游戏逻辑。看看它和“武器装备该不该从游戏角色继承”能差多远。想想到得后来,以游戏角色为基类的方案会变成什么样子?为什么会这样?——你还敢说面向对象无害吗?——在真正明白何谓封装、何谓归一化之前,每一次写下class,就在错误的道路上又多走了一步。——设计真正需要关注的核心其实很简单,就是封装和归一化。一个项目开始的时候,“class”写的越早,就离这个核心越远。——过去鼓吹的各种面向对象方法论、甚至某些语言本身,恰恰正是在怂恿甚至逼迫开发者尽可能早、尽可能多的写class。误解四、只有面向对象语言写的程序才是面向对象的。事实上,unix系统提出泛文件概念时,面向对象语言根本就不存在;游戏界的精灵这个基础抽象,最初是用C甚至汇编写的;……。面向对象其实是汲取以上各种成功设计的经验才提出来的。所以,面向对象的设计,不必非要c++/java之类支持面向对象的语言才能实现;它们不过是在你做出了面向对象的设计之后,能让你写得更惬意一些罢了——但,如果一个项目无需或无法做出面向对象的设计,某些面向对象语言反而会让你很难受。用面向对象语言写程序,和一个程序的设计是面向对象的,两者是八杆子打不着的两码事。纯C写的linux kernel事实上比c++/java之类语言搞出来的大多数项目更加面向对象——只是绝大部分人都自以为自己到处瞎写class的面条代码才是面向对象的正统、而死脑筋的linus搞的泛文件抽象不过是过程式思维搞出来的老古董。——这个误解之深,甚至达到连wiki词条里面,都把OOP定义为“用支持面向对象的语言写程序”的程度。——恐怕这也是没有人说泛文件设计思想是个骗局、而面向对象却被业界大牛们严厉抨击的根本原因了:真正的封装、归一化精髓被抛弃,浮于表面的、喋喋不休的class/设计模式却成了”正统“!总结: 面向对象其实是对过去成功的设计经验的总结。但那些成功的设计,不是因为用了封装/归一化而成功,而是切合自己面对的问题,给出了恰到好处的设计。让一个初学者知道自己应该向封装/归一化这个方向前进,是好的;用一个面向对象的条条框框把他们框在里面、甚至使得他们以为写下class是完全无需思索的、真正应该追求的是设计模式,则是罪恶的。事实上,class写的越随意,才越需要设计模式;就着错误的实现写得越多、特性用得越多,它就越发的死板,以至于必须更加多得多的特性、模式、甚至语法hack,才能勉强完成需求。只有经过真正的深思熟虑,才有可能做到KISS。到处鼓噪的面向对象编程的最大弊端,是把软件设计工作偷换概念,变成了“就着class及相关教条瞎胡闹,不管有没有好处先插一杠子”,甚至使得人们忘记去关注“抽象是否真正简化了面对的问题”。

动物之魂

动物之魂。丨俄罗斯插画家 Alexandra Khitrova 的空灵系列插画.