<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">博客园_1-2-3.cnblogs.com</title><subtitle type="text">Think different</subtitle><id>http://feed.cnblogs.com/blog/u/28843/rss</id><updated>2012-05-21T01:42:55Z</updated><author><name>1-2-3</name><uri>http://www.cnblogs.com/1-2-3/</uri></author><generator>feed.cnblogs.com</generator><link rel="alternate" type="text/html" href="http://www.cnblogs.com/1-2-3/"/><link rel="self" type="application/atom+xml" href="http://feed.cnblogs.com/blog/u/28843/rss"/><entry><id>http://www.cnblogs.com/1-2-3/archive/2012/05/21/ext_extend_source.html</id><title type="text">[javascript]图解+注释版 Ext.extend()</title><summary type="text">Ext.extend() 体现了程序员非凡的制造轮子的能力。基于 javascript 古老的对象模型，最大程度地模拟出现代面向对象语言的类型继承的语意。为了达到更好的可读性，我更改了部分变量名称，加上了详细的注释。</summary><published>2012-05-21T01:40:00Z</published><updated>2012-05-21T01:40:00Z</updated><author><name>1-2-3</name><uri>http://www.cnblogs.com/1-2-3/</uri></author><link rel="alternate" href="http://www.cnblogs.com/1-2-3/archive/2012/05/21/ext_extend_source.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/1-2-3/archive/2012/05/21/ext_extend_source.html"/><content type="html">&lt;p&gt;Ext.extend() 体现了程序员非凡的制造轮子的能力。它基于 javascript 古老的对象模型，最大程度地模拟出现代面向对象语言的类型继承的语意。但是在程序界有太多的&amp;ldquo;与XXX很像&amp;rdquo;，但是实际上又有很多差别。要想最彻底、最精确地理解 Ext.extend()，最直接（往往也是最有效）的方法就是去读它的源代码。为了达到更好的可读性，我更改了部分变量名称，加上了详细的注释。&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;&lt;pre&gt;&lt;span style="color: #008080;"&gt;  1&lt;/span&gt; &lt;span style="color: #008000;"&gt;/*&lt;/span&gt;&lt;span style="color: #008000;"&gt;*&lt;/span&gt;&lt;span style="color: #008080;"&gt;  2&lt;/span&gt; &lt;span style="color: #008000;"&gt;         * &amp;lt;p&amp;gt;Extends one class to create a subclass and optionally overrides members with the passed literal. This method&lt;/span&gt;&lt;span style="color: #008080;"&gt;  3&lt;/span&gt; &lt;span style="color: #008000;"&gt;         * also adds the function "override()" to the subclass that can be used to override members of the class.&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;  4&lt;/span&gt; &lt;span style="color: #008000;"&gt;         * For example, to create a subclass of Ext GridPanel:&lt;/span&gt;&lt;span style="color: #008080;"&gt;  5&lt;/span&gt; &lt;span style="color: #008000;"&gt;         * &amp;lt;pre&amp;gt;&amp;lt;code&amp;gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;  6&lt;/span&gt; &lt;span style="color: #008000;"&gt;MyGridPanel = Ext.extend(Ext.grid.GridPanel, {&lt;/span&gt;&lt;span style="color: #008080;"&gt;  7&lt;/span&gt; &lt;span style="color: #008000;"&gt;    constructor: function(config) {&lt;/span&gt;&lt;span style="color: #008080;"&gt;  8&lt;/span&gt; &lt;span style="color: #008080;"&gt;  9&lt;/span&gt; &lt;span style="color: #008000;"&gt;//      Create configuration for this Grid.&lt;/span&gt;&lt;span style="color: #008080;"&gt; 10&lt;/span&gt; &lt;span style="color: #008000;"&gt;        var store = new Ext.data.Store({...});&lt;/span&gt;&lt;span style="color: #008080;"&gt; 11&lt;/span&gt; &lt;span style="color: #008000;"&gt;        var colModel = new Ext.grid.ColumnModel({...});&lt;/span&gt;&lt;span style="color: #008080;"&gt; 12&lt;/span&gt; &lt;span style="color: #008080;"&gt; 13&lt;/span&gt; &lt;span style="color: #008000;"&gt;//      Create a new config object containing our computed properties&lt;/span&gt;&lt;span style="color: #008080;"&gt; 14&lt;/span&gt; &lt;span style="color: #008000;"&gt;//      *plus* whatever was in the config parameter.&lt;/span&gt;&lt;span style="color: #008080;"&gt; 15&lt;/span&gt; &lt;span style="color: #008000;"&gt;        config = Ext.apply({&lt;/span&gt;&lt;span style="color: #008080;"&gt; 16&lt;/span&gt; &lt;span style="color: #008000;"&gt;            store: store,&lt;/span&gt;&lt;span style="color: #008080;"&gt; 17&lt;/span&gt; &lt;span style="color: #008000;"&gt;            colModel: colModel&lt;/span&gt;&lt;span style="color: #008080;"&gt; 18&lt;/span&gt; &lt;span style="color: #008000;"&gt;        }, config);&lt;/span&gt;&lt;span style="color: #008080;"&gt; 19&lt;/span&gt; &lt;span style="color: #008080;"&gt; 20&lt;/span&gt; &lt;span style="color: #008000;"&gt;        MyGridPanel.superclass.constructor.call(this, config);&lt;/span&gt;&lt;span style="color: #008080;"&gt; 21&lt;/span&gt; &lt;span style="color: #008080;"&gt; 22&lt;/span&gt; &lt;span style="color: #008000;"&gt;//      Your postprocessing here&lt;/span&gt;&lt;span style="color: #008080;"&gt; 23&lt;/span&gt; &lt;span style="color: #008000;"&gt;    },&lt;/span&gt;&lt;span style="color: #008080;"&gt; 24&lt;/span&gt; &lt;span style="color: #008080;"&gt; 25&lt;/span&gt; &lt;span style="color: #008000;"&gt;    yourMethod: function() {&lt;/span&gt;&lt;span style="color: #008080;"&gt; 26&lt;/span&gt; &lt;span style="color: #008000;"&gt;        // etc.&lt;/span&gt;&lt;span style="color: #008080;"&gt; 27&lt;/span&gt; &lt;span style="color: #008000;"&gt;    }&lt;/span&gt;&lt;span style="color: #008080;"&gt; 28&lt;/span&gt; &lt;span style="color: #008000;"&gt;});&lt;/span&gt;&lt;span style="color: #008080;"&gt; 29&lt;/span&gt; &lt;span style="color: #008000;"&gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt; 30&lt;/span&gt; &lt;span style="color: #008000;"&gt; *&lt;/span&gt;&lt;span style="color: #008080;"&gt; 31&lt;/span&gt; &lt;span style="color: #008000;"&gt; * &amp;lt;p&amp;gt;This function also supports a 3-argument call in which the subclass's constructor is&lt;/span&gt;&lt;span style="color: #008080;"&gt; 32&lt;/span&gt; &lt;span style="color: #008000;"&gt; * passed as an argument. In this form, the parameters are as follows:&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt; 33&lt;/span&gt; &lt;span style="color: #008000;"&gt; * &amp;lt;div class="mdetail-params"&amp;gt;&amp;lt;ul&amp;gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt; 34&lt;/span&gt; &lt;span style="color: #008000;"&gt; * &amp;lt;li&amp;gt;&amp;lt;code&amp;gt;subclass&amp;lt;/code&amp;gt; : Function &amp;lt;div class="sub-desc"&amp;gt;The subclass constructor.&amp;lt;/div&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt; 35&lt;/span&gt; &lt;span style="color: #008000;"&gt; * &amp;lt;li&amp;gt;&amp;lt;code&amp;gt;superclass&amp;lt;/code&amp;gt; : Function &amp;lt;div class="sub-desc"&amp;gt;The constructor of class being extended&amp;lt;/div&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt; 36&lt;/span&gt; &lt;span style="color: #008000;"&gt; * &amp;lt;li&amp;gt;&amp;lt;code&amp;gt;overrides&amp;lt;/code&amp;gt; : Object &amp;lt;div class="sub-desc"&amp;gt;A literal with members which are copied into the subclass's&lt;/span&gt;&lt;span style="color: #008080;"&gt; 37&lt;/span&gt; &lt;span style="color: #008000;"&gt; * prototype, and are therefore shared among all instances of the new class.&amp;lt;/div&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt; 38&lt;/span&gt; &lt;span style="color: #008000;"&gt; * &amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt; 39&lt;/span&gt; &lt;span style="color: #008000;"&gt; *&lt;/span&gt;&lt;span style="color: #008080;"&gt; 40&lt;/span&gt; &lt;span style="color: #008000;"&gt; * @param {Function} superclass The constructor of class being extended.&lt;/span&gt;&lt;span style="color: #008080;"&gt; 41&lt;/span&gt; &lt;span style="color: #008000;"&gt; * @param {Object} overrides &amp;lt;p&amp;gt;A literal with members which are copied into the subclass's&lt;/span&gt;&lt;span style="color: #008080;"&gt; 42&lt;/span&gt; &lt;span style="color: #008000;"&gt; * prototype, and are therefore shared between all instances of the new class.&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt; 43&lt;/span&gt; &lt;span style="color: #008000;"&gt; * &amp;lt;p&amp;gt;This may contain a special member named &amp;lt;tt&amp;gt;&amp;lt;b&amp;gt;constructor&amp;lt;/b&amp;gt;&amp;lt;/tt&amp;gt;. This is used&lt;/span&gt;&lt;span style="color: #008080;"&gt; 44&lt;/span&gt; &lt;span style="color: #008000;"&gt; * to define the constructor of the new class, and is returned. If this property is&lt;/span&gt;&lt;span style="color: #008080;"&gt; 45&lt;/span&gt; &lt;span style="color: #008000;"&gt; * &amp;lt;i&amp;gt;not&amp;lt;/i&amp;gt; specified, a constructor is generated and returned which just calls the&lt;/span&gt;&lt;span style="color: #008080;"&gt; 46&lt;/span&gt; &lt;span style="color: #008000;"&gt; * superclass's constructor passing on its parameters.&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt; 47&lt;/span&gt; &lt;span style="color: #008000;"&gt; * &amp;lt;p&amp;gt;&amp;lt;b&amp;gt;It is essential that you call the superclass constructor in any provided constructor. See example code.&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt; 48&lt;/span&gt; &lt;span style="color: #008000;"&gt; * @return {Function} The subclass constructor from the &amp;lt;code&amp;gt;overrides&amp;lt;/code&amp;gt; parameter, or a generated one if not provided.&lt;/span&gt;&lt;span style="color: #008080;"&gt; 49&lt;/span&gt;  &lt;span style="color: #008000;"&gt;*/&lt;/span&gt;&lt;span style="color: #008080;"&gt; 50&lt;/span&gt; Ext.extend = &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt;&lt;span style="color: #000000;"&gt;(){&lt;/span&gt;&lt;span style="color: #008080;"&gt; 51&lt;/span&gt;     &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; inline overrides&lt;/span&gt;&lt;span style="color: #008080;"&gt; 52&lt;/span&gt;     &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; io = &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt;&lt;span style="color: #000000;"&gt;(o){&lt;/span&gt;&lt;span style="color: #008080;"&gt; 53&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;for&lt;/span&gt;(&lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; m &lt;span style="color: #0000ff;"&gt;in&lt;/span&gt;&lt;span style="color: #000000;"&gt; o){&lt;/span&gt;&lt;span style="color: #008080;"&gt; 54&lt;/span&gt;             &lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;[m] =&lt;span style="color: #000000;"&gt; o[m];&lt;/span&gt;&lt;span style="color: #008080;"&gt; 55&lt;/span&gt; &lt;span style="color: #000000;"&gt;        }&lt;/span&gt;&lt;span style="color: #008080;"&gt; 56&lt;/span&gt; &lt;span style="color: #000000;"&gt;    };&lt;/span&gt;&lt;span style="color: #008080;"&gt; 57&lt;/span&gt;     &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; oc = Object.prototype.constructor; &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 如果一个对象的 constructor == oc，说明它是使用类似 { age:22 } 这种语法创建的字面量对象，&lt;/span&gt;&lt;span style="color: #008080;"&gt; 58&lt;/span&gt;                                            &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 而且没有对constructor属性赋值&lt;/span&gt;&lt;span style="color: #008080;"&gt; 59&lt;/span&gt; &lt;span style="color: #008080;"&gt; 60&lt;/span&gt;     &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt;&lt;span style="color: #000000;"&gt;(subClass, superClass, overrides){&lt;/span&gt;&lt;span style="color: #008080;"&gt; 61&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;if&lt;/span&gt;(&lt;span style="color: #0000ff;"&gt;typeof&lt;/span&gt; superClass == 'object'&lt;span style="color: #000000;"&gt;){&lt;/span&gt;&lt;span style="color: #008080;"&gt; 62&lt;/span&gt;             &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 如果 superClass 是对象而不是构造函数，就说明是使用的是&lt;/span&gt;&lt;span style="color: #008080;"&gt; 63&lt;/span&gt;             &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; var Cat = Ext.extend(Animal, {&lt;/span&gt;&lt;span style="color: #008080;"&gt; 64&lt;/span&gt;             &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;     say : function() {&lt;/span&gt;&lt;span style="color: #008080;"&gt; 65&lt;/span&gt;             &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;         document.writeln("I'm a cat name " + this.name);&lt;/span&gt;&lt;span style="color: #008080;"&gt; 66&lt;/span&gt;             &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;     }&lt;/span&gt;&lt;span style="color: #008080;"&gt; 67&lt;/span&gt;             &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; });&lt;/span&gt;&lt;span style="color: #008080;"&gt; 68&lt;/span&gt;             &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 这种方式调用的。也就是说返回值是subClass, subClass 参数其实是 superClass，superClass参数其实是overrides，overrides参数应该被忽略&lt;/span&gt;&lt;span style="color: #008080;"&gt; 69&lt;/span&gt;             overrides = superClass; &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 忽略 overrides 参数&lt;/span&gt;&lt;span style="color: #008080;"&gt; 70&lt;/span&gt;             superClass = subClass; &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; subClass 参数其实是 superClass&lt;/span&gt;&lt;span style="color: #008080;"&gt; 71&lt;/span&gt;             &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; subClass 参数将作为将来的返回值。&lt;/span&gt;&lt;span style="color: #008080;"&gt; 72&lt;/span&gt;             &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 如果 overrides 对象没有自定义构造函数，为其定义一个，并且里面调用父类构造函数；&lt;/span&gt;&lt;span style="color: #008080;"&gt; 73&lt;/span&gt;             &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 如果 overrides 对象含有自定义构造函数，把overrides的构造函数赋给子类&lt;/span&gt;&lt;span style="color: #008080;"&gt; 74&lt;/span&gt;             subClass = overrides.constructor != oc ? overrides.constructor : &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt;&lt;span style="color: #000000;"&gt;(){ &lt;/span&gt;&lt;span style="color: #008080;"&gt; 75&lt;/span&gt;                     superClass.apply(&lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;, arguments); &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 调用父类构造函数（前提是子类构造函数的参数可以比父类少，但是顺序要一致）&lt;/span&gt;&lt;span style="color: #008080;"&gt; 76&lt;/span&gt; &lt;span style="color: #000000;"&gt;                };&lt;/span&gt;&lt;span style="color: #008080;"&gt; 77&lt;/span&gt; &lt;span style="color: #000000;"&gt;        }&lt;/span&gt;&lt;span style="color: #008080;"&gt; 78&lt;/span&gt;         &lt;span style="color: #008080;"&gt; 79&lt;/span&gt;         &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 原型式继承。之所以创建一个临时构造函数F，而不是令 subClass.prototype = new SuperClass，&lt;/span&gt;&lt;span style="color: #008080;"&gt; 80&lt;/span&gt;         &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 是为了更改子类的prototype的时候不会影响到父类的prototype&lt;/span&gt;&lt;span style="color: #008080;"&gt; 81&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; F = &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt;&lt;span style="color: #000000;"&gt;(){},&lt;/span&gt;&lt;span style="color: #008080;"&gt; 82&lt;/span&gt;             subPrototype, &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 子类构造函数的 Prototype&lt;/span&gt;&lt;span style="color: #008080;"&gt; 83&lt;/span&gt;             superPrototype = superClass.prototype; &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 父类构造函数的 Prototype &lt;/span&gt;&lt;span style="color: #008080;"&gt; 84&lt;/span&gt; &lt;span style="color: #008080;"&gt; 85&lt;/span&gt;         F.prototype =&lt;span style="color: #000000;"&gt; superPrototype;&lt;/span&gt;&lt;span style="color: #008080;"&gt; 86&lt;/span&gt;         subPrototype = subClass.prototype = &lt;span style="color: #0000ff;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; F();&lt;/span&gt;&lt;span style="color: #008080;"&gt; 87&lt;/span&gt;         subPrototype.constructor=&lt;span style="color: #000000;"&gt;subClass;&lt;/span&gt;&lt;span style="color: #008080;"&gt; 88&lt;/span&gt;         &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 只所以没写成 subClass.superclass=superClass，是为了在overrides对象的constructor方法里&lt;/span&gt;&lt;span style="color: #008080;"&gt; 89&lt;/span&gt;         &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 可以使用诸如 &amp;ldquo;MyGridPanel.superclass.constructor.call(this, config)&amp;rdquo;这种（读起来比较&lt;/span&gt;&lt;span style="color: #008080;"&gt; 90&lt;/span&gt;         &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 自然的）写法调用父类构造函数。&lt;/span&gt;&lt;span style="color: #008080;"&gt; 91&lt;/span&gt;         subClass.superclass=&lt;span style="color: #000000;"&gt;superPrototype;&lt;/span&gt;&lt;span style="color: #008080;"&gt; 92&lt;/span&gt;         &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 如果 superclass.prototype 是字面量对象，确保 superclass.prototype。constructor 指向 superClass&lt;/span&gt;&lt;span style="color: #008080;"&gt; 93&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;if&lt;/span&gt;(superPrototype.constructor ==&lt;span style="color: #000000;"&gt; oc){&lt;/span&gt;&lt;span style="color: #008080;"&gt; 94&lt;/span&gt;             superPrototype.constructor=&lt;span style="color: #000000;"&gt;superClass;&lt;/span&gt;&lt;span style="color: #008080;"&gt; 95&lt;/span&gt; &lt;span style="color: #000000;"&gt;        }&lt;/span&gt;&lt;span style="color: #008080;"&gt; 96&lt;/span&gt;         &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 为子类增加一个override()方法。调用　subClass.override(o) 等价于调用 Ext.override(subClass, o)&lt;/span&gt;&lt;span style="color: #008080;"&gt; 97&lt;/span&gt;         subClass.override = &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt;&lt;span style="color: #000000;"&gt;(o){&lt;/span&gt;&lt;span style="color: #008080;"&gt; 98&lt;/span&gt; &lt;span style="color: #000000;"&gt;            Ext.override(subClass, o);&lt;/span&gt;&lt;span style="color: #008080;"&gt; 99&lt;/span&gt; &lt;span style="color: #000000;"&gt;        };&lt;/span&gt;&lt;span style="color: #008080;"&gt;100&lt;/span&gt;         &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 增加一个名为 superclass() 的实例方法，这样在overrides对象的constructor方法里&lt;/span&gt;&lt;span style="color: #008080;"&gt;101&lt;/span&gt;         &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 就可以使用诸如 &amp;ldquo;this.superclass().constructor.call(this, config)&amp;rdquo;来调用父类&lt;/span&gt;&lt;span style="color: #008080;"&gt;102&lt;/span&gt;         &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 构造函数，而且没有依赖子类构造函数的名称。&lt;/span&gt;&lt;span style="color: #008080;"&gt;103&lt;/span&gt;         subPrototype.superclass = subPrototype.supr = (&lt;span style="color: #0000ff;"&gt;function&lt;/span&gt;&lt;span style="color: #000000;"&gt;(){&lt;/span&gt;&lt;span style="color: #008080;"&gt;104&lt;/span&gt;             &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt;&lt;span style="color: #000000;"&gt; superPrototype;&lt;/span&gt;&lt;span style="color: #008080;"&gt;105&lt;/span&gt; &lt;span style="color: #000000;"&gt;        });&lt;/span&gt;&lt;span style="color: #008080;"&gt;106&lt;/span&gt;         &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 为子类增加一个实例方法: override()&lt;/span&gt;&lt;span style="color: #008080;"&gt;107&lt;/span&gt;         subPrototype.override =&lt;span style="color: #000000;"&gt; io;&lt;/span&gt;&lt;span style="color: #008080;"&gt;108&lt;/span&gt;         &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 将 overrides 对象里的方法复写到 subClass.prototype 中&lt;/span&gt;&lt;span style="color: #008080;"&gt;109&lt;/span&gt; &lt;span style="color: #000000;"&gt;        Ext.override(subClass, overrides);&lt;/span&gt;&lt;span style="color: #008080;"&gt;110&lt;/span&gt;         &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 为子类增加一个extend()方法。调用　subClass.extend(o); 等价于调用 Ext.extend(subClass, o);&lt;/span&gt;&lt;span style="color: #008080;"&gt;111&lt;/span&gt;         subClass.extend = &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt;(o){&lt;span style="color: #0000ff;"&gt;return&lt;/span&gt;&lt;span style="color: #000000;"&gt; Ext.extend(subClass, o);};&lt;/span&gt;&lt;span style="color: #008080;"&gt;112&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt;&lt;span style="color: #000000;"&gt; subClass;&lt;/span&gt;&lt;span style="color: #008080;"&gt;113&lt;/span&gt; &lt;span style="color: #000000;"&gt;    };&lt;/span&gt;&lt;span style="color: #008080;"&gt;114&lt;/span&gt; }();&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;下面这张对象图则是执行了 var SubClass = Ext.extend(SuperClass, { someprop : 'some' }) 之后的效果。&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/25284/2012052022590411.png" alt="" /&gt;&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/1-2-3/113933/o_logo2_170_r.png" alt="" width="170" height="66" /&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/1-2-3/aggbug/2510966.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/1-2-3/archive/2012/05/21/ext_extend_source.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/1-2-3/archive/2012/04/05/oracle-isolation-level-ora-08177.html</id><title type="text">Oracle 数据库隔离级别，特性，问题和解决方法</title><summary type="text">献给所有被数据库并发操作问题和ORA-08177错误折磨过的兄弟们</summary><published>2012-04-05T01:56:00Z</published><updated>2012-04-05T01:56:00Z</updated><author><name>1-2-3</name><uri>http://www.cnblogs.com/1-2-3/</uri></author><link rel="alternate" href="http://www.cnblogs.com/1-2-3/archive/2012/04/05/oracle-isolation-level-ora-08177.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/1-2-3/archive/2012/04/05/oracle-isolation-level-ora-08177.html"/><content type="html">&lt;p&gt;&amp;ldquo;&amp;hellip;&amp;hellip;老手根本就不会照着指示去做，他边做边取舍，因此必须全神贯注于手上的工作，即使他没有刻意这样做，他的动作和机器之间也自然有一种和谐的感觉。他不需要遵照任何书面的指示，因为手中的机器给他的感觉决定他的思路和动作，同时也影响他手中的工作。所以机器和他的思想同时不断地改变，一直到把事情做好了，他的内心才真正地安宁下来。&amp;rdquo;&lt;br /&gt;&amp;ldquo;听起来好像艺术一样。&amp;rdquo;&lt;br /&gt;&lt;strong&gt;&amp;mdash;&amp;mdash;罗伯特&amp;middot;M.波西格 《禅与摩托车维修艺术》&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;如果没有任何数据库隔离策略，在多用户（多事务）并发时，会产生下列问题：&lt;br /&gt;- &lt;strong&gt;丢失更新（lost update）&lt;/strong&gt;：两个事务同时更新同一条数据时，会发生更新丢失。&lt;br /&gt;例如：用户A读取学号为107的学生（学号=107，姓名=&amp;ldquo;小明&amp;rdquo;，年龄=28） &lt;br /&gt;=&amp;gt; 用户B读取学号为107的学生（学号=107，姓名=&amp;ldquo;小明&amp;rdquo;，年龄=28）&lt;br /&gt;=&amp;gt; 用户A把姓名更改为&amp;ldquo;王小明&amp;rdquo;（学号=107，姓名=&amp;ldquo;&lt;span style="color: #ff0000;"&gt;王小明&lt;/span&gt;&amp;rdquo;，年龄=28）&lt;br /&gt;=&amp;gt; 用户B把年龄更改为33（学号=107，姓名=&amp;ldquo;小明&amp;rdquo;，年龄=&lt;span style="color: #ff0000;"&gt;33&lt;/span&gt;）&lt;br /&gt;=&amp;gt; 用户A提交（学号=107，姓名=&amp;ldquo;&lt;span style="color: #ff0000;"&gt;王小明&lt;/span&gt;&amp;rdquo;，年龄=28）&lt;br /&gt;=&amp;gt; 用户B提交（学号=107，姓名=&amp;ldquo;小明&amp;rdquo;，年龄=&lt;span style="color: #ff0000;"&gt;33&lt;/span&gt;）&lt;br /&gt;用户A对学生姓名的更新丢失了。&lt;br /&gt;- &lt;strong&gt;脏读（dirty read）&lt;/strong&gt;：当一个事务读取另一个事务尚未提交的修改时，产生脏读。&lt;br /&gt;例如：用户A读取学号为107的学生（学号=107，姓名=&amp;ldquo;小明&amp;rdquo;，年龄=28） &lt;br /&gt;=&amp;gt; 用户A把姓名更改为&amp;ldquo;王小明&amp;rdquo;（学号=107，姓名=&amp;ldquo;&lt;span style="color: #ff0000;"&gt;王小明&lt;/span&gt;&amp;rdquo;，年龄=28）&lt;br /&gt;=&amp;gt; 用户B读取学号为107的学生（学号=107，姓名=&amp;ldquo;&lt;span style="color: #ff0000;"&gt;王小明&lt;/span&gt;&amp;rdquo;，年龄=28）&lt;br /&gt;=&amp;gt; 用户A撤销更改，事务回滚（学号=107，姓名=&amp;ldquo;小明&amp;rdquo;，年龄=28）&lt;br /&gt;这样用户B相当于读取了一个从未存在过的数据&amp;ldquo;王小明&amp;rdquo;。如果涉及到金额的话问题更为严重，因为用户B读取了一个金额之后，很可能把这个金额与其它金额累加，再把结果保存到汇总数据之中，这样在月底对不上账的时候，由于用户A回滚了事务，数据库内不会有任何操作记录，这样用户B是何时、从哪里读取了错误数据根本无从查起。&lt;br /&gt;&lt;strong&gt;- 不可重现的读取（nonrepeatable read）&lt;/strong&gt;：同一查询在同一事务中多次进行，在此期间，由于其他事务提交了对数据的修改或删除，每次返回不同的结果。&lt;br /&gt;例如：假设学生表里只有&amp;ldquo;小明&amp;rdquo;和&amp;ldquo;小丽&amp;rdquo;2条记录（小明.年龄=20，小丽.年龄=30）。如果用户A把&amp;ldquo;小明.年龄&amp;rdquo;更改为40、把&amp;ldquo;小丽.年龄&amp;rdquo;更改为50并提交事务。在用户A修改数据并提交前，学生的平均年龄为(20+30)/2=&lt;span style="color: #008000;"&gt;25&lt;/span&gt;；在用户A修改数据并提交后，学生的平均年龄为(40+50)/2=&lt;span style="color: #008000;"&gt;45&lt;span style="color: #000000;"&gt;。&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;现在考虑在用户A更新 2 名学生的年龄时，用户B执行了一个计算平均年龄的事务：&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/25284/2012032911180832.png" alt="" /&gt;&lt;/p&gt;&lt;p&gt;如前所述，在用户A修改数据并提交前，学生的平均年龄为(20+30)/2=&lt;span style="color: #008000;"&gt;25&lt;/span&gt;；在用户A修改数据并提交后，学生的平均年龄为(40+50)/2=&lt;span style="color: #008000;"&gt;45&lt;span style="color: #000000;"&gt;。用户B计算得出的平均年龄是 25 或 45 都是可以接受的，但是在上例中用户B计算得出的平均年龄 &lt;span style="color: #ff0000;"&gt;35&lt;/span&gt; 是从未出现在系统中的错误数值。&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;- &lt;strong&gt;幻读（phantom read）&lt;/strong&gt;：同一查询在同一事务中多次进行，由于其他提交事务所做的插入操作，虽然查询条件相同，每次返回的结果集却不同。&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/25284/2012032914182348.png" alt="" /&gt;&lt;/p&gt;&lt;p&gt;事务B使用相同的条件进行了2次查询/筛选，一次是为了向费用结算表插入汇总数据，一次为了确定对费用明细表的更新范围。在这两次筛选之间，事务A提交了一条新的费用明细数据，导致两次筛选的结果不一致。&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #008000;"&gt;&lt;strong&gt;隔离级别&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;为避免上述并发问题，ANSI/ISO SQL92标准定义了一些隔离级别：&lt;/p&gt;&lt;p&gt;- 读取未提交数据（read uncommitted）&lt;/p&gt;&lt;p&gt;- 读取已提交数据（read committed）&lt;/p&gt;&lt;p&gt;- 可重现的读取（repeatable read）&lt;/p&gt;&lt;p&gt;- 序列化（serializable）&lt;/p&gt;&lt;p&gt;通过指定不同的隔离级别，可避免上述一种或多种并发问题，见下图。&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/25284/2012032914285695.png" alt="" /&gt;&lt;/p&gt;&lt;p&gt;细心的读者可能已经注意到上图不包括&amp;ldquo;丢失更新（lost update）&amp;rdquo;，这是因为&amp;ldquo;丢失更新&amp;rdquo;问题需要使用乐观锁或悲观锁来解决，超出本文范围，先不详述。&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #008000;"&gt;&lt;strong&gt;Oracle 的隔离级别&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;SQL92定义的隔离级别在理论上很完善，但是 Oracle 显然认为在实际实现的时候并不应该完全照搬SQL92的模型。&amp;nbsp;&lt;br /&gt;- Oracle不支持 SQL92 标准中的&amp;ldquo;读取未提交数据（read uncommitted）&amp;rdquo;隔离级别，想要脏读都没可能。&lt;br /&gt;- Oracle 支持 SQL92 标准中的&amp;ldquo;读取已提交数据（read committed）&amp;rdquo;隔离级别，（这也是Oracle默认的隔离级别）。&lt;br /&gt;- Oracle不支持 SQL92 标准中的&amp;ldquo;可重现的读取（repeatable read）&amp;rdquo;隔离级别，要想避免&amp;ldquo;不可重现的读取（nonrepeatable read）&amp;rdquo;可以直接使用&amp;ldquo;序列化（serializable）&amp;rdquo;隔离级别。&lt;br /&gt;- Oracle 支持 SQL92 标准中的&amp;ldquo;序列化（serializable）&amp;rdquo;隔离级别，但是并不真正阻塞事务的执行（这一点在后文还有详述）。&lt;br /&gt;- Oracle 还另外增加了一个非&lt;span lang="EN-US"&gt;SQL92&lt;/span&gt;标准的&amp;ldquo;只读(&lt;span lang="EN-US"&gt;read-only)&amp;rdquo;隔离级别。&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="color: #008000;"&gt;Oracle的序列化（serializable）隔离级别&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;序列化，顾名思义，是让并发的事务感觉上是一个挨一个地串行执行的。之所以说是&amp;ldquo;感觉上&amp;rdquo;，是因为当2个事务并发时，Oracle并不会阻塞其中一个事务去等待另一个事务执行完毕再执行，而是仍然让2个事务同时并行，那么如何能&amp;ldquo;感觉&amp;rdquo;是串行的呢？请看下图的实验。&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/25284/2012033118270476.png" alt="" /&gt;&lt;/p&gt;&lt;p&gt;用户B的事务因为指定了serializable隔离级别，所以虽然在查询费用明细表之前，用户A提交了对费用明细表的更改，但是因为用户A提交的更改是在用户B的事务开始之后才提交的，所以这个更改对用户B的事务不可见。也就是说，用户B的事务开始之后，其它事务提交的更改都不会再影响事务内的查询结果，这样感觉上用户A的事务好像是在用户B的事务结束之后才执行的似的。这本来是非常好的一个特性，极大地提高了并行性，但是也会造成问题。&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;问题1：Oracle的这种&amp;ldquo;假串行&amp;rdquo;会让严格依赖于时间的程序产生混乱。&lt;br /&gt;&lt;br /&gt;&lt;/strong&gt;请看下图这个例子，对费用结算的例子稍稍做了一点改动。&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/25284/2012033023283573.png" alt="" /&gt;&lt;/p&gt;&lt;p&gt;程序员的本意是统计2012-3-4这天从零点至运行程序之时的费用总额。如果他以为 Oracle 的 serializable&amp;nbsp;会像 C# 的 lock 一样阻塞其它事务的话，就会对结果非常吃惊：在2012-3-4 0:00 ~ 2012-3-4 10:02 实际有3条费用明细，总额为20+30+100=150，而不是用户B的事务统计得出的50。&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;问题2：ORA-08177 Can't serialize access for this transaction (无法序列化访问)错误。&lt;br /&gt;&lt;/strong&gt;&lt;br /&gt;如果你使用了 serialize 隔离级别，没准你的客户会经常抱怨这个随机出现的错误。兄弟，你并不孤独！&lt;br /&gt;导致这个错误的原因有2个：&lt;br /&gt;(1) 两个事务同时更新了同一条数据。你可以这样重现这个错误：事务B开始（使用serialize 隔离级别) =&amp;gt; 事务A开始，更新 表1.RowA&amp;nbsp;但不提交&amp;nbsp;=&amp;gt; 事务B更新表1.RowA，因为行锁定而被阻塞 =&amp;gt; 事务A提交 =&amp;gt; 事务B报 ORA-08177 错误。&lt;br /&gt;(2) 事务所更新的表的 initrans 参数太小。Oracle 官方文档的说法是，如果使用了 serialize 隔离级别，表的 initrans 参数最小要设置成3（默认是1）。&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;&lt;pre&gt;&lt;span style="color: #0000ff;"&gt;alter&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;table&lt;/span&gt; 费用明细表 initrans &lt;span style="color: #800000; font-weight: bold;"&gt;3&lt;/span&gt;;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;原文：&amp;ldquo;Oracle Database stores control information in each data block to manage access by concurrent transactions. Therefore, if you set the transaction isolation level to SERIALIZABLE, then you must use the ALTER TABLE command to set INITRANS to at least 3. This parameter causes Oracle Database to allocate sufficient storage in each block to record the history of recent transactions that accessed the block. Higher values should be used for tables that will undergo many transactions updating the same blocks.&amp;rdquo;&lt;br /&gt;注意，人家说的是&amp;ldquo;最小是3&amp;rdquo;。我用自己笔记本里的 32 位 Oracle10g 测试的结果是设置成 3 也会频繁地报 ORA-08177 错误。后来改成5 和 10，都不行。改成50，终于不报错了。但是都说了这个错误是随机的，有时候3也没问题的&amp;mdash;&amp;mdash;反过来说，设置成50也未必保险。坑爹啊！真坑爹！！这就像菜谱里面写的&amp;ldquo;放入适量的油&amp;hellip;&amp;hellip;&amp;rdquo;，他喵的到底多少算是&amp;ldquo;适量&amp;rdquo;啊？！！！&lt;br /&gt;有兴趣的读者可以使用下图的语句实际测试一下。&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/25284/2012033123224033.png" alt="" /&gt;&lt;/p&gt;&lt;p&gt;我的建议是，还是尽量不要用 serialize 隔离级别吧，用户是不会理解什么叫&amp;ldquo;无法序列化访问&amp;rdquo;的，他只会觉得你的&amp;ldquo;XX功能会随机地不好用&amp;rdquo;倒是真的。稍后我们再简单讨论一下不用 serialize 隔离级别如何避免幻读。现在先来看一下 Oracle 官方文档建议的适合使用 serialize 隔离级别的3种情况。&lt;/p&gt;&lt;p&gt;(1) With large databases and short transactions that update only a fewrows（大数据库、只更新几条数据的短事务）&lt;/p&gt;&lt;p&gt;(2) Where the chance that two concurrent transactions will modify thesame rows is relatively low（2个并发事务更新同一条数据的几率不大）&lt;/p&gt;&lt;p&gt;(3) Where relatively long-running transactions are primarily read only（相对运行时间较长的事务主要用来读取数据）&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;span style="color: #008000;"&gt;&lt;strong&gt;使用默认的 read committed 隔离级别，如何避免幻读产生的问题&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;使用默认的 read committed 隔离级别，如何编写程序才能避免幻读产生的问题呢？首先，无论是&amp;ldquo;不可重现的读取（nonrepeatable read）&amp;rdquo;还是&amp;ldquo;幻读（phantom read）&amp;rdquo;，都是因为程序反复读取数据产生的。所以首先需要做的是，在一个事务里确保只读取数据一次。最好用C#而不是存储过程实现业务逻辑，这样很容易做到只读取一次，然后把结果存放到IList或IDictionary里。比较难办的是需要更新数据的情况。回顾一下前面所举的幻读的例子。&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/25284/2012040121360929.png" alt="" /&gt;&lt;/p&gt;&lt;p&gt;事务B使用相同的条件进行了2次查询/筛选，一次是为了向费用结算表插入汇总数据，一次为了确定对费用明细表的更新范围。在这两次筛选之间，事务A提交了一条新的费用明细数据，导致两次筛选的结果不一致。要避免这个问题，还是要贯彻&amp;ldquo;只读取一次&amp;rdquo;的原则，或者更广义地说，是&amp;ldquo;只确定一次筛选范围&amp;rdquo;。大致有2种方法。&lt;br /&gt;&lt;strong&gt;&amp;lt;法一&amp;gt;&lt;/strong&gt; 可以先把符合条件的费用明细读取出来保存到一个列表里，然后无论统计还是更新，都局限于这个列表里的数据。下面的C#代码与上图的功能相同，但是没有幻读的问题。&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;&lt;pre&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 用户B的事务开始&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;IList&amp;lt;费用明细&amp;gt; chargeList = 费用明细Repository.获取未结算列表();&lt;br /&gt;费用结算 balance = &lt;span style="color: #0000ff;"&gt;new&lt;/span&gt; 费用结算 &lt;br /&gt;{&lt;br /&gt;    总金额 = chargeList.Sum(t =&amp;gt; t.金额),&lt;br /&gt;    结算编号 = &lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;J122&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;br /&gt;};&lt;br /&gt;费用结算Repository.Save(balance);&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 这时候用户A提交了一条新的费用明细，不过没关系&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #0000ff;"&gt;foreach&lt;/span&gt;(费用明细 charge &lt;span style="color: #0000ff;"&gt;in&lt;/span&gt; chargeList)&lt;br /&gt;{&lt;br /&gt;    charge.是否已结算 = &lt;span style="color: #800080;"&gt;1&lt;/span&gt;;&lt;br /&gt;    charge.结算编号 = &lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;J122&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;;&lt;br /&gt;    费用明细Repository.Update(charge);&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个方法的缺点是要对 chargeList 里的每个实体 Update 一次，如果数据量较大可能会有性能问题。这时候可以用&amp;lt;法二&amp;gt;。&lt;br /&gt;&lt;strong&gt;注&lt;/strong&gt; 本文为了表述的方便使用了中文和英文混杂的代码，实际编程的时候不要这样做。&lt;br /&gt;&lt;strong&gt;&amp;lt;法二&amp;gt;&lt;/strong&gt; 使用事务B独有的方法标识出操作数据的范围。&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/25284/2012040211094776.png" alt="" /&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;注&lt;/strong&gt; 虽然上图是用SQL语句来演示的，使用C#（实体+ORM）同样可以用这种方法。&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;严格依赖时间的程序&lt;br /&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/25284/2012040117255554.png" alt="" /&gt;&lt;/p&gt;&lt;p&gt;严格来说这并不是幻读造成的问题&amp;mdash;&amp;mdash;事务A还没提交呢。这种设计十分危险，无论使用 read committed 还是 serializable 隔离级别都不足以避免并发造成的不一致，应该尽量避免这样的设计。依赖时间很危险，因为系统时间是随时可能被系统管理员更改的，更别提有些国家和地区会实行夏时制，想想看，事务B提交了之后，系统时间被回拨了1小时！&lt;br /&gt;然而世事往往不尽如人意，你可能不幸遇到了这样一个遗留系统，或者用户有很多其它的业务或与你交互的系统严格依赖于时间而逼得你不得不这么做的时候，该怎么办呢？&lt;br /&gt;&lt;strong&gt;&amp;lt;法一&amp;gt;&lt;/strong&gt; 在业务逻辑层面，可以把用户B和用户A的两个方法使用C#提供的&lt;a href="http://www.cnblogs.com/1-2-3/archive/2008/05/26/colloquialism-thread-synchronization-part1.html" target="_blank"&gt;线程同步&lt;/a&gt;技术串行化&amp;mdash;&amp;mdash;理论上行的通，但是操作费用明细实体的方法那么多，很容易有所遗漏。&lt;br /&gt;&lt;strong&gt;&amp;lt;法二&amp;gt;&lt;/strong&gt; 在Repository层面，为费用明细实体设置一个令牌，并且可以设置是否进入令牌模式。在令牌模式下，费用明细Repository里面的所有持久化操作都必须拿到令牌才能操作，拿不到令牌直接抛异常。平时的业务操作都在非令牌模式下工作。在用户B想要进行结算操作时，事务开始之后，马上设置成令牌模式，然后获取令牌，这样就能确保此时只有用户B才能操作费用明细表了。此法虽然并发性很差，但是既简单又保险。而且很多时候像结算这样的操作一个月（或一天）只进行一次，并发性差一些也可以忍受。值得注意的是下面这种情况。&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/25284/2012040411383983.png" alt="" /&gt;&lt;/p&gt;&lt;p&gt;虽然发生的概率不高，但是让令牌法彻底失效了。综合考虑系统时间被管理员改变的可能性，仅仅在结算事务里独占令牌也是不够的，还必须在费用明细Repository.Save()方法里验证费用明细.创建时间必须大于最近一次的结算时间。&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/1-2-3/113933/o_logo2_170_r.png" alt="" width="170" height="66" /&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/1-2-3/aggbug/2421550.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/1-2-3/archive/2012/04/05/oracle-isolation-level-ora-08177.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/1-2-3/archive/2012/03/08/ski-vidio.html</id><title type="text">[双板滑雪]教学视频和技术贴精选</title><summary type="text">双板滑雪教学视频和技术贴精选(持续更新...）</summary><published>2012-03-08T01:43:00Z</published><updated>2012-03-08T01:43:00Z</updated><author><name>1-2-3</name><uri>http://www.cnblogs.com/1-2-3/</uri></author><link rel="alternate" href="http://www.cnblogs.com/1-2-3/archive/2012/03/08/ski-vidio.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/1-2-3/archive/2012/03/08/ski-vidio.html"/><content type="html">&lt;p&gt;&lt;strong&gt;&lt;span style="color: #008000;"&gt;教学视频&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;1. &lt;em&gt;央视教学片&lt;/em&gt;&lt;/strong&gt;&lt;a href="http://www.youku.com/playlist_show/id_1526075_ascending_1_mode_pic_page_1.html"&gt;http://www.youku.com/playlist_show/id_1526075_ascending_1_mode_pic_page_1.html&lt;/a&gt;&lt;br /&gt;共21集，讲解非常详尽，但是从另一方面讲，它太长了，掺杂了各种花里胡哨的东西，其实捞干的4集足够，需要些耐心才能看完。从犁式到半犁式再到平行式，打好基础，循序渐进。缺点是没讲刻滑，对大回转和小回转也是一带而过。另外想要吐槽的一点是里面的练习花样繁多，但多是摆出姿态让你模仿，很少讲背后的原理和发力的方法。这就像只把打狗棍法的招式摆给你看，却不教你心法。这是双板教学片的通病，也是学习滑雪的难点之一。因为滑雪的发力动作短暂（有的不到半秒钟）而且发力动作幅度很小，往往隐藏在教练厚厚的雪服里面，不特意指给你看你根本注意不到。所以当我们想要模仿教练的动作的时候，就会发现不是怎么也做不出来，就是做到了8、9分像却总是差那么一点点，怎么也搞不明白差在哪。曾经看过一个日本的单板教学片，里面的教练会特地在室内把雪鞋脱掉，把雪裤挽到膝盖以上，然后一边在一个小窗格里慢放雪上的动作，一边在室内讲解脚部和全身是如何发力和协调的，这样的双板教学片至今还没遇到。所以童鞋们要想学好滑雪的话，要么找到循循善诱的好教练，要么只能把本文里面所有的视频全看一遍，再结合技术帖，自己好好琢磨啦。&lt;br /&gt;练习方法：先在初级雪道上把犁式和半犁式练熟。从半犁式到平行式的过渡最好去稍缓一些的中级雪道上练习，因为滑平行式最好要有小跑以上的速度，雪太薄也不利于练习。&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span class="name"&gt;12 Steps to ski like a pro &lt;/span&gt;&lt;/strong&gt;&lt;a href="http://www.youku.com/playlist_show/id_5558115.html"&gt;http://www.youku.com/playlist_show/id_5558115.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;2. 丸山贵雄教练的&lt;em&gt;纵滑的几种练习方法&lt;/em&gt;&lt;/strong&gt;&amp;nbsp;&lt;a href="http://www.tudou.com/programs/view/9kyuQGC0Kn8/?resourceId=0_06_02_99"&gt;http://www.tudou.com/programs/view/9kyuQGC0Kn8/?resourceId=0_06_02_99&lt;/a&gt;&lt;br /&gt;一些不错的搓雪练习。&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;3. 佐藤久哉教练的&lt;em&gt;刻滑入门&lt;/em&gt;&lt;/strong&gt;&lt;a href="http://www.tudou.com/programs/view/lzyvc802ku0/?resourceId=0_06_02_99"&gt;http://www.tudou.com/programs/view/lzyvc802ku0/?resourceId=0_06_02_99&lt;/a&gt;&lt;br /&gt;强烈推荐！练习滑雪，找到简单有效、能让你循序渐进的一套练习方法是至关重要的。很多教学视频看上去特别NB，但是里面练习方法很多，有些难度也较大，入门选手很容易抓不住重点。反观这个视频只有12分钟，涵盖carving入门、几种单脚carving练习和bending。每种滑法都有2、3种简单而有效的练习方法。看了这个视频的第一感觉就是，原来carving和bending一点也不神秘！&lt;br /&gt;正如这个教学片的名字所表达的，它的重点是carving入门滑法。我觉得它最有价值也最让我喜欢的地方是，他是先教你做核心技术（换刃）的练习，然后再一次一个地加上辅助动作，这样初学者就不会因为动作太多太复杂而顾此失彼。先把核心技术练好、定型，然后再加辅助动作，这才是最有效的练习顺序。特别是一开始的双手扶膝练习一定不要跳过，它可以很好地控制你的上身姿态和转换重心时身体的协调性。我实际练习的时候感觉第二个板子与肩同宽的练习相对容易些，反而第一个板子分开较宽的练习比较难，很容易摔倒。可以先做第二个练习，不过如果板子比较宽的练习总摔倒的话，说明你的动作还是有做得不对的地方，可以作为对你的技术动作的一个很好的检验。&lt;br /&gt;刻滑内涵丰富，需要练习、注意的细节很多。这个视频只是用来入门，很多重要的细节，包括转弯的不同阶段的连接技术，双板的重心分配，手部动作都没有详细介绍。有兴趣练刻滑的童鞋还要多看后面的视频和技术贴。&lt;br /&gt;很多童鞋可能跟我一样，练搓雪平行式总练不利索，看到有高手练刻滑很羡慕，就急着也开始试着刻滑。特别是男生，有运动基础的，一走刃感觉也挺像那么回事，就一路练下来了。但是刻滑的很多发力方法和重心的转换与搓雪是一样的，如果搓雪练不好，刻滑很快就会遇到难以突破的瓶颈。这时候建议再开始练搓雪。因为搓雪的动作频率比刻滑慢，也容易把动作分解开来练，把搓雪的发力方法搞明白了，对刻滑会有很大帮助。&lt;strong&gt;&lt;br /&gt;小贴士&lt;/strong&gt; 所谓&lt;strong&gt;carving&lt;/strong&gt;，直译为&lt;strong&gt;卡宾转弯&lt;/strong&gt;，意译为&lt;strong&gt;刻雪转弯&lt;/strong&gt;或&lt;strong&gt;刻滑&lt;/strong&gt;(有时也会直接管它叫&amp;ldquo;走刃&amp;rdquo;)，是利用carving雪板的特性作出快速转弯的技术。所谓&lt;strong&gt;carving雪板&lt;/strong&gt;，是指板头宽、板腰窄、板尾宽的那种雪板&amp;mdash;&amp;mdash;怪坡滑雪场的就是，东北亚的那种鸟板估计够呛。这种雪板，在立刃的时候受到下压力时，雪板将弯曲成弧线，不需要做其它的多余动作就可以自然、迅捷地完成漂亮的弧形转弯。&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;佐藤久哉的另一个很细致的教程，可惜没字幕&lt;/strong&gt; &lt;a href="http://www.tudou.com/programs/view/KKWWuGoLjnc/?fr=rec1"&gt;http://www.tudou.com/programs/view/KKWWuGoLjnc/?fr=rec1&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;4. 丸山贵雄教练的&lt;em&gt;纵滑的前后动&lt;/em&gt;&lt;/strong&gt; &lt;a href="http://www.tudou.com/programs/view/Vs684off4EE/?fr=rec1"&gt;http://www.tudou.com/programs/view/Vs684off4EE/?fr=rec1&lt;/a&gt;&lt;br /&gt;这个视频专门讲解转弯的第1阶段（从斜滑降轻身之后到雪板指向滚落线），非常细致。&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;5. 柏林义之教练的&lt;em&gt;大弯滑法&lt;/em&gt;&lt;/strong&gt; &lt;a href="http://www.tudou.com/programs/view/1DaMtuIWMl4/?resourceId=0_06_02_99"&gt;http://www.tudou.com/programs/view/1DaMtuIWMl4/?resourceId=0_06_02_99&lt;/a&gt;&lt;br /&gt;非常细致地讲解刻滑大弯的各个阶段的重心和动作，适合已经初步掌握刻滑，想要滑出更漂亮、规范的动作的童鞋。（可惜没字幕）&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;6. 一个马脸帅哥的教学视频&lt;/strong&gt; &lt;a href="http://v.youku.com/v_show/id_XODQ3MDE3ODA=.html"&gt;http://v.youku.com/v_show/id_XODQ3MDE3ODA=.html&lt;/a&gt;&lt;br /&gt;包括大回转、小回转和点杖。一开始感觉讲得不太细，后来才发现里面有很多不错的要点，包括站姿、重心、上身动作，练习也很不错。&lt;strong&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #000000;"&gt;7. Greg Gurshman 教练的刻滑大、小回转教学 &lt;a href="http://www.tudou.com/playlist/id/14026280/"&gt;http://www.tudou.com/playlist/id/14026280/&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/strong&gt;&lt;span style="color: #000000;"&gt;慢动作拍摄得非常好。没字幕和讲解，理论部分可参考本文最后的技术贴。&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;8. Lito Tejada-Flores教练的 &lt;em&gt;a private lesson with Lito Tejada-Flores &lt;/em&gt;&lt;/strong&gt;&lt;a href="http://v.youku.com/v_show/id_XNjQ1NjUyNzY=.html"&gt;http://v.youku.com/v_show/id_XNjQ1NjUyNzY=.html&lt;/a&gt;&lt;br /&gt;1个小时全是单脚carving的多角度慢动作。教练的动作很优美，没字幕，甚至也没多少讲解。&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;9. 佐藤久哉教练的&lt;em&gt;小弯入门&lt;/em&gt;&lt;/strong&gt;&amp;nbsp;&lt;a href="http://www.tudou.com/programs/view/V7n1PZ4tIN8/?fr=rec1"&gt;http://www.tudou.com/programs/view/V7n1PZ4tIN8/?fr=rec1&lt;/a&gt;&lt;br /&gt;与刻滑入门一样，佐藤久哉一如既往地简明、独特。特别是里面的点杖练习非常独到，想练点杖小回转的童鞋一定得看看。&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;朴教练的小回转教学 &lt;/strong&gt;&lt;a href="http://v.youku.com/v_show/id_XMjQwNjM0NDgw.html"&gt;http://v.youku.com/v_show/id_XMjQwNjM0NDgw.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;10. &lt;em&gt;六种小回转技术&lt;/em&gt; &lt;a href="http://v.youku.com/v_show/id_XMzE0NzU5Njgw.html"&gt;http://v.youku.com/v_show/id_XMzE0NzU5Njgw.html&lt;/a&gt;&lt;/strong&gt;&lt;br /&gt;里面的练习都很有效，后面几个难度稍大，但是游戏要是只有3关岂不是会很没追求？&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;11. 渡边一树教练的&lt;em&gt;猫跳入门&lt;/em&gt;&lt;/strong&gt;&amp;nbsp; &lt;a href="http://www.tudou.com/programs/view/qkOVJ32llcM/" target="_blank"&gt;第一部分&lt;/a&gt;，&lt;a href="http://www.tudou.com/programs/view/RcJswxnQcpA/" target="_blank"&gt;第二部分&lt;/a&gt;&lt;br /&gt;很细致的蘑菇教程。里面有很好的搓雪练习，即使你暂时还不打算挑战蘑菇也值得一看（特别是6:30开始的180及360度搓雪练习）。&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;12. 丸山贵雄教练的&lt;em&gt;深弧小弯&lt;/em&gt;&lt;/strong&gt; &lt;a href="http://www.tudou.com/programs/view/tCF6D37tlEA/?resourceId=0_06_02_99"&gt;http://www.tudou.com/programs/view/tCF6D37tlEA/?resourceId=0_06_02_99&lt;/a&gt;&lt;br /&gt;所谓深弧小弯是指在陡坡上使用搓雪和刻滑相结合的技术作出快节奏、大弧度的转弯。里面有很多非常独特的练习点杖的方法，连我这个严重的点杖恐惧症患者看了之后都忍不住想要练习一下了。&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;13.&amp;nbsp;&lt;em&gt;滑雪表演串烧&lt;/em&gt;&lt;/strong&gt; &lt;a href="http://www.tudou.com/programs/view/qbN1bTAN74c/?resourceId=0_06_02_99"&gt;http://www.tudou.com/programs/view/qbN1bTAN74c/?resourceId=0_06_02_99&lt;/a&gt;&lt;br /&gt;4:00开始的那段雪上舞蹈真是酷毙了。视频的最后还非常体贴地录了一段摔跤集锦，意思是你看那么多高手也摔得啪啪地。&lt;br /&gt;&lt;strong&gt;超酷的雪上芭蕾，神乎其技&lt;/strong&gt;：&lt;a href="http://www.tudou.com/programs/view/vW9RwLPCutA/"&gt;http://www.tudou.com/programs/view/vW9RwLPCutA/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;14. 非常清晰的大、小回转慢动作视频&lt;/strong&gt;&lt;br /&gt;大回转：&lt;a href="http://www.tudou.com/programs/view/yEMSmc2D0M0/?resourceId=0_06_02_99"&gt;http://www.tudou.com/programs/view/yEMSmc2D0M0/?resourceId=0_06_02_99&lt;/a&gt;&lt;br /&gt;小回转：&lt;a href="http://www.tudou.com/programs/view/9zjU6BciRWY/?resourceId=0_06_02_99"&gt;http://www.tudou.com/programs/view/9zjU6BciRWY/?resourceId=0_06_02_99&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;15. 大小回转欣赏&lt;/strong&gt;&lt;br /&gt;&lt;a href="http://www.tudou.com/programs/view/x7a17mPc2d4/?fr=rec1"&gt;http://www.tudou.com/programs/view/x7a17mPc2d4/?fr=rec1&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.tudou.com/programs/view/T42Iij0xRZI/?fr=rec1"&gt;http://www.tudou.com/programs/view/T42Iij0xRZI/?fr=rec1&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #000000;"&gt;&lt;strong&gt;16. 野雪教程&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;野雪教程-第一部分基本练习 &lt;a href="http://www.tudou.com/programs/view/KUxaCFXGZyE/?resourceId=0_06_02_99"&gt;http://www.tudou.com/programs/view/KUxaCFXGZyE/?resourceId=0_06_02_99&lt;/a&gt;&lt;br /&gt;野雪教程-第二部分实用技巧 &lt;a href="http://www.tudou.com/programs/view/IiDr_SKlQQQ/?resourceId=0_06_02_99"&gt;http://www.tudou.com/programs/view/IiDr_SKlQQQ/?resourceId=0_06_02_99&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #008000;"&gt;&lt;strong&gt;技术贴&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;1. &lt;a href="http://bbs.lvye.cn/thread-199845-1-1.html" target="_blank"&gt;滑雪中的连接技术：出弯、入弯和转换&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;2. Greg Gurshman 教练的几篇非常精辟的文章&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;a href="http://bbs.lvye.cn/thread-334175-1-1.html" target="_blank"&gt;内侧板在现代竞技转弯中的应用&lt;/a&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;a href="http://bbs.lvye.cn/thread-334061-1-1.html" target="_blank"&gt;现代高山滑雪竞技技术&lt;/a&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;a href="http://bbs.lvye.cn/thread-394036-1-1.html" target="_blank"&gt;一个利用S弯和香蕉弯提高大回转转弯技术的方法&lt;/a&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;a href="http://bbs.lvye.cn/thread-399339-1-1.html" target="_blank"&gt;现代技术的趋势&amp;mdash;&amp;mdash;神话和现实&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;3. &lt;a href="http://wolfski.blog.sohu.com/165697095.html" target="_blank"&gt;NPS自然能量滑雪学习体系（进阶衔接部分：图解雪板转弯的奥秘）&lt;/a&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 布鲁狼的理论讲解非常给力，特别是里面配的手绘的图特别好，非常有才。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;a href="http://wolfski.blog.sohu.com/199901978.html" target="_blank"&gt;Carving 基础&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;4. &lt;a href="http://wolfski.blog.sohu.com/197753884.html" target="_blank"&gt;《点杖小回转》之 &amp;ldquo;揭开新的篇章--扭转&amp;rdquo;&lt;/a&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;a href="http://wolfski.blog.sohu.com/197859172.html" target="_blank"&gt;《点杖小回转》之二 &amp;ldquo;如何用好三板斧&amp;rdquo;&lt;/a&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;a href="http://wolfski.blog.sohu.com/137671000.html" target="_blank"&gt;滑雪葵花宝典之 《点仗心经》&lt;/a&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 布鲁狼的讲解比较独到的地方是，他不是一开始就告诉你最标准的全套动作是怎么样的，而是给你一些练习让你自己去实验不同的动作出来的效果，然后再讲解发力的方法和原理，非常难得。&lt;br /&gt;&lt;br /&gt;5. 一篇很不错的野雪教程&amp;nbsp;&lt;a href="http://blog.sina.com.cn/s/blog_489d12ef0100kq1m.html"&gt;http://blog.sina.com.cn/s/blog_489d12ef0100kq1m.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;6.&amp;nbsp;&amp;nbsp;登山滑雪装备概述 &lt;a href="http://bbs.lvye.cn/thread-354353-1-1.html"&gt;http://bbs.lvye.cn/thread-354353-1-1.html&lt;/a&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 登山滑雪装备最新发展趋势 &lt;a href="http://bbs.lvye.cn/thread-356322-1-1.html"&gt;http://bbs.lvye.cn/thread-356322-1-1.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;(持续更新...）&lt;/p&gt;&lt;img src="http://www.cnblogs.com/1-2-3/aggbug/2380318.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/1-2-3/archive/2012/03/08/ski-vidio.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/1-2-3/archive/2012/03/04/diary-2012-03-04.html</id><title type="text">[日记]夜滑</title><summary type="text">追着雪季的尾巴接连滑了好几次夜场，今天是最后一次。</summary><published>2012-03-04T15:17:00Z</published><updated>2012-03-04T15:17:00Z</updated><author><name>1-2-3</name><uri>http://www.cnblogs.com/1-2-3/</uri></author><link rel="alternate" href="http://www.cnblogs.com/1-2-3/archive/2012/03/04/diary-2012-03-04.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/1-2-3/archive/2012/03/04/diary-2012-03-04.html"/><content type="html">&lt;p&gt;拉开车门准备下车的时候，我对小驰和夜场大哥说&amp;ldquo;明年见！&amp;rdquo;。我们开怀一笑，心里隐隐觉得这回也许又要食言。&lt;br /&gt;追着雪季的尾巴接连滑了好几次夜场，今天是最后一次。&lt;br /&gt;&lt;br /&gt;从雪具大厅出来，往魔毯上走的时候挺费劲，因为要越过一个结了冰的小陡坡，雪板不容易刻住雪，用雪杖小心地支着往上蹭的样子挺笨拙。这时候，一个玩单板的家伙指着我跟他的同伴说，&amp;ldquo;你看你看，就这种连走都走不明白的就别上中级道了，还往上上&amp;hellip;&amp;hellip;&amp;rdquo;当时真把我气得够呛，心想滑雪要是像篮球那样也能单挑就好了，咱俩比比到底谁技术好。不过后来转念一想，他刚刚被一个新手狠狠地撞了一下胳膊，也算是受害者。其实任何运动都一样，你老是得忍受那些一瓶不满、半瓶晃荡的伪高手们脆弱的自尊心。&lt;br /&gt;所以，脸皮厚很重要。&lt;br /&gt;为什么这么说呢？初学者都希望能尽快进入中级水平，这是人之常情。进入中级水平之后，往往就会因基本功不好而遇到瓶颈。这时如果害怕被伪高手笑话而不好意思从头练基本功的话，就很难突破瓶颈。不拍摔跤固然可贵，不怕笑话才是难得。&lt;br /&gt;胆大，心细，脸皮厚，业余滑雪爱好者三大天赋是也。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/1-2-3/aggbug/2397892.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/1-2-3/archive/2012/03/04/diary-2012-03-04.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/1-2-3/archive/2012/01/18/intro-pomodoro.html</id><title type="text">[推荐]番茄工作法——专治拖延症、精神涣散、再要五分钟综合症</title><summary type="text">首先向仍然在工作岗位上奋战以及回到家还不忘刷博客园的兄弟们致敬。最近读了一点《The Clean Coder》，一个意外的收获是，知道了原来还有个“番茄工作法”。尝试了几天，觉得很有效，推荐给你。XX工作法，一听这名字你就能猜到，是时间管理，而时间管理又往往等同于成功学，所以如果你看了标题之后习惯性地无视本文的话，我也不怪你。成功学没有效果，是因为每个人的特质、境遇都不相同，成功往往无法复制。相反的，想办法改掉自己的一些坏习惯会更有效果。</summary><published>2012-01-18T01:51:00Z</published><updated>2012-01-18T01:51:00Z</updated><author><name>1-2-3</name><uri>http://www.cnblogs.com/1-2-3/</uri></author><link rel="alternate" href="http://www.cnblogs.com/1-2-3/archive/2012/01/18/intro-pomodoro.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/1-2-3/archive/2012/01/18/intro-pomodoro.html"/><content type="html">&lt;p&gt;&lt;img style="float: right;" src="http://pic002.cnblogs.com/images/2012/25284/2012011717574219.png" alt="" width="178" height="150" /&gt;&lt;/p&gt;&lt;p&gt;首先向仍然在工作岗位上奋战以及回到家还不忘刷博客园的兄弟们致敬。&lt;br /&gt;最近读了一点《&lt;a href="http://book.douban.com/subject/6114900/" target="_blank"&gt;The Clean Coder&lt;/a&gt;》，一个意外的收获是，知道了原来还有个&amp;ldquo;番茄工作法&amp;rdquo;。尝试了几天，觉得很有效，推荐给你。&lt;br /&gt;XX工作法，一听这名字你就能猜到，是时间管理，而时间管理又往往等同于成功学，所以你看了标题之后习惯性地无视本文的话，我也不怪你。成功学没有效果，是因为每个人的特质、境遇都不相同，成功往往无法复制。相反的，想办法改掉自己的一些坏习惯会更有效果。&lt;br /&gt;我同时患有严重的拖延症、精神涣散和再要五分钟综合症，用了番茄工作法之后，好转了很多。&lt;br /&gt;先说说我的病情。&lt;br /&gt;&lt;strong&gt;拖延症&lt;/strong&gt;&amp;mdash;&amp;mdash;一直在心里想着&amp;ldquo;那件事必须得做了&amp;rdquo;，可就是迟迟不能开始。越是困难的、重要的事情，越容易这样。&lt;br /&gt;&lt;strong&gt;再要五分钟综合症&lt;/strong&gt;&amp;mdash;&amp;mdash;无论娱乐还是工作，都容易烂尾。想好了娱乐到9点就工作，结果拖拖拉拉到10点还没开始干正经事；下定决心要在11点之前睡觉，但是天天都会磨蹭到11点半，12点还没上床。&lt;br /&gt;&lt;strong&gt;精神涣散&lt;/strong&gt;&amp;mdash;&amp;mdash;我们都知道&lt;strong&gt;打断&lt;/strong&gt;的危害。但是仔细想想，被别人打断的次数远没有被自己打断自己的次数多。譬如编译程序的时候，感觉好慢呐，赶快趁机刷下微博或者豆瓣吧，然后看到网友上传了美女图片，呦，韩寒写了新博客啦，完了又想起周报还没写呢&amp;hellip;&amp;hellip;&lt;br /&gt;这三种毛病综合发作的结果就是，整天愁眉苦脸、心急火燎，好像忙忙叨叨累的不行，到了该睡觉的时候却发现其实没干多少正经事，越发舍不得睡觉。脸色苍白，目光空洞，神疲气短，恶性循环。&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #008000;"&gt;&lt;strong&gt;番茄工作法&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;番茄工作法用一句话来概括就是&amp;mdash;&amp;mdash;&lt;strong&gt;工作25分钟休息5分钟&lt;/strong&gt;。详细条款如下：&lt;br /&gt;&lt;strong&gt;0. 准备工作：买一个厨房计时器，或者使用软件计时器也行&lt;/strong&gt;（&lt;a href="http://code.google.com/p/pomodairo/" target="_blank"&gt;pc&lt;/a&gt;，&lt;a href="http://pomodoro.ugolandini.com/" target="_blank"&gt;mac&lt;/a&gt;，&lt;a href="http://pomodoropro.com/" target="_blank"&gt;iphone&lt;/a&gt;，&lt;a href="http://51souapp.com/other/939405.html" target="_blank"&gt;Android&lt;/a&gt;）。&lt;br /&gt;&lt;strong&gt;1. 把计时器设定为25分钟，开始工作，直到响铃，算工作了一个番茄时间。然后在你的本子的工作项后面画一个叉叉表示此项工作已经耗费了一个番茄时间（如果使用软件会自动记录，更为方便）。&lt;/strong&gt;&lt;br /&gt;在一个番茄时间内，不可以被打断。这意味着等待编译的时候也不可以刷微博，也不要去泡咖啡或者吃东西。如果想到周报还没写，在本子上记上&amp;ldquo;需要写周报&amp;rdquo;然后马上回到工作中。如果有同事打扰你或者接到客户的电话，礼貌地请求他等待20分钟，在本子上记上&amp;ldquo;需要给王科长回电话&amp;rdquo;然后马上回到工作中。当然有时人家也会大喊一声&amp;ldquo;我等不了了！&amp;rdquo;，需要你立即紧急处理，那么这个番茄时间就相当于白费了，回到工作中时要重新开始这个番茄时间，就当这个番茄时间从来没有开始过。响铃之后，你就可以看一下你的本子，处理刚刚记下的事情。如果这些零碎的小事很多，可以开启一个番茄时间集中处理它们。&lt;br /&gt;在一个番茄时间内，只做一件工作。如果工作了20分钟就完成了，也不要开始下一项工作或者去娱乐，可以回顾一下，检查/测试，总结经验，做一些优化或美化。如果工作了5分钟就完成了，而且你觉得它本可以在上一个番茄时间里就完成的，并且再去复查一遍也没什么必要，可以将这个番茄时间作废。&lt;br /&gt;&lt;strong&gt;2. 休息5分钟。&lt;/strong&gt;&lt;br /&gt;响铃之后必须立即停止手中的工作，想象你在考试，时间到，马上停笔。即使你觉得在休息的几分钟里就能完成它，也不能继续工作。而且，脑子里也不要再去想任何有关你工作的事情。这其实挺难做到的，我的建议是&amp;mdash;&amp;mdash;离开你的座位&amp;mdash;&amp;mdash;去做运动，或者跟同事聊聊天。&lt;br /&gt;&lt;strong&gt;3. 每4个番茄之后休息15分钟。&lt;br /&gt;&lt;/strong&gt;如果感到疲劳，也可3个番茄就长休一次。或者把休息时间延长，但是不应超过25分钟。&lt;strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;/strong&gt;更为详细的内容可以参考&lt;a href="http://www.pomodorotechnique.com/" target="_blank"&gt;网站&lt;/a&gt;，电子书&lt;a href="http://www.pomodorotechnique.com/resources/ThePomodoroTechnique_v1-3.pdf" target="_blank"&gt;英文版&lt;/a&gt;，&lt;a href="http://www.pomodorotechnique.com/resources/ThePomodoroTechnique-CHN_v1-3.pdf" target="_blank"&gt;中文版&lt;/a&gt;（仅32页，值得一读）。番茄工作法由意大利人Francesco Cirillo(弗朗切斯科&amp;bull;齐立罗)发明于1992年。因发明人最开始使用的是番茄形状的计时器而得名。&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #008000;"&gt;&lt;strong&gt;番茄工作法的好处&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;健康&lt;/strong&gt;。这个世界上有太多好玩的事情让人目不暇接。今晚有NFL季后赛野马vs爱国者耶，另外巴萨的比赛也不能错过，还有NBA快船vs湖人也想看一看。《The Clean Coder》看了一半不能扔下，还想再研究下ios编程&amp;hellip;&amp;hellip;一件接着一件，一直不休息，直到累得实在难受才去床上趴一会儿，要么睡不着要么一睡不起。这种被动的休息方式对健康极为不利。最好的休息方式是在感到累之前就做短暂的休息。&lt;br /&gt;&lt;strong&gt;保持大脑的活力&lt;/strong&gt;。由于特定的生化原理，大脑内每个神经元都只能连续工作几分钟，之后就必须休息。连续的娱乐和工作会让大脑处于一种麻木、呆滞的状态。番茄团队经过大量研究发现，为保持大脑的活力和效率最大化，最佳番茄时间为25～35分钟。我们大多数人连续工作的时间都是1小时或2小时，所以直觉上会怀疑25分钟的工作时间是不是短了点？会不会刚刚进入状态就被休息打断了？其实，5分钟的休息并不会中断你的思路，反而可以让大脑里的清洁工有时间打扫战场，给脑细胞做做马杀鸡，之后你会文思泉涌也说不定。&lt;br /&gt;&lt;strong&gt;成就感&lt;/strong&gt;。每个番茄代表一个纯的25分钟的工作时间。每天数一下番茄数就知道自己做了多少有用功，对每项工作耗费了多少时间也会有更具体的感觉。你可以知道自己娱乐了几个番茄，看了几个番茄的书，做了几个番茄的练习，对时间更有掌控感。&lt;br /&gt;&lt;strong&gt;安全感&lt;/strong&gt;。你可以安全地先娱乐一个番茄，然后再看一个番茄的书，不必担心一娱乐起来就停不下。&lt;br /&gt;&lt;strong&gt;专心&lt;/strong&gt;。娱乐的时候想着工作，工作的时候不忘娱乐，这是我们大多数人的工作方式，也是低效和焦虑的根源。稀里糊涂地就到了下午，到了晚上，计划难以完成，好像做了很多额外的事情，却又想不起来都干了些什么了。要想摆脱精神涣散的阴影，必须明确地强迫自己&amp;ldquo;一次只做一件事情，并且必须连续做25分钟&amp;rdquo;。&lt;br /&gt;&lt;strong&gt;张弛有度&lt;/strong&gt;。根据我的经验，对于一个严重的&amp;ldquo;再要五分钟综合症&amp;rdquo;患者，只是自己在心里想着&amp;ldquo;到九点就休息&amp;rdquo;是不够的，到时候不是差一点没完成，就是有了新的兴趣点。借助于计时器和&amp;ldquo;铃声响起立即停止&amp;rdquo;条款，才能干净利落地根治此症。&lt;br /&gt;&lt;strong&gt;不再拖延&lt;/strong&gt;。拖延症患者大多喜欢追求完美。越是想要做得好，就越容易否定自己，觉得没思路，不够好，信心受挫之下更容易精神涣散。使用番茄法，按下计时器，叮咚，强迫自己没思路也不许干别的，就算干想也要想满25分钟。&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #008000;"&gt;&lt;strong&gt;娱乐也番茄&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;书里说空闲的时间就不要用番茄法了。但是我的问题是，因为我觉得刚刚吃饱就工作不太合适，总想先娱乐一会儿。可是一旦娱乐起来就停不下来，所以我想娱乐也可以使用番茄法。比如&amp;ldquo;看一个番茄的电影&amp;rdquo;，或者&amp;ldquo;看一个番茄的比赛&amp;rdquo;，之后休息五分钟。这样既有利于健康，也有机会决定之后是继续娱乐一个番茄，还是工作一个番茄。&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #008000;"&gt;&lt;strong&gt;番茄你的计划&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;把番茄法与你的工作计划结合起来也很简单，就是使用番茄数作为工作项的时间单位。首先，你可以通过经验知道你每天平均能做几个番茄的工作。然后，估算每个工作项需要的番茄数，这样你就能知道明天可以完成哪几个工作项了。每天结束的时候，再回顾一下每个工作项实际使用的番茄数。简单实用，是吧？&lt;br /&gt;也许你还没有做计划的习惯，番茄法可以给你一个很好的起点。因为在按下计时器的开关之前，必须先想好&amp;ldquo;只能做一件事情呐，这个番茄我要做什么？&amp;rdquo; 这样子，逐渐的升级为&amp;ldquo;上午的3个番茄我要做什么？&amp;rdquo;随着你的技巧越来越娴熟，你会有能力并且愿意做全天的计划，一周的计划&amp;mdash;&amp;mdash;不是随便写写、应付领导的，也不会写完就丢在一边，而是真正切合实际，容易跟踪和度量，通过不断的反馈得到改进的计划。&lt;br /&gt;&lt;strong&gt;贴士&lt;/strong&gt; 一个工作项最多5到7个番茄，如果多于这个数，就认为这个任务太过复杂，最好把它分解为几个小任务。如果一个工作项的估值小于一个番茄，把它与其它的小任务组合成一个大任务。&lt;br /&gt;&lt;strong&gt;小贴士&lt;/strong&gt; 你可以在做计划的时候用空心方框&amp;ldquo;[]&amp;rdquo;表示一个番茄时间，3个番茄时间就是&amp;ldquo;[][][]&amp;rdquo;，把它标记在工作项的后面。实际工作时，每耗费一个番茄时间就在空心方框里打个叉叉，耗费2个番茄时间是就是&amp;ldquo;[x][x][]&amp;rdquo;，耗费4个番茄时间时是&amp;ldquo;[x][x][x]x&amp;rdquo;）。&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/1-2-3/113933/o_logo2_170_r.png" alt="" width="170" height="66" /&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/1-2-3/aggbug/2324917.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/1-2-3/archive/2012/01/18/intro-pomodoro.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/1-2-3/archive/2011/09/12/impression-of-toshiba.html</id><title type="text">东芝の印象</title><summary type="text">东芝有两个挺气派的厂房。说它气派，主要是因为它有好几个又高又宽，大大的卷帘门——方便用卡车直接把电梯运出去。卷帘门平常是关着的，按规定也不允许人走，我每次只能很委屈地走两侧普普通通的、一比较起来就显得很寒酸的小铁门。“要是卷帘门完全敞开了，直接从那么大的门走进去，哇，不知道有多爽呢。”</summary><published>2011-09-12T12:18:00Z</published><updated>2011-09-12T12:18:00Z</updated><author><name>1-2-3</name><uri>http://www.cnblogs.com/1-2-3/</uri></author><link rel="alternate" href="http://www.cnblogs.com/1-2-3/archive/2011/09/12/impression-of-toshiba.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/1-2-3/archive/2011/09/12/impression-of-toshiba.html"/><content type="html">&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;东芝有两个挺气派的厂房。说它气派，主要是因为它有好几个又高又宽，大大的卷帘门&amp;mdash;&amp;mdash;方便用卡车直接把电梯运出去。卷帘门平常是关着的，按规定也不允许人走，我每次只能很委屈地走两侧普普通通的、一比较起来就显得很寒酸的小铁门。&amp;ldquo;要是卷帘门完全敞开了，直接从那么大的门走进去，哇，不知道有多爽呢。&amp;rdquo;每次深吸一口气弓着腰去拽小铁门的时候我都这么想着。功夫不负有心人，机会是给有准备的人的，总之，有一天我发现了一个大敞着的卷帘门，顿时心中一亮，小鹿乱撞，急忙小跑着过去，四下打量了一下没人，&amp;ldquo;咻&amp;rdquo;的一下就穿了过去。可是并没有多爽，反而因为担心挨骂以及头上的卷帘会突然砸下来而提心吊胆的，再说那个过程太快了，也根本来不及爽。唉，想象中美好的东西多半不是那么回事。但是，以后只要发现了敞开着的卷帘门，我还是会四下打量一下然后&amp;ldquo;咻&amp;rdquo;的一下穿过去，因为放着现成的、大大门的不走，非要费劲巴力地拽开小铁门走进去，那是多么的白痴啊。&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;2.&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;我喜欢聊天，和美女聊天，自由自在、无拘无束的聊天，我喜欢东芝的聊天文化。当然东芝高管们的字典里可没有这一条，要是他们知道聊天成了企业文化不知道会不会气得翻白眼？那时我刚刚步入职场，业务、人头都不熟，整天干坐着心里空落落地挺不好过，看着别人忙这忙那只能干瞪眼。那时候，桌子还是四张四张拼在一起，我对面正巧坐着宁宁和蓉蓉两位美女。我们一开始就聊得挺开心，每天都能聊上一个多小时。我印象最深的是每次宁宁都会特意把脑袋从大大的显示器后面探出来，这对她来说是很自然的事情，但是我总觉得得到了特别的尊重和关注，每次心里都特别高兴。&lt;br /&gt;在东芝，拉家常是一项必备的工作技能。可能因为前身是老国企的缘故，这里到处都洋溢着一股子慵懒&amp;mdash;&amp;mdash;倒不是说大家都很闲&amp;mdash;&amp;mdash;早就不是国企了，大家其实都挺忙，压力也不小，但是就像英国的绅士以不工作为荣，这里每个人见了面都憋着非得拉几句家常不可。每次跟宁宁去别的部门办事，她们都得聊上至少半小时，而我就像被聊得热乎的家长们撇在一边的小孩子，站也不是、坐也不是。&lt;br /&gt;我们聊得多了梁部长会感觉很不爽。有一次开会的时候他就拉着脸说让我们注意工作气氛，那以后他在屋里的时候我们就不怎么聊了。过了一段时间，又开会，梁部长说你们全是年轻人怎么一天到晚死气沉沉的，都没人说话的&amp;hellip;&amp;hellip;我们全部晕倒。&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;3.&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;我在东芝呆了五年，跟许多日本人打过交道，由于种种原因相处得不太愉快，有时候我甚至气得骂脏话，日本人估计也会说些不好听的，但是我从没听他们说过&amp;ldquo;巴嘎&amp;rdquo;，一次都没有，多奇怪！在动画片里不是也经常出现这个词的么？为什么来中国就不说了呢？我真后悔没直接问他们。是不是来中国前受过培训呢？（要是他们真对我说了这个词不知道我会是什么反应）我知道每个日本人来中国之前都会有短期培训，学一些简单的会话，以及注意事项什么的，这种细致的工作作风挺让人佩服。很多日本人来中国都会主动要求去九一八纪念馆参观，有一次还正好是在9月18号前后，我开玩笑地说他们还真不怕挨打。&lt;br /&gt;前年忘年会的时候，梁部长不无感慨地说&amp;ldquo;我跟日本人打了10多年交道，老实说，我不了解日本人。&amp;rdquo;确实，日本人跟中国人长得虽然很像，确是那么不同的一个品种。大事说起来没意思，有一件小事很有趣，我记得很清楚。那天是周六，我们三四个同事还有一个叫星原的日本人一起加班。一个同事带了一袋子油桃，洗了之后分给大家吃。这时候星原进来了，我们想也不能让他看着我们吃呀，于是也分给他一个。星原看着手里的油桃，问&amp;ldquo;这是什么东东？&amp;rdquo;我们告诉他是油桃，他听不懂，我们说&amp;ldquo;是桃子。&amp;rdquo;他很机警地说&amp;ldquo;桃子不是有毛的么？这个怎么没有毛？&amp;rdquo;我们说&amp;ldquo;诶呀，你看我们不是吃的挺香的嘛，让你吃你就放心大胆地吃好了，婆婆妈妈的。&amp;rdquo;星原又端详了半天，突然从兜里掏出一把小刀，把桃子的皮给削了，很得意地嘿嘿了两声，然后才吃了起来。&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;4.&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;我辞职的时候，按规定必须拿着一张印着所有部长名字的纸，让每个部长在那上面盖章。我拿着那张纸跑来跑去。在楼上的时候（好像是技术部），部长一边盖章一边说&amp;ldquo;可惜了&amp;rdquo;，挺痛快。在一楼，贺部长拿着他的印章，看着我的那张纸，自己在那儿琢磨，也不知道在琢磨啥，琢磨了半天，然后撇了撇嘴，掂了掂手里的章，又琢磨了一下，才重重地盖了上去，一边对我说&amp;ldquo;可惜了，虽然东芝不是最好的选择。&amp;rdquo;这件事我现在还记得，因为当时想着&amp;ldquo;怪不得人家能当那么大领导，同样一句话，人家说出来就感觉那么真诚，那么有分量。&amp;rdquo;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;5.&lt;/strong&gt;&amp;nbsp;&lt;br /&gt;&lt;br /&gt;还有一件挺有意思的小事。那是我们刚刚入职的时候，要统一进行入职培训，其实就是HR给我们讲一讲公司的制度、纪律什么的。讲师是一个50多岁感觉很实在的大叔，我们管他叫赵老师。赵老师说&amp;ldquo;注意上班时的纪律，不允许上网、看报纸，也不要趴在桌子上睡觉&amp;rdquo;，我上学的时候很喜欢趴在桌子上睡觉，所以当时心里很忐忑地想&amp;ldquo;不让趴桌子呐，不知道能不能挺得住。&amp;rdquo;他接着说&amp;ldquo;中午午休一小时，不允许打扑克。&amp;rdquo;，还有&amp;ldquo;我们中午去食堂吃饭，分两拨，第一拨11:50，第二拨12:10分，间隔20分钟。&amp;rdquo;我因为吃饭比较慢所以很担心地问&amp;ldquo;要是20分钟之内吃不完怎么办？&amp;rdquo;他说&amp;ldquo;一般都能吃得完的。&amp;rdquo;我还是很担心地问&amp;ldquo;那万一吃不完怎么办呢？&amp;rdquo;他说&amp;ldquo;怎么会吃不完呢？你上了班就知道了，都着急回去打扑克，没有吃不完的&amp;hellip;&amp;hellip;&amp;rdquo;&amp;ldquo;啊？你刚刚不是说不允许打扑克的吗？&amp;rdquo;我狂晕。&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/1-2-3/113933/o_logo2_170_r.png" width="170" height="66" /&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/1-2-3/aggbug/2174192.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/1-2-3/archive/2011/09/12/impression-of-toshiba.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/1-2-3/archive/2011/09/05/model-method-part3.html</id><title type="text">建模心法(3) 模型的演进——保持简单和弹性的3个建议</title><summary type="text">对于开发信息系统的程序员来说，最闹心、最上火、最忐忑、最纠结、最不好干、而且肯定会被埋怨的活儿是什么？</summary><published>2011-09-05T01:32:00Z</published><updated>2011-09-05T01:32:00Z</updated><author><name>1-2-3</name><uri>http://www.cnblogs.com/1-2-3/</uri></author><link rel="alternate" href="http://www.cnblogs.com/1-2-3/archive/2011/09/05/model-method-part3.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/1-2-3/archive/2011/09/05/model-method-part3.html"/><content type="html">&lt;p&gt;问：&amp;ldquo;对于开发信息系统的程序员来说，最闹心、最上火、最忐忑、最纠结、最不好干、而且肯定会被埋怨的活儿是什么？&amp;rdquo;&lt;br /&gt;答案是&amp;ldquo;建立领域模型&amp;rdquo;。好的（或者说合理的）模型必须基于对业务深刻理解。一个刚刚接触某个陌生领域的程序员，无论他怎么努力，对业务的理解一定是肤浅和片面的。他明知这个时候作出的模型一定是不够合理的，但是又不得不做，可谓明知不可为而为之，怎能不闹心、不上火、不忐忑？随着项目的推进，对业务的理解也在不断深入，需要对模型不断地重构&amp;mdash;&amp;mdash;&lt;a href="http://book.douban.com/subject/1629512/" target="_blank"&gt;《DDD》&lt;/a&gt;也是这么说的嘛，但是它没提重构的成本有多大。讽刺的是，越到项目的最后阶段，甚至是上线一段时间之后，越容易产生&amp;ldquo;啊哈！原来是这么回事！&amp;rdquo;的顿悟。如果你已经完成了100个页面、300张报表，还有几百万条一分钱都不许错的生产数据，这时候，即使把一个字段从一个表转移到另一个表都可能让你闹心好几天。重构还是不重构？你怎能不纠结，不被埋怨？&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="color: #008000;"&gt;简单&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;重构是不可避免的，特别是项目想要产品化，生命周期在5年、10年甚至更久的时候。既然明知道初始的模型肯定是不够合理的&amp;mdash;&amp;mdash;还是咬咬牙承认吧&amp;mdash;&amp;mdash;我们的目标只能（很委屈地）变成&amp;ldquo;构建一个重构成本比较小的模型&amp;rdquo;。&lt;br /&gt;那是怎样的模型呢？答案也很普通：&amp;ldquo;简单的模型&amp;rdquo;。&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;建模心法4&lt;/strong&gt;&amp;nbsp; 初始模型一定要简单。&lt;br /&gt;&lt;br /&gt;任何事物，总是由小到大、由简单到复杂的，所以你一准儿在心里吐槽说&amp;ldquo;这神马破文章，看了半天原来全是废话。&amp;rdquo;这里的问题是，我们知道领域模型的重构成本很大，对此心有余悸，所以即使想着&amp;ldquo;就目前的状况来看一个员工只有一个所属科室&amp;hellip;&amp;hellip;&amp;rdquo;，但是总有个小恶魔在耳边嘀咕着：&amp;ldquo;就以往的经验和客户的口风，一个员工有多个所属科室是非常可能而且合理的，与其将来重构，不如现在就设计成多对多关系&amp;hellip;&amp;hellip;&amp;rdquo;&lt;/p&gt;&lt;p&gt;&lt;img alt="" src="http://pic002.cnblogs.com/images/2011/25284/2011082710400072.png" /&gt;&lt;br /&gt;一段时间之后，果然出现了这样的需求：一个医生本来是属于某个住院科室的（例如一病区、二病区&amp;hellip;&amp;hellip;），但是他也要隔三差五地出门诊，他在出门诊时，是属于某个门诊科室的（例如普通门诊、小儿门诊&amp;hellip;&amp;hellip;）。难道我们的前瞻性设计成功了？！再仔细考察一下，就会发现2个问题：1）虽说一个医生可以有多个部门和角色，但是他同一时间不能既是住院医生又是门诊医生；2）当他在住院部工作时，不但所属科室是某个住院科室，而且所需的功能模块也和出门诊时不一样。也就是说，医生有&amp;ldquo;住院医生&amp;rdquo;和&amp;ldquo;门诊医生&amp;rdquo;两个身份，分别对应不同的科室和角色，他在工作时，必须选择一个身份作为他的当前身份。&lt;/p&gt;&lt;p&gt;&lt;img alt="" src="http://pic002.cnblogs.com/images/2011/25284/2011082815141378.png" /&gt;&lt;/p&gt;&lt;p&gt;在实际实现的时候，考虑到&amp;ldquo;大多数员工都只有一个身份&amp;rdquo;以及&amp;ldquo;员工最多有3个身份&amp;rdquo;这两个实际情况，可以使用更为简单的结构：&lt;/p&gt;&lt;p&gt;&lt;img alt="" src="http://pic002.cnblogs.com/images/2011/25284/2011082816345273.png" /&gt;&lt;/p&gt;&lt;p&gt;如果某个员工没有第二部门和第二角色，就空着。如果某个员工有第二部门和第二角色，在登录系统时，需要由用户选择一个部门&amp;amp;角色作为他的当前部门&amp;amp;当前角色。&lt;br /&gt;&lt;strong&gt;小贴士&lt;/strong&gt; 在分析业务的时候要注意区分&amp;ldquo;有限多个&amp;rdquo;和&amp;ldquo;不确定数量的多个&amp;rdquo;，它们往往需要不同的结构。&lt;br /&gt;讨论到这里不难得出结论，&lt;strong&gt;把简单模型重构成复杂模型不是件容易的事，但是如果一开始就使用复杂的模型，往往就会需要由一个复杂模型向另一个复杂模型的重构，那可就有点让人望而生畏了&lt;/strong&gt;。&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="color: #008000;"&gt;概念&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;由于模型的重构成本比较大，所以终归还是希望不要动不动就大改。但是新需求是无法预料的，同时由于前面讨论的原因又不想做太多的前瞻性设计，如何增加模型的弹性呢？为什么那些设计得好的模型总能从容应对需求的变化呢？如果硬要说有什么诀窍的话，那就是&amp;mdash;&amp;mdash;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;建模心法5&lt;/strong&gt; 从一开始就关注实物背后的概念&lt;br /&gt;&lt;br /&gt;我不想故弄玄虚，玩概念。这里的&lt;strong&gt;概念&lt;/strong&gt;指的是对实物进行一点儿&lt;strong&gt;抽象&lt;/strong&gt;，考虑它的&lt;strong&gt;本质&lt;/strong&gt;。&lt;strong&gt;抽象&lt;/strong&gt;的意思是指把不重要的属性略去不考虑，重点考量它对客户、对系统的价值和存在的意义。&lt;strong&gt;本质&lt;/strong&gt;指的是那些固有的、不容易发生变化的东西。譬如对于人来说性格不容易改变，所以把握住司马懿多疑的性格，就大概可以料到摆个空城计能令他不敢攻城。同样，如果你能把握住业务中每个对象对客户或系统的价值和存在的意义，能看透哪一部分是固有的、不易变化的，那么，在外人看来你大概也有了料敌机先的本事。&lt;br /&gt;说起来容易，真要操作起来还有几个难题。1）到底应该从什么时候开始，又从何处入手呢？2）哪些是重要的、什么是本质的，可谓仁者见仁，很难找到评判的依据和标准；3）对一个东西的抽象，可以有无数个方向、无数个层次。就像故事里说的明代那个叫王守仁的大牛，年轻的时候立志要成为圣贤，所以决定实践一下朱大圣人的&amp;ldquo;格物致知&amp;rdquo;，每天在自家后院&amp;ldquo;格&amp;rdquo;竹子，无论风吹雨淋，一直盯着竹子&amp;ldquo;格&amp;rdquo;，后来竹子没格成功，自己先得了感冒。&lt;br /&gt;后面两个问题无法可想，它们大概属于&amp;ldquo;艺术&amp;rdquo;的范畴。对于第一个问题倒是有个不错的建议&amp;mdash;&amp;mdash;当遇到&lt;strong&gt;矛盾&lt;/strong&gt;的时候，就是一个很好的契机。&lt;br /&gt;看一个具体的例子。如果你去医院看过病，一定对交了费之后拿到的那一大堆收据不陌生。当然，我们一般也不会仔细看它们，都是到了医生那里就把一堆收据全给人家，等人家捡走几张之后再把剩下的揣到自己兜里。所以现在让领域专家给我们讲讲吧。&lt;br /&gt;&amp;ldquo;这种收据，一般都是&amp;lsquo;3联&amp;rsquo;，也就是3张大小、格式全都一样的纸叠在一起，纸张之间有复写功能，由针式打印机打印之后3张纸就有了一模一样的内容。这3张纸，第一联叫存根联，由收款员留着；第二联叫通知联，由执行科室（也就是负责执行医嘱的部门，像放射线室、化验室、门诊药局等等）留着，所以这一联也叫执行联；第三联叫报销凭证，由患者留着。&lt;br /&gt;&amp;ldquo;每张收据有一个收据号，收据是一张接着一张连续打印的。对于非药医嘱，每个医嘱一张收据；对于药品医嘱，每个处方（一个处方可能包含1～5种药品）一张收据。收据上打印的主要内容是医嘱名称、数量、金额和整张收据的总金额。&lt;br /&gt;&amp;ldquo;需要注意的是，上面说的是针对自费患者的规则。如果是医保患者，则变成所有医嘱的总费用打印在第一张收据上，并注明此收据为&amp;lsquo;医保总联收据&amp;rsquo;，专门用于报销；然后再接着打印执行联，其规则是每个非药医嘱一张执行联，每个处方一个执行联。因为执行联也是打印在收据纸上的，所以还要注明&amp;lsquo;这是执行联，不可用于报销&amp;rsquo;的字样&amp;hellip;&amp;hellip;&amp;rdquo;领域专家说完之后有点小得意地瞟了程序员一眼。&lt;br /&gt;&amp;ldquo;我艹，这都神马乱七八糟的&amp;hellip;&amp;hellip;&amp;rdquo;程序员听到这儿都快崩溃了，&amp;ldquo;不但非药医嘱和药品医嘱的处理方式不一样，自费患者和医保患者的处理方式相差的更多，这要怎么搞？&amp;rdquo;&lt;br /&gt;说实在的，模型变得臃肿和难以理解很大程度上是拜各种&amp;ldquo;其它情况&amp;rdquo;所赐。但是，这些都是每天正在发生着的实际业务。如果说&amp;ldquo;存在就有它的理由&amp;rdquo;的话，要是我们能把这些理由搞明白，这反而可以成为加深理解的好机会。&lt;br /&gt;要从哪里开始呢？就从最明显的不一致的地方开始：我们可以发现对于自费患者，收据和执行联是一模一样的（因为是一次打印3联，无论票据号还是内容都完全相同）；对于医保患者，收据和执行联是分开的，且票据号和内容都不一样。这暗示我们收据和执行联可能是2个不同的概念。&lt;br /&gt;从价值和存在的意义考虑，收据的作用是1）作为患者已经交了费用的证明；2）报销的依据。执行联的作用是 1）表明患者已经交了费可以执行医嘱；2）指示医院的执行科室应该执行哪些医嘱。&lt;br /&gt;再抽象一点从拓扑的角度考虑，可以认为收据和执行联都是对医嘱（所对应的收费项目）的分组，只是分组的方法不一定相同。一个重要的问题是这两种分组方法是否有必然的联系？&lt;br /&gt;再回过头来仔细考虑那些规则，看看有多少非本质的因素。当然就像前面所讨论的，是不是本质的很大程度上是主观的、相对的。可以认为，&amp;ldquo;X光检查和验血不应该打印到同一张执行联上&amp;rdquo;是本质的，因为这两项医嘱需要在不同的执行科室执行，如果打在一张执行联上，难不成要把执行联撕成两片吗？但是&amp;ldquo;每个非药医嘱一张执行联&amp;rdquo;分明是因为程序员想偷懒&amp;mdash;&amp;mdash;判断哪些医嘱属于同一个执行科室有点麻烦，还要考虑医嘱太多的话一张小小的票据可能打不下，另外这样做会使退费更容易处理（退费是另一个有点复杂的主题，本文限于篇幅不再讨论）。&amp;ldquo;对于医保患者，所有医嘱的总费用打印在第一张收据上&amp;rdquo;可能是因为医保中心要求这么做，也可能仅仅因为分几组进行医保结算操作技术上很麻烦。&lt;br /&gt;经过一些分析之后，不难得出结论。需要把医嘱（所对应的费用明细）分组打印到票据（收据和执行联）上是本质的（当然将来随着医院和全社会无纸化水平越来越高，这些票据也会逐步消失）。生成这两种票据的分组规则可能完全相同，也可能有很大的不同，这取决于技术上的限制或实现上的方便，它们之间没有必然的联系。&lt;/p&gt;&lt;p&gt;&lt;img alt="" src="http://pic002.cnblogs.com/images/2011/25284/2011090123270949.png" /&gt;&lt;/p&gt;&lt;p&gt;在创建票据的时候，我们给出两种分组算法：GroupMethod1&amp;mdash;&amp;mdash;对于非药医嘱，每个医嘱一组；对于药品医嘱，每个处方一组；GroupMethod2&amp;mdash;&amp;mdash;所有医嘱一组。对于自费患者，使用 GroupMethod1 创建收据和执行联；对于医保患者，使用 GroupMethod2 创建收据，使用 GroupMethod1 创建执行联。打印票据的时候，自费患者只打印收据；医保患者先打印收据，再接着打印执行联。&lt;br /&gt;因为收据和执行联的属性几乎一样，所以实际实现的时候也可以把收据和执行联用一个叫&amp;ldquo;票据&amp;rdquo;的实体来表示。另外，由于普通患者的收据和执行联的所有属性值都相同，持久化两条几乎一模一样的数据好像也没什么意思，可以暂且先不创建自费患者的执行联。至于要不要使用子类、弄个 factory 什么的，可以根据实际情况再做决定，本文限于篇幅不再讨论，但是至少应该把那两个分组算法和创建票据的代码封装到函数之中。&lt;/p&gt;&lt;p&gt;&lt;img alt="" src="http://pic002.cnblogs.com/images/2011/25284/2011090123403527.png" /&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;span style="color: #008000;"&gt;功夫在模型外&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;再说一点题外话。当客户要求我们实现很复杂的功能时，一定要小心&amp;mdash;&amp;mdash;那可能只是客户脑袋发热，没有想到可以有更简单明了的解决问题的方法。这时候，即使可以假设开发团队的水平相当高，可以又快又好的把程序搞出来，仍然还要面对两个问题：1）程序太复杂了，想要向客户证明程序是光荣伟大而正确的很困难，客户需要经过很长时间才能信任程序，这预示着更大的培训和维护成本；2）客户可能没有程序员那么聪明，特别是需要多个部门的基础、利益各不相同的众多用户协作的时候&amp;mdash;&amp;mdash;他们用不明白那么复杂、微妙的程序，最后只能使用最简单的那一部分&amp;mdash;&amp;mdash;程序员应该对程序复杂性的边界心中有数，可以感觉到&amp;ldquo;这个要越线了&amp;rdquo;。这时候，如果程序员能给出更简单的方法，客户反而会感谢你，取得双赢的结果。这也可以说是&amp;ldquo;功夫在模型外&amp;rdquo;。相信将来企业管理软件的开发会与管理咨询一同进行，由目前的业务驱动开发逐渐转变为信息化建设促进管理改进。&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;建模心法6&lt;/strong&gt;&amp;nbsp; 帮助客户找到既简单又有效的解决方案。&lt;br /&gt;&lt;br /&gt;这一条要求程序员在更高的层面上思考业务的本质。直觉上可能会觉得这已经和程序无关了，或者至少程序员不是最适合干这个的人选，但是我不这么认为。有句话说得好&amp;ldquo;想知道自己是不是真正理解了就用程序去实现它&amp;rdquo;，如果程序员已经成功地创造了一个信息系统来仿真真实业务，他对业务的理解一定比普通的业务人员更深刻、更全面，再加上他又是那么的聪明伶俐，如果他愿意更进一步的话，谁能拦得住？&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/1-2-3/113933/o_logo2_170_r.png" width="170" height="66" /&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/1-2-3/aggbug/2155353.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/1-2-3/archive/2011/09/05/model-method-part3.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/1-2-3/archive/2011/08/13/diary-2011-08-08.html</id><title type="text">[日记]游长白遇梅花，植物大战僵尸</title><summary type="text">2011年8月6、7、8，和东芝的老朋友们一起去游长白山。</summary><published>2011-08-13T15:22:00Z</published><updated>2011-08-13T15:22:00Z</updated><author><name>1-2-3</name><uri>http://www.cnblogs.com/1-2-3/</uri></author><link rel="alternate" href="http://www.cnblogs.com/1-2-3/archive/2011/08/13/diary-2011-08-08.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/1-2-3/archive/2011/08/13/diary-2011-08-08.html"/><content type="html">&lt;p&gt;&amp;ldquo;旅客朋友们&amp;mdash;&amp;mdash;&amp;rdquo;我因为坐在面包车副驾驶的位置，所以假装自己是导游，&amp;ldquo;您即将经过的景点是&amp;lsquo;董眼镜修车铺&amp;rsquo;，请向车窗的左侧观看&amp;hellip;&amp;hellip;&amp;rdquo;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #008000;"&gt;&lt;strong&gt;董眼镜修车铺&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;img style="float: right;" src="http://images.cnblogs.com/cnblogs_com/1-2-3/316160/o_IMG_0284_40.JPG" alt="" width="384" height="288" /&gt;&amp;ldquo;董眼镜修车铺&amp;rdquo;是怎么回事呢？原来，我们昨天经过这附近的时候爆了胎。从长白山西坡到长白县要3个多小时的车程，大段大段的在修路，差不多 2/3 都是土路，坑坑洼洼的，有些地段铺满了碎石子。我们刚刚行进了不到 1/4 的路程，就被小石子扎爆了左后轮。&lt;br /&gt;那是我跟东芝的老朋友们出来旅行的第二天。我们刚刚从西坡登上长白山的峰顶，很幸运地看到了天池。下午 4 点钟左右，阳光正灿烂，司机换备胎累得满头大汗，我只能帮忙压了几下千斤顶。备胎不宜使用太长时间，所以还得找修车铺补胎。开着车在附近找到一个村子，转了3家修车铺，都对我们摇了头。他们有的说&amp;ldquo;董眼镜！&amp;rdquo;，有的指着门外说&amp;ldquo;去，董眼镜！&amp;rdquo;。于是&amp;ldquo;董眼镜&amp;rdquo;成了我们心目中一个好似银河抵抗组织基地一般既神秘又好玩的所在。其实董眼镜的修车铺就在公路的旁边，挺破烂的一间瓦房，里面有火炕和一台老式电视机。我们大大咧咧地上了炕（后来想想好像真正算是上了炕的只有宁宁），开着各种好玩的玩笑，比如扎爆了我们轮胎的小石子其实是董眼镜的徒弟们故意扔的等等，很欢乐。补胎需要不少时间，以至于我们可以去附近的村子里上趟厕所，还买了雪糕吃。董眼镜是个 60 来岁剃着平头的小老头，皮肤黑黑的，确实带着副大大的眼镜。&amp;ldquo;我不干这么没技术含量的活儿&amp;rdquo;董眼镜说，&amp;ldquo;平时都是我老婆负责补胎的&amp;rdquo;。也许他没吹牛，过道上摆得满满的车床、电焊机和架子上好几本技术书都是证明。另一间屋子里则摆满了各种汽车的配件，我们都说董眼镜其实是这一片修车行里的老大呢。&lt;br /&gt;就像圣埃克絮佩里说的，&amp;ldquo;当你在长长的公路上找到了一间修车铺，度过了本该不怎么愉快的一个小时，留下了欢声笑语，看过了修车师傅的全家福之后，对于你来说，它就不再是一间普通的修车铺，对于它来说，你也不再是一名普通的游客。以后，每当你看到它，都会在心底泛起微笑&amp;rdquo;。&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="color: #008000;"&gt;夜行&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;img style="float: right;" src="http://images.cnblogs.com/cnblogs_com/1-2-3/316160/o_IMG_0368.JPG" alt="" width="400" height="300" /&gt;由于修车耽搁了一个多小时，我们不得不早早地开始走夜路。我是真的喜欢夜路！山路窄窄的，车灯投射在地面上，形成一片小小的淡黄色光芒，周围则渐渐没入黑暗。有时会遇到浓烟和雾气，蜿蜒曲折的山路，好像永远也到不了头似的。路上静悄悄地，很难遇到对头车。黑暗包裹着，你就像一只孤单的萤火虫。有时，飞蛾迎着车头飞过，在车灯的映照下闪着金黄色的光芒，黑夜也平添了一丝绚丽和神秘。如果你的眼神好，偶尔能看见有金色的小东西在地上一跳一跳的，那是蛤蟆想要横过马路，到另一侧的鸭绿江边去。半个月亮很明亮，星空清晰可见。路两旁黑压压的原始森林向你压过来。我心想，要睁大眼睛注意看有没有停在路边还不亮灯的危险车辆&amp;hellip;&amp;hellip;&lt;br /&gt;&amp;ldquo;啊呀！好危险！还真有这么无德的车！&amp;rdquo;我们刚转过一个弯角，就看到一辆黑色的轿车停在路边，两个模糊的人影正在后备箱那里忙活着什么。&amp;ldquo;那辆车&amp;hellip;&amp;hellip;&amp;rdquo;司机过了几秒钟才说，&amp;ldquo;八成是干走私的，因为公路那边就是朝鲜，他们有铜和铁，我们用粮食交换，有时也会走私从日本过来的毒品&amp;mdash;&amp;mdash;冰毒一类的。&amp;rdquo;后来，这样的车我们陆陆续续看到了 3 辆。司机是本地人，朝鲜族，32岁，个子不高，瘦瘦的，既开朗又自信。&amp;ldquo;路的左边隔着鸭绿江就是朝鲜。注意看那些像萤火虫似的亮点，那就是村民的房子，灯光有些暗，因为都是二三十瓦的老式灯泡。&amp;rdquo;我们没请导游，全靠热情的司机给我们介绍，&amp;ldquo;再往前开一段就是朝鲜第三大城市惠山，明天我们返程时可以看一下。&amp;rdquo;&lt;br /&gt;&amp;ldquo;师傅你这条路很熟吧？&amp;rdquo;开始了下山路的连续弯道，司机居然都不用刹车的，弯来转去的公路像一条大蛇扭动着斑驳的腰身，怎么都觉得像是在玩极品飞车一类的游戏。&amp;ldquo;啊&amp;hellip;&amp;hellip;&amp;rdquo;司机听到我这么问好像有一点惊讶，过了好一会儿才回答我说，&amp;ldquo;是挺熟啊，这条路哪里有坑、哪里有包我全都知道。&amp;rdquo;&lt;br /&gt;有那么几个瞬间，我甚至希望永远不要到达终点。我希望&amp;hellip;&amp;hellip;被大雨耽搁在路上；滚石摧毁铁轨；台风阻断一切交通。也许&amp;hellip;&amp;hellip;司机会突然不省人事&amp;mdash;&amp;mdash;就让我们永远迷失在旅途上吧。&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="color: #008000;"&gt;就算只看到一片乌云，那也是天池上空的乌云啊&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;img style="float: right;" src="http://images.cnblogs.com/cnblogs_com/1-2-3/316160/o_IMG_0307_40.JPG" alt="" width="288" height="384" /&gt;我的乌鸦嘴向来有点名气。昨天刚说这次唯一的一点遗憾是新买的旅行专用雨衣和一次性鞋套没用上，早上就下起了小雨，我们披上雨衣穿行在十五道沟（官方名称是望天鹅峡谷）的小桥和木质栈道上。峡谷里有一条挺漂亮的小河，两侧的山上有好几个大大小小的瀑布浇灌着青草和绿树。游客很稀少，实际上，很难遇到别的游客，拍起照来很过瘾。有点可惜的是景点有些少，只用了2个小时就游遍了。门口景区地图上的景点有一半都是灰色的未开放状态，不知道是不是因为它是新开发的景区，还没有完全完工。&lt;br /&gt;我们按计划接着赶到长白山南坡，想要再看一次天池，却收到坏消息&amp;mdash;&amp;mdash;受台风&amp;ldquo;梅花&amp;rdquo;的影响，山顶刮起了大风，门票全部停售（据说风力强到把一辆汽车的车门都吹坏了）。我们都暗自庆幸幸亏昨天看到了天池，但是其它刚刚赶到的游客就惨了，有一个旅行团找了景区领导，非要买票进门不可。那边的答复是想进去也可以，但是所有景区全部关闭，门票售出不退。即使这样他们也愿意进山，还有一个女游客悲壮地说&amp;ldquo;就算只看到一片乌云，那也是天池上空的乌云啊&amp;rdquo;。希望和失望的力量多么强大！它们两个加在一起的杀伤力更是超乎想象。&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="color: #008000;"&gt;绿皮火车&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;晚上，回程的火车没有买到卧铺，好在提前买好了票，有座&amp;mdash;&amp;mdash;13 个小时的车程，总好过站票。老雷他们凑了一桌打扑克。宁宁和我因为很悲惨地明早还要上班，早早地眯起眼睛打盹。这是古老的绿皮火车。我想起了周云蓬的《绿皮火车》。我虽然没有他那么多姿多彩的经历，但是也同样从小就喜欢自己坐火车出去玩。那脏兮兮的地面，局促的空间，深绿色的座椅，还有偶尔钻进鼻孔的厕所味道，都隐隐地勾起了一丝熟悉的旅愁。座椅虽然看上去很宽大，但是靠背太直了，想要打个盹还真是不容易，脖子疼的像是要折了。我一个盹下去，脑袋啪嚓一下砸在宁宁的头上，心里很过意不去，于是把脑袋稍稍偏向珊珊这边一点。过了一会儿，不知怎么搞的啪嚓一下又砸在宁宁的脑袋上。后来，早上的时候，我问她，她却什么也不记得了。&lt;br /&gt;靠窗的桌子很小，宁宁在上面铺了一件厚衣服，趴在上面貌似睡得挺舒服。我琢磨着桌子还剩 20 厘米的一小块地方，也够我趴在上面了。多亏我上学的时候天天练习趴在桌子上睡觉，这么小的地方也能掌握平衡，还做了一个离奇的梦，梦见章弦和&amp;hellip;&amp;hellip;算了，为了他的性命着想就忍着不说了。&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="color: #008000;"&gt;植物大战僵尸&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;早上醒来，居然有一点神清气爽的感觉。背着旅行包直接赶到医院。同事们见了我都说&amp;ldquo;你这个豌豆射手T恤很漂亮呀&amp;rdquo;。我告诉他们说这次旅行我们每个人都事先在淘宝上买了一件植物大战僵尸T恤&amp;mdash;&amp;mdash;有的是植物，有的是僵尸。小孩子们见了我们都会抢着说&amp;ldquo;这个是豌豆射手，那个是寒冰射手&amp;hellip;&amp;hellip;&amp;rdquo;，等哪天我把合影给你们看看。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/1-2-3/aggbug/2137586.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/1-2-3/archive/2011/08/13/diary-2011-08-08.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/1-2-3/archive/2011/07/31/ExtJs4-SASS-Custom-Theme.html</id><title type="text">ExtJs4 与 SASS 的亲密接触——使用 SASS 自定义主题</title><summary type="text">“Sass makes CSS fun again”，我特别喜欢 SASS 网站上的这个口号。SASS 能不能让 ExtJs 也变得更加有趣呢？</summary><published>2011-07-31T03:48:00Z</published><updated>2011-07-31T03:48:00Z</updated><author><name>1-2-3</name><uri>http://www.cnblogs.com/1-2-3/</uri></author><link rel="alternate" href="http://www.cnblogs.com/1-2-3/archive/2011/07/31/ExtJs4-SASS-Custom-Theme.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/1-2-3/archive/2011/07/31/ExtJs4-SASS-Custom-Theme.html"/><content type="html">&lt;p&gt;SASS 是一个在 Ruby 社区兴起的样式语言，支持嵌套、变量、混入、继承等等语言特性，通过命令行工具或Web框架插件生成标准的 CSS 样式（参加SASS主页&lt;a href="http://sass-lang.com/"&gt;http://sass-lang.com/&lt;/a&gt;）。使用 SASS 定义的样式代码更少、更清晰，更重要的是减少重复代码，符合 DRY 原则。让人高兴的是，ExtJs4 已经转向 SASS，你可以在 ext-4.0.2a\resources\sass\ 找到所有 ExtJs 样式的 SASS 版本。&lt;br /&gt;由于 SASS 支持变量，所以修改/覆盖样式变得更加容易。例如，只要把 ExtJs 预定义的一个名为 $base-color 的变量重新赋值为 #a1c148（绿色），所有 Ext 控件的底色就会全都变成绿色了。在 ext-4.0.2a\resources\themes\stylesheets\ext4\default\variables\ 文件夹里可以找到所有预定义的变量。&lt;/p&gt;&lt;p&gt;&lt;img alt="" src="http://pic002.cnblogs.com/images/2011/25284/2011073021343525.png" /&gt;&lt;/p&gt;&lt;p&gt;下面我们一步一步地演练一下使用 SASS 自定义样式的全过程。&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #008000;"&gt;&lt;strong&gt;准备工作&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Step1&lt;/strong&gt; 下载 ExtJs4. &lt;a href="http://www.sencha.com/products/extjs/download"&gt;http://www.sencha.com/products/extjs/download&lt;/a&gt;&lt;br /&gt;&lt;strong&gt;Step2&lt;/strong&gt; 下载 Ruby。在 &lt;a href="http://www.ruby-lang.org/en/downloads/"&gt;http://www.ruby-lang.org/en/downloads/&lt;/a&gt;&amp;nbsp;页，如果你是 Windows 用户应该下载&amp;ldquo;Ruby on Windows&amp;rdquo; 节下的&amp;ldquo;&lt;a href="http://rubyforge.org/frs/download.php/74298/rubyinstaller-1.9.2-p180.exe" target="_blank"&gt;Ruby 1.9.2-p180 RubyInstaller&lt;/a&gt;&amp;rdquo;。下载后将 Ruby 安装到 D:\Ruby192\。为方便使用Ruby命令行工具，安装时建议勾选&amp;ldquo;Add Ruby executables to your PATH&amp;rdquo;选项。&lt;br /&gt;&lt;strong&gt;Step3&lt;/strong&gt; 安装 Compass/SASS。打开 Windows 命令行，执行&lt;br /&gt;gem install compass&lt;br /&gt;安装后，可使用命令&lt;br /&gt;compass &lt;span class="sy0"&gt;-&lt;/span&gt;v&lt;br /&gt;sass &lt;span class="sy0"&gt;-&lt;/span&gt;v&lt;br /&gt;检验是否已成功安装了 compass 和 sass。&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #008000;"&gt;&lt;strong&gt;创建网站，规划目录&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Step4&lt;/strong&gt; 创建一个 MVC 网站，添加一个名为 MyThemDemoController 的控制器，以及配套的 Views\MyThemDemo\MyThemDemo.aspx 和 Scripts\Demo\MyThemDemo.js。把 Step1 中下载的 ext-4.0.2a 复制到网站的 Scripts 目录下。在 IIS 中创建网站，我把端口设成了 6000，你如果想偷懒的话可以下载我建好的整个 &lt;a href="http://files.cnblogs.com/1-2-3/ExtJsThemeDemo.rar" target="_blank"&gt;Demo 项目&lt;/a&gt;，但是要注意 1）别忘了在IIS中建网站，端口是 6000，2）ext-4.0.2a 一共 128M，我为了节省上传、下载时间没有放进去，3）Demo 页 URL 是 &lt;a href="http://localhost:6000/MyThemDemo/MyThemDemo"&gt;http://localhost:6000/MyThemDemo/MyThemDemo&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Step5&lt;/strong&gt; 把 Scripts\ext-4.0.2a\resources\themes\templates\resources 文件夹复制到 Scripts 文件夹下。把 Scripts\ext-4.0.2a\resources\themes\images\default 文件夹复制到 Scripts\resources 文件夹下，并把它由 &amp;ldquo;default&amp;rdquo;重命名为&amp;ldquo;images&amp;rdquo;。之后你的目录结构应该如下图所示。&lt;/p&gt;&lt;p&gt;&lt;img alt="" src="http://pic002.cnblogs.com/images/2011/25284/2011073110511462.png" /&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;br /&gt;&lt;span style="color: #008000;"&gt;由 SASS 生成 CSS&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Step6&lt;/strong&gt; 修改 Scripts\resources\sass\config.rb 中的 $ext_path 变量，使其指向 ExtJs 根目录。&lt;br /&gt;$ext_path = ".. /.. /ext-4.0.2a"&lt;br /&gt;注意".."和"/"之间不要有空格，天杀的博客园居然会把 &amp;ldquo;.. /.. /&amp;rdquo;替换为&amp;ldquo;http://www.cnblogs.com&amp;rdquo;，逼得我没办法只好加了个空格进去 囧&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Step7&lt;/strong&gt; 修改 Scripts\ext-4.0.2a\resources\themes\lib\utils.rb 第 62 行，由&lt;br /&gt;images_path = File.join($ext_path, 'resources', 'themes', 'images', theme) &lt;br /&gt;修改为&lt;br /&gt;images_path = relative_path&lt;br /&gt;这样 ExtJs 将使用 Scripts\resources\images 下的图片而不是 Scripts\ext-4.0.2a\resources 下的图片。&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Step8&lt;/strong&gt; 打开 Windows 命令行，进入 Scripts\resources\sass 目录下，执行命令&lt;br /&gt;&amp;gt; compass compile&lt;br /&gt;此命令会在 Scripts\resources\css 文件夹下生成 my-ext-theme.css 文件。&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #008000;"&gt;&lt;strong&gt;修改 $base-color 变量，查看效果&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Step9&lt;/strong&gt;&amp;nbsp;编辑 Scripts\resources\sass\my-ext-theme.scss，在 &lt;a href="mailto:&amp;ldquo;@import"&gt;&amp;ldquo;@import&lt;/a&gt; 'ext4/default/all';&amp;rdquo; 这一行之前插入一行&lt;br /&gt;&amp;nbsp;$base-color: #a1c148;&lt;br /&gt;&lt;strong&gt;注意&lt;/strong&gt; 对变量的重新赋值的语句都应该放在 &lt;a href="mailto:&amp;ldquo;@import"&gt;&amp;ldquo;@import&lt;/a&gt; 'ext4/default/all';&amp;rdquo; 这一行之前。&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Step10&lt;/strong&gt; 再次打开 Windows 命令行，进入 Scripts\resources\sass 目录下，执行命令&lt;br /&gt;&amp;gt; compass compile&lt;br /&gt;重新生成CSS文件。&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Step11&lt;/strong&gt; 编辑 MyThemDemo.aspx，添加对 my-ext-theme.css 等文件的引用。&lt;/p&gt;&lt;p&gt;&lt;img alt="" src="http://pic002.cnblogs.com/images/2011/25284/2011073111443597.png" /&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Step12&lt;/strong&gt; 编辑 MyThemDemo.js，把 Scripts\ext-4.0.2a\examples\themes\themes.js 里的内容全部复制到 MyThemDemo.js 里面。&lt;br /&gt;&lt;strong&gt;Step13&lt;/strong&gt; 打开浏览器，进入 &lt;a href="http://localhost:6000/MyThemDemo/MyThemDemo"&gt;http://localhost:6000/MyThemDemo/MyThemDemo&lt;/a&gt;&amp;nbsp;即可看到效果了。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;但是如何能够实时更改样式呢？（以便达到&lt;a href="http://www.screencast.com/users/sroussey/folders/Jing/media/abbaafdc-82d6-4431-81b0-832d6729266c" target="_blank"&gt;这个视频&lt;/a&gt;所演示的效果），偶也不知道。你要是知道的话请告诉我！&lt;br /&gt;本文主要内容来自&lt;a href="http://www.sencha.com/learn/theming/"&gt;http://www.sencha.com/learn/theming/&lt;/a&gt;。&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/1-2-3/113933/o_logo2_170_r.png" width="170" height="66" /&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/1-2-3/aggbug/2120373.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/1-2-3/archive/2011/07/31/ExtJs4-SASS-Custom-Theme.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/1-2-3/archive/2011/07/12/nhibernate-guid-key-performance.html</id><title type="text">[NHibernate] Guid 作主键速度超慢的背后</title><summary type="text">从一个70万条的表里面取一条匹配Id的数据居然需要3.5秒！性能瓶颈到底是 NHibernate 还是 .net？难道真的不能用字符串做主键？问题又该如何解决呢？</summary><published>2011-07-12T14:32:00Z</published><updated>2011-07-12T14:32:00Z</updated><author><name>1-2-3</name><uri>http://www.cnblogs.com/1-2-3/</uri></author><link rel="alternate" href="http://www.cnblogs.com/1-2-3/archive/2011/07/12/nhibernate-guid-key-performance.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/1-2-3/archive/2011/07/12/nhibernate-guid-key-performance.html"/><content type="html">&lt;p&gt;最近遇到了一个让人抓狂的性能问题。生产环境里有一张表的数据量目前达到了 70 万条。结果发现无论是匹配主键的查询还是更新，执行一条语句居然需要 3.5 秒！如果把 NH Prof 中截获的 SQL 语句拿到 PL/SQL Developer 里执行，就只需几十毫秒。一开始还以为是NH的问题，后来发现其实另有隐情。&lt;br /&gt;介绍一下环境先。数据库使用 Oracle10g，所有字符类型的字段都是 varchar2 &lt;a href="#r1"&gt;[1]&lt;/a&gt;。所有的主键都使用 Guid，在数据库里是 varchar2(36) 类型，相应的，实体的 Id 属性的类型是 string。ORM 使用的是 NHibernate 2.1.0 和 FluentNHibernate1.1。&lt;br /&gt;经过一番排查之后发现，问题的根源是 NH 将 SQL 语句传递给 Oracle 时，所有字符型的参数都是 nvarchar2 类型，而数据库里对应的字段却是 varchar2 类型，这将导致 Oracle 无法使用索引，终于造成全表扫描，所以数据量稍大就慢得不行。&lt;br /&gt;第一种解决方法是，把数据库中所有的字符型字段的类型由 varchar2 更改为 nvarchar2，出于种种原因我们不希望这么做。&lt;br /&gt;第二种解决方法是，让 NH&amp;nbsp;把 varchar2 作为参数类型传递给 Oracle。&lt;br /&gt;事实上，NH 默认把 .net 的 string 映射为 DbType.String &lt;a href="#r2"&gt;[2]&lt;/a&gt;，把 DbType.String 映射为 nvarchar2 &lt;a href="#r3"&gt;[3]&lt;/a&gt;。把 DbType.AnsiString 映射为 varchar2 &lt;a href="#r4"&gt;[4]&lt;/a&gt;。&lt;br /&gt;所以对于查询比较简单，只要把 HQL 的参数类型指定为 AnsiString 就行了。&lt;/p&gt;&lt;div class="cnblogs_Highlighter"&gt;&lt;pre class="brush:csharp;gutter:false;"&gt;var query = Session.CreateQuery(@"select t from Region as t                                   where t.Id = :Id")                     .SetAnsiString("Id", id);&lt;/pre&gt;&lt;/div&gt;&lt;div class="cnblogs_Highlighter"&gt;&lt;pre class="brush:csharp;gutter:false;"&gt;var query = Session.CreateQuery(@"select t from Region as t                                   where t.Id in (:Ids)")                     .SetParameterList("Ids", ids.ToList(), NHibernateUtil.AnsiString);&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;但是如何设置 Update 和 Delete 语句的参数类型呢？这里有个小小的秘技，把映射文件里的属性类型指定为&amp;ldquo;AnsiString&amp;rdquo;即可。&lt;/p&gt;&lt;div class="cnblogs_Highlighter"&gt;&lt;pre class="brush:csharp;gutter:false;"&gt;public class RegionMap : TreeNodeMap&amp;lt;Region&amp;gt;{    public RegionMap()    {        Table("INFRA_REGION");        Id(t =&amp;gt; t.Id, "REGION_ID").CustomType("AnsiString");        ...    }}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt; 一定要使用 CustomType() 而不是 CustomSqlType()。&lt;br /&gt;当然了，要是把每一个配置文件都改一遍实在很烦，好像项目使用了 Fluent&amp;nbsp; NHibernate，只要添加一个 IdConvention 就行了。&lt;/p&gt;&lt;div class="cnblogs_Highlighter"&gt;&lt;pre class="brush:csharp;gutter:false;"&gt;public class IdConvention : FluentNHibernate.Conventions.IIdConvention{    public void Apply(FluentNHibernate.Conventions.Instances.IIdentityInstance instance)    {        instance.CustomType("AnsiString");    }}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;想要彻底一点的话，可以再加一个 string 类型的 property 的 convention。&lt;/p&gt;&lt;div class="cnblogs_Highlighter"&gt;&lt;pre class="brush:csharp;gutter:false;"&gt;public class StringPropertyConvention : IPropertyConvention, IPropertyConventionAcceptance{    public void Accept(IAcceptanceCriteria&amp;lt;IPropertyInspector&amp;gt; criteria)    {        criteria.Expect(x =&amp;gt; x.Property.PropertyType == typeof(string));    }    public void Apply(IPropertyInstance instance)    {        instance.CustomType("AnsiString");    }}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;把这两个 Convention 加到配置里面：&lt;/p&gt;&lt;div class="cnblogs_Highlighter"&gt;&lt;pre class="brush:csharp;gutter:false;"&gt;Session["SessionFactory"] = Fluently.Configure()          .Database(OracleClientConfiguration.Oracle10              .Dialect&amp;lt;Oracle10gDialect&amp;gt;()              .ConnectionString("User ID=iBlast;Password=不可说;Data Source=Moki")              .QuerySubstitutions("true 1, false 0, yes 'Y', no 'N'")              .UseOuterJoin()              .ProxyFactoryFactory&amp;lt;ProxyFactoryFactory&amp;gt;()              .AdoNetBatchSize(1000)              .Driver&amp;lt;OracleClientDriver&amp;gt;())          .Mappings(m =&amp;gt; { m.HbmMappings.AddFromAssembly(Assembly.Load("Infrastructure.Repositories"));                            m.FluentMappings.AddFromAssembly(Assembly.Load("Infrastructure.Repositories"))                                           .Conventions.Add&amp;lt;EnumConvention&amp;gt;()                                           .Conventions.Add&amp;lt;HasManyConvention&amp;gt;()                                           .Conventions.Add&amp;lt;HasManyToManyConvention&amp;gt;()                                           .Conventions.Add&amp;lt;StringPropertyConvention&amp;gt;()                                           .Conventions.Add&amp;lt;IdConvention&amp;gt;()                                           .ExportTo(@"F:\temp\"); })          .BuildSessionFactory();&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;注意倒数第二行的 .ExportTo(@"F:\temp\") 是为了测试一下生成的映射文件对不对而把映射文件输出到了 &amp;ldquo;F:\temp\&amp;rdquo;，映射文件应该像这个样子：&amp;nbsp;&lt;/p&gt;&lt;div class="cnblogs_Highlighter"&gt;&lt;pre class="brush:csharp;gutter:false;"&gt;&amp;lt;hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true"&amp;gt;  &amp;lt;class xmlns="urn:nhibernate-mapping-2.2" dynamic-insert="true" dynamic-update="true" mutable="true" where="IsDelete=0" name="Dawn.HIS.Infrastructure.Core.Data.Region, Infrastructure.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="INFRA_REGION"&amp;gt;    &amp;lt;id name="Id" type="AnsiString"&amp;gt;      &amp;lt;column name="REGION_ID" /&amp;gt;      &amp;lt;generator class="assigned" /&amp;gt;    &amp;lt;/id&amp;gt;    &amp;lt;version name="Version" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"&amp;gt;      &amp;lt;column name="Version" /&amp;gt;    &amp;lt;/version&amp;gt;    &amp;lt;property name="CreateTime" type="System.DateTime, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"&amp;gt;      &amp;lt;column name="CREATETIME" /&amp;gt;    &amp;lt;/property&amp;gt;    &amp;lt;property name="Name" type="AnsiString"&amp;gt;      &amp;lt;column name="NAME" /&amp;gt;    &amp;lt;/property&amp;gt;    ...  &amp;lt;/class&amp;gt;&amp;lt;/hibernate-mapping&amp;gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;a name="r1"&gt;&lt;/a&gt;[1] 之所以使用 varchar2 而不是 nvarchar2，除了考虑 varchar2 可以节省空间之外，主要是为了避免 &lt;a href="http://blog.csdn.net/wh62592855/article/details/4896635" target="_blank"&gt;nvarchar2 排序时的性能问题&lt;/a&gt;。&lt;br /&gt;&lt;a name="r2"&gt;&lt;/a&gt;[2] 见 NHibernate-2.1.0.GA-src\src\NHibernate\Type\TypeFactory.cs 第 197 行。&lt;br /&gt;&lt;a name="r3"&gt;&lt;/a&gt;[3] 见&amp;nbsp;NHibernate-2.1.0.GA-src\src\NHibernate\Dialect\Oracle8iDialect.cs 第 92&amp;nbsp;行。&lt;br /&gt;&lt;a name="r4"&gt;&lt;/a&gt;[4] 见&amp;nbsp;NHibernate-2.1.0.GA-src\src\NHibernate\Dialect\Oracle8iDialect.cs 第&amp;nbsp;88&amp;nbsp;行。&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/1-2-3/113933/o_logo2_170_r.png" width="170" height="66" /&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/1-2-3/aggbug/2102436.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/1-2-3/archive/2011/07/12/nhibernate-guid-key-performance.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry></feed>
