2009年06月12日 星期五 10:16
租房时可能和两类人打交道,中介和房东。房东的心态和做任何生意的人一样,都希望尽量高价成交,但中介不是。
事实上,中介会尽力促成成交,而根本 不在乎价格,也不在乎房屋质量。原因有很多,首先,中介做的是无本生意,中介费多一点少一点关系不大,但是,能不能收到中介费则关系很大;其次,中介之间 是有竞争的,一套房源可能登记在多家中介公司,就算同一家中介公司,也会有多个中介人,对中介来说,把一套房子以低于市场价的价格租掉对他来说没有任何损失 照收中介费,反而,如果被其他中介租掉了,他一分钱都拿不到。
既然知道了中介关注什么,就不难理解中介租房时采用的策略了。中介带人看房时是非常 简单粗暴的,他不管房源质量如何,价格如何,始终就带着房客一家家去看,当房客看累了时,房客自然会选择看过的房子中性价比最高的一处来成交。不论房屋装 修情况、租金价格如何,中介总是会说同样的话劝房客成交,根本对房屋状况毫不在乎。
对于房客来说,想要租到价廉物美的房子,和中介打交道时要注意以下几点:
第 一、在中介带你看房以前,先问清楚房屋状况,虽然很多信息不看房是不会知道的,但也有很多信息问问房东就可以知道,比如房屋配备的家具家电、面积、装修时 间、床的大小,是否有地板、墙纸。如果这些状况不如你曾经看过的某套房,那么就告诉中介,不去看房。一定要中介找性价比最高的房子给你看,如果某个中介公 司没有比你曾经看过的房更好的,那么就换家中介公司。不要在没有可能成交的房子上浪费时间,那样会让你变累,而倾向于马上在看过的房子中性价比最高的一处 来成交,而不是潜在可能的性价比最高的房子。
第二、如果看中了一套房子想租,要求中介帮你讲价。不要直接找房东讲价,让中介去谈价格。你只需威胁中介如果价格不降下来就不成交,中介自然会努力帮你讲价的。
第三、有些房屋是托管房,是中介低价租来,改造一下再高价租出去赚取差价的,这类房屋不用交中介费,而且可以要求中介配备缺少的设施或电器。
第 四、在网上找房时只找房东房,不要去找中介房,网上的中介房源几乎都是虚假的。如果你打电话过去,中介会告诉你你要的那套房已经租出去了,但是他有更合适 的房源可以租给你。这是因为中介只是把租房网站当成了一个免费的广告发布地,并不真正在上面提供信息。某些公司甚至要求每个中介人每天要在每个租房网站发 布10条租房信息,实际上每个中介人新增房源都没有10条,这些信息只能是虚假的。所以,对于房客来说,就算要去看中介房,也应该直接去中介公司的门店, 不必在网上找。
有空我再写一篇《三亚旅游心得》,一年多以前就想要写的。
2009年04月24日 星期五 21:28
有时候想要把lua中的表的内容作为log打出来,对于数组,可以这样写:
print(unpack(t))
不过,如果是字符串的键名,则需要写这样的代码:
for key, value in pairs(t) do print(key, value) end
有没有办法像unpack一样,对枚举器进行unpack,返回多个返回值呢?
这也可以用递归实现:
local function _iterator_unpack_i(i, f, s, var1, ...) if var1 == nil then return else return select(i, var1, ...), _iterator_unpack_i(i, f, s,f(s, var1)) end end local function iterator_unpack_i(i, f, s, var) return _iterator_unpack_i(i, f, s, f(s, var)) end local function _iterator_unpack_f(formatstring, f, s, var1, ...) if var1 == nil then return else return format(formatstring, var1, ...), _iterator_unpack_f(formatstring, f, s,f(s, var1)) end end local function iterator_unpack_f(formatstring, f, s, var) return _iterator_unpack_f(formatstring, f, s, f(s, var)) end local function keys(t) return iterator_unpack_i(1, pairs(t)) end local function values(t) return iterator_unpack_i(2, pairs(t)) end
然后你就可以这样用:
print(keys(t))
或者
print(values(t))
或者
print(iterator_unpack_f("%s=%s", pairs(t))) 类似昨天的例子,用递归的实现在参数较少时比用临时表快,而且没有堆内存分配
2009年04月24日 星期五 03:47
从lua 5.1开始,就不鼓励用隐含的arg来处理可变变量了,推荐做法是使用递归来处理...
在lua中,不论多返回值还是函数调用,...都只能用于最后剩下的参数,例如下列代码中
function f(...) print("a", ...) print(..., "a") end f("1", "2")
输出为:
a 1 2 1 a
这是因为第一个print调用是把...作为剩余的所有参数传进去,而第二次调用,...则被调整为1个参数。如果要想把参数添加到...后面,有两种做法。
第一种做法是使用临时表:
local tinsert = table.insert local function append(x, ...) local t = {...} tinsert(t, x) return unpack(t) end
第二种做法则是使用递归:
local function append(x, arg1, ...) if arg1 == nil then return x else return arg1, append(x, ...) end end
当参数较少时(在我的机器上大概10个以下参数时),第二种做法更快。不过,第二种做法的另一个优势是没有分配堆内存,因而可以减少垃圾收集的次数。
2009年01月23日 星期五 19:56
首 先,要对ActionScript 3中的XML提出批评,这个XML搞了些全局设置,比如 ignoreComments、 prettyPrinting啥的。其实这些设置不应该是设置,而应该是构造函数和toXMLString的参数。为了进行某一次序列化或者反序列化的操 作就得改变全局设置,这是很傻的设定。
以下代码证明,Flex中的RemoteObject会猥琐的修改全局设置:
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="main()"> <mx:Script><![CDATA[ import flash.xml.*; import flash.utils.*; import mx.messaging.*; import mx.messaging.channels.*; import mx.rpc.*; import mx.rpc.xml.*; import mx.rpc.remoting.*; import mx.rpc.events.*; private function main():void { var channels:ChannelSet = new ChannelSet(); channels.addChannel(new HTTPChannel(null, "http://xxx.xxx.xxx.xxx/xxx")); var userService:RemoteObject = new RemoteObject(); userService.destination = "XXX"; userService.channelSet = channels; trace("XML.prettyPrinting before RPC invoking:", XML.prettyPrinting); userService.xxx().addResponder(new mx.rpc.Responder( function (event:Object):void { trace("XML.prettyPrinting after RPC invoking:", XML.prettyPrinting); }, function (event:Object):void { trace("XML.prettyPrinting after RPC invoking:", XML.prettyPrinting); } )); } ]]></mx:Script> </mx:Application>
ASDoc 中提到,XML.prettyPrinting的默认设置为true。但是,RemoteObject猥琐的修改了全局设置,导致ASDoc所说只是在忽悠我。事实上,XML.prettyPrinting有时为true,XML.prettyPrinting有时为false,取决于与你代码一起工作的 未知代码干了啥。我的结论是,在一个库里面使用全局变量是很蠢的事情,这一点和云风说的不同。云风说可执行程序中可以使用一些永远不销毁的全局变量,他的 前提是,你确切的知道这些东西的生命周期,而对库、框架之类的东西来说,你永远都不知道别人会怎么使用你,所以全局变量就很猥琐了。
2009年01月09日 星期五 23:27
2007年我写过一篇文章,Ubuntu 的字体设置 。不过Ubuntu 8.04以后出现了新问题,Flash Player中的中文都变成了方块。为什么呢?
要 知道为什么,得从fontconfig匹配字体的方式说起。其实呢,fontconfig匹配字体的时候,会考虑locale(本地化),这个 locale就是我们平常 export LANG=XXX 的那个XXX。中文locale就用中文字体,英文locale就用英文字体,是挺好的。此外,fontconfig的配置文件里有一个 binding="strong"的选项,可以要求fontconfig无视locale,直接选用最前面的字体。
到了Ubuntu 8.04,其中的language-selector配置文件会把一些英文字体添加到了中文字体的前面,还用了<edit binding="strong">的标志。这就产生了一个问题,即使需要渲染一个汉字,fontconfig却总是选择英文字体。这可怎么办?
大部分应用程序的解决办法是使用fontconfig选择的下一个字体渲染,然而Adobe Flash Player却不这样干,它直接渲染出一个方块。所以我们就看到了方块。
最好的解决办法是让Adobe修复这个bug,不过,在Adobe修复这个bug以前,另一种暂时的解决办法就是不要使用 binding="strong"。所以我建议卸载language-selector和language-selector-common这两个包,在 命令行输入以下代码即可:
sudo apt-get remove language-selector language-selector-common 若想使用文泉驿的字体,并且要让等宽字体等宽,可以把以下代码保存到~/.fonts.conf
<?xml version="1.0"?><!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
<match target="pattern">
<test qual="any" name="family" compare="eq">
<string>monospace</string>
</test>
<edit name="family" mode="prepend" binding="strong">
<string>DejaVu Sans Mono</string>
</edit>
</match>
<alias>
<family>sans-serif</family>
<prefer>
<family>WenQuanYi Zen Hei</family>
</prefer>
</alias>
<alias>
<family>monospace</family>
<prefer>
<family>WenQuanYi Zen Hei</family>
</prefer>
</alias>
</fontconfig>
2008年12月22日 星期一 14:37
虽然目前我们项目使用的是Java,但我对嵌入脚本的爱是挡不住的。我惊喜的发现原来Java 5以后就有一个javax.script包,可以嵌入脚本,而且默认就支持JavaScript。JavaScript是我第二热爱的脚本语言,仅次于 Lua,所以我觉得很爽。
有了脚本就是爽,不但可以解析JSON,还可以执行代码,可以在配置文件里面加入函数,可以写公式了。
第 一次被郁闷到,是因为JSON,Java提供了一个ScriptEngine.eval()方法,可以执行脚本。不过这个eval返回的是个 Object,具体是啥类型,我不知道。我是希望JavaScript那边返回一个JSON对象当配置文件的,可是它丢给我一个 java.lang.Object,我根本不知道怎么读里面的配置信息。找了半天也没找到类似Lua API中的lua_setfield之类的东西。不过我不是这么容易屈服的,我写下了一些邪恶的代码,可以读出JSON:
ObjectOperators.java
public interface ObjectOperators { public Object get(Object o, Object key); public void put(Object o, Object key, Object value); public void remove(Object o, Object key); }
ECMAScriptHelper.java
import javax.script.Invocable; import javax.script.ScriptEngine; import javax.script.ScriptException; public final class ECMAScriptHelper { static final public ObjectOperators getObjectOperators(ScriptEngine ecmaScriptEnging) throws ScriptException { return ((Invocable)ecmaScriptEnging).getInterface(ecmaScriptEnging.eval( "({" + " 'get':function(o, key) {" + " return o[key]" + " }," + " 'put':function(o, key, value) {" + " o[key] = value;" + " }," + " 'remove':function(o, key) {" + " delete o[key];" + " }" + "})" ), ObjectOperators.class); } }
使用示例
ScriptEngineManager scriptEngineManager = new ScriptEngineManager(); ScriptEngine scriptEngine = scriptEngineManager.getEngineByName("ECMAScript"); ObjectOperators objectOperators = ECMAScriptHelper.getObjectOperators(scriptEngine); Object json = scriptEngine.eval("({a:1, b:3, c:['xxx', 'yy']})"); System.out.print(objectOperators.get(json, "a")); // 嘿嘿,能print出a的值,1 Object jsonArray = objectOperators.get(json, "c"); String xxx = (String)objectOperators.get(jsonArray, 0); System.out.print(xxx);
顺便说一下,用JavaScript虚拟机执行代码时,若要eval出一个JSON对象,需要在JSON外面加上圆括号,这样才会被认为是一个值。
2008年12月13日 星期六 16:46
领域特定语言(DSL,Domain-Specific Language )是近年来的热门话题。关于DSL,有一个趋势就是在通用语言中集成DSL。
很多年以前,我们如果要在通用语言中使用DSL,通常需要用字符串,比如作数据库访问,就要拼接一串SQL语句。缺点有很多:a) 不自然,特殊字符需要转义;b) 容易留下SQL注入的漏洞;c) 缺乏语法检查,拼错了一个单词还要到运行时才能知道出错,调试起来麻烦。
不 过现在是二十一世纪了。许多语言都内置了一些DSL。比如JavaScript、ActionScript等ECMAScript语言中的新特性E4X, 可以方便的使用XML和XPath。再比如说C# 3.0中的LINQ表达式,可以写SQL查询。这是一个很好的现象,不过本文所讲的则是“定制”DSL,可以不仅仅用上语言本身提供的DSL,还能创建自 己的DSL.
这可以靠运算符重载实现。我最早接触运算符重载是C++,这个特性原本并未打算用来设计DSL,只是希望简化一些日常操作的 代码,比如字符串连接。不过现在是二十一世纪了。终于出现了一群变态程序员把这个特性的潜力挖掘了出来。boost中的许多库都用运算符重载实现了 DSL,比如Boost.Xpressive(实现了正则表达式)、Boost.Spirit(实现了BNF范式)、Boost.Phoenix(实现了 匿名函数声明或者说Lambda表达式)。我自己也用运算符重载实现过static_lambda,可以把一个匿名函数变成一个静态类型的类,用到模板参 数里面。
根据我用过的经验,这个技术是有用的,Boost.Xpressive和Boost.Spirit都很实用。
不过用C++运算符重载实现的DSL还是有很多致命问题的:
1. C++98的模板缺少可变数量的模板参数这个特性。
2. C++98的左值右值引用的匹配有严重缺陷。
3. C++编译速度太慢。
4. C++不支持重载空格。
前两个问题是C++98的问题,再过几年如果C++0x普及了就解决了。
第三个问题是老大难问题,估计等到太阳变成红色巨星的时候都未必能解决。不过C++倒是有其继承者D语言,编译速度奇快无比,用来定制DSL倒是不错,只不过现在用D语言做应用开发又缺乏相应的库、IDE啥的,因而D语言开发的项目很罕见。
第四个问题会导致DSL的语法不能很干净,比如正则表达式里面就得加入 << ,看着有点乱。这个问题运算符重载解决不了,因为运算符重载原本就没打算还有这么变态的功能。
不 过C++本来定位就是有高级特性的底层语言,我们做应用开发的,大多数情况还是用更上层的语言,比如Java、C#、Ruby啥的,来连连数据库、画画网 页。C++的死活于我何相干?我关心的是应用,关心怎么在我现在的Java项目中用上内嵌的XML、SQL之类的东西。Java写SQL用的 PreparedStatement很恶心的一点就在于我要去数问号,设一个参数还得去数数那是第几个问号,很让人吐血。
所以这里我要推荐的是scala,兼容Java字节码,也可以生成兼容.net的cli,能无缝集成到现有系统。这个scala里面就有一个数据库连接的库,可以内嵌SQL语句到代码中:
object foo extends Application { import scala.dbc._ import scala.dbc.Syntax._ import syntax.Statement._ val db = database("jdbc:postgresql://localhost/test","myUserName","") val res = db.executeStatement { select fields ("name" of characterVarying(50)) from "person" } for(val i <- res; val f <- i.fields) { Console.println(f.content.sqlString) } }
这里面的SQL语句并非语言原生支持,而是依靠库实现的,所以很牛叉。
不过只要搞清楚实现,就知道其实一点都不牛叉,因为scala的语法里面可以省略访问成员用的“.”以及函数调用的括号,所以实际上上面那一句
select fields ("name" of characterVarying(50)) from "person"
等价于
select.fields("name" of characterVarying(50)).from("person");
这只不过就是一段函数调用而已。
虽 然这个SQL语法scala只用了一些小把戏就实现了,不过scala是个强类型的语言,运算符重载、自动推断类型等特性它都有,而且又没有C++那样的 缺陷,确实很容易实现任何DSL。此外Java社区争论了很多年的closure它早就有了,还有一大堆函数式编程的,算是Haskell和Java的混 合体吧,既可以干Java干的日常工作,又有小朋友们不会用的牛叉的高级特性。所以,scala是个很好玩的东西,而且比较实用,应该会有前途,值得关 注。
2008年11月25日 星期二 21:17
第一,Cookie管理依赖于单件类CookieHandler,这就使得我们无法在一个Java程序中模拟多个会话上同一个网站。
第 二,默认的CookiePolicy.ACCEPT_ORIGINAL_SERVER不接受不包含Domain属性的Cookie,而根据RFC 2965,HTTP头Set-Cookie2中的Domain属性是可选的。况且,没有Domain属性的Cookie很常见,实践中,至少tomcat 管理Session用的JSESSIONID就没有传Domain.
第三,sun.net.www.protocol.http.InMemoryCookieStore是默认的CookieStore实现,这个实现完全 无视Cookie中指定的Port,虽然这个功能用得很少,但也是RFC的一部分。尽管Java文档上处处提到遵循RFC,但实际代码却完全把这个功能给无视了。
第二个问题的临时解决方案是加上下面这行代码。放在使用URLConnection之前就可以了,一个程序只需要调用一次。
CookieHandler.setDefault(new CookieManager(null, CookiePolicy.ACCEPT_ALL));
如果担心安全问题,这样写更保险些:
CookieHandler.setDefault(new CookieManager(null, new CookiePolicy(){
@Override
public boolean shouldAccept(URI uri, HttpCookie cookie) {
String domain = cookie.getDomain();
if(domain == null) {
return true;
} else {
return HttpCookie.domainMatches(cookie.getDomain(), uri.getHost());
}
}
}));
2008年08月16日 星期六 22:29
有人说浓眉大眼的ActionScript背叛革命了。背叛革命的证据有很多,其中之一就是去掉了eval。虽然正常情况下很少使用eval,不过有的时候,比如调试时还是想要使用eval的。
今天偶然看见AIR里面的HTML引擎,发现可以利用这个东西动态执行JavaScript代码。于是我就包装了一个evalJs函数,第一个参数是要执行的字符串,第二个参数是环境,比如EvalJsTest中就传入了this, 于是JavaScript代码就可以访问flash这边的对象了。最后两个参数是执行完以后的回调函数,分别在正常完成和出错时调用。
我还把getDefinitionByName函数放到了EvalJsTest的公有变量中,这样一来,JavaScript就能很方便地访问ActionScript中的各个类库了。
使用代码进行画图
通过getDefinitionByName取得API,然后再调用。以上代码会打开我的日志(www.ac.net.blog.163.com)
加上void就能避免显示出Result对话框。本行代码将窗口布局改为水平。
嫌字体太小了?没问题,以上代码能让窗口变大e倍。
evalJs的函数的源代码:http://svn.assembla.com/svn/Atry/EvalJs/evalJs.as
EvalJsTest工具的源代码:http://svn.assembla.com/svn/Atry/EvalJs/EvalJsTest.mxml
编译好的EvalJsTest工具:http://svn.assembla.com/svn/Atry/EvalJs/EvalJsTest.air
2008年08月16日 星期六 21:49
width和height这个两个属性很诡异,不同对象的width/height属性的含义完全不同。非常淫荡。
第一种情况是一般的flash.display.DisplayObject,包括flash.display.Shape、flash.display.MovieClip、flash.display.Sprite等,其width和height代表该对象的外接矩形的实际长宽像素数。等一下,这个定义还不严谨,严谨的说法应该是:如果这个DisplayObject的父容器、爷爷容器,一直到祖宗容器也就是到Stage,全部都没有缩放,那么这个width和height就能代表该对象的外接矩形的实际长宽像素数。之所以要加上这个前提,是因为《Flex 2、Flex 3和Flash 9中的缩放》提到过,父容器的缩放并不会改变子对象的属性,而只会改变子对象在屏幕上的实际渲染尺寸。
虽然父容器的缩放并不会改变子对象的属性,但是DisplayObject对象自己的缩放属性(scaleX和scaleY)却会影响自己的width和height. 设置DisplayObject的scaleX和scaleY会改变其width和height,而且,设置width和height也会改变scaleX和scaleY. 这就意味着,设置一般的DisplayObject的width和height就会导致该对象缩放。不过,如果这个DisplayObject原本的width和height为0,则无法改变它的width和height,比如一个空无一物的Sprite,不论如何设置,width和height始终是0.
第二种情况是flash.display.TextField,它重写了基类的set width和set height函数,所以改变TextField的width和height并不会令TextField缩放,而只是改变文本区域的大小。改变width和height时,单个文字不会变大或变小,而可显示的文字数量会变多或变少。
改变TextField的width和height不会改变TextField的scaleX和scaleY,但是改变TextField的scaleX和scaleY会改变TextField的width和height.
第三种情况是Flex的mx.core.UIComponent,它的width和height不代表实际尺寸,而代表“占据”的尺寸,即使是一个空无一物的VBox,也可以设置其width和height(这一点与Sprite不同).
不过UIComponent有一点和TextField类似,改变UIComponent的width和height不会改变UIComponent的scaleX和scaleY,但是改变UIComponent的scaleX和scaleY会改变width和height. UIComponent中有unscaledWidth和unscaledHeight两个属性。unscaledWidth等于width/scaleX, unscaledHeight等于height/scaleY.
因为UICompoent涉及Flex的布局,所以其中还有许多尺寸相关的属性和函数,可以用来自动计算布局。这里就不一一介绍了。
最后说一下MXML中的width属性(attribute),它其实对应了该组件在ActionScript中的percentWidth和width两个属性(property);而MXML中的height属性(attribute)也对应了ActionScript中的percentHeight和height两个属性(property)。
2008年08月16日 星期六 21:48
Flash中有两种缩放。
第一种缩放是对除了Stage以外的DisplayObject设置scaleX和scaleY。
第二种缩放是Stage自动进行的缩放。Stage中虽然有scaleX和scaleY,但若修改这两个值就会抛出异常。这两个值总是1,其实与Stage的缩放无关。Stage自动进行的缩放不会改变包括Stage自己在内的任何对象的width和height,而是根据stage.scaleMode以及播放器右键菜单的放大缩小操作进行的。程序仅能通过scaleMode来控制。
此外,缩放时的缩放算法还会考虑scale9grid属性。
对容器缩放时,尽管容器中子对象的实际渲染效果会受到容器的缩放相关属性的控制,但不论是何种方式的缩放子对象的width、height、scaleX、scaleY等属性都不会改变。
Flash应用程序的默认stage.scaleMode是"showAll",而Flex应用程序的默认值则是"noScale"。因而播放器窗口尺寸改变时,Flex应用程序并不会跟着缩放,而是根据改变了的尺寸来调整内容布局。
2008年08月16日 星期六 21:41
我们知道.NET的很多类都实现了IDisposable接口和dispose方法,而Java的很多类也实现了Closeable接口和close方法。不论是dispose方法还是close方法都要求用户手动调用来释放资源。
可是.NET和Java不是都有垃圾收集吗?为何还需要手动释放资源呢?
这是因为.NET和Java的垃圾收集器并不会立即释放资源,而只在需要的时候才释放资源。某些类是对内核对象的一层包装,它们往往只占用了极少的内存,但却持有系统中某个内核对象,这个内核对象却可能有很大的开销。比如文件句柄。垃圾收集器会在用户内存不足的情况下进行垃圾收集,但却不会顾及操作系统资源是否紧张。比如打开很多文件,可能对于.NET或者Java的虚拟机来说,只分配了很少的内存,根本无所谓,但是对操作系统来说,却可能给这些文件提供了许多的系统缓存。
因此,.NET和Java就提供了dispose或close,这两个方法只会释放系统资源,而不释放内存。因为释放内存是垃圾收集器的职责,不需要用户操心。
最好的做法是在编写.NET和Java代码的时候手动调用dispose或close.
不过,对于ActionScript来说就不是这样的。ActionScript的垃圾收集同时使用了“引用计数”和“标记整理”两个机制。如果一个对象没有被引用,就会立刻被收集。而如果某个对象虽然有引用但却只有循环引用,那么AVM在内存不足的时候会遍历引用关系,把这些只有循环引用的对象也收集掉。
因此在ActionScript中,如果能保证某个持有昂贵资源的对象不被循环引用,那么完全可以不调用其close方法,不必手动释放。
甚至用ActionScirpt编写AIR应用时使用数据库都不必手动释放。
2008年08月04日 星期一 21:27
刚才阅读了一下Lua实现闭包的部分源码。发现闭包引用的局部变量是一个垃圾收集的结构UpVal. UpVal原本指向栈上的局部变量,闭包中的代码访问这个闭包变量实际上是访问栈上的局部变量。在外层函数退出以后,局部变量的值被复制到UpVal结构中,此后闭包中的代码访问这个闭包变量时,变量值就会取自UpVal结构本身,因为栈上的变量在函数退出以后已经没了。
顺便提一下Java的闭包,Java的闭包要求闭包变量必须是final, 由此可见Java实现时并不会像Lua那样将栈上的值复制到动态分配的内存(也就是会被垃圾收集的内存)中,而是直接把那个变量复制到闭包的栈上。
2008年05月08日 星期四 08:58
将几处相似的代码抽出来定义一个函数/类/模板之类的办法当然可以重用代码。但是重用代码不见得就是好事,只有相似代码确实有相同的流程并且有明确的意义时才应该重用。
如果需要根据输入值进行判断来决定处理流程,这就不是“相同的流程”。
如果你说不上你的函数是做什么的,只不过刚好有好几处代码都是这么写的,你就抽了个函数出来,这就不是“明确的意义”。
“相同的流程”和“明确的意义”就是判断代码重用是否必要的判断标准,如果不符合这个标准,就应该复制+粘贴。
有 一个理由是说抽出相同代码以后,如果要修改代码就只用改一处而不用到处改。这个理由很愚蠢,你怎么知道今后需要修改时,改的是多处使用的共同部分,而不是 某处使用时的特殊逻辑呢?要知道,随着代码变得复杂,特殊逻辑总是会不断增加,而原本相似的业务逻辑中的共同部分的比例则越来越小。与其在一个函数写一大堆判断、 switch,还不如当初就不要把它写成一个函数。
2008年04月17日 星期四 00:02
pop_atry
男, 23岁
上次登录: 20天前
加为好友