阿里云搜索

阿里云在去年就推出了搜索服务,但是由于一直没有推广,很多人到现在才知道,这其中就包括这个项目最早的负责人、现唱吧创始人陈华 。
陈华告诉腾讯科技,阿里云搜索是一个通用搜索,其功能和百度、搜狗、360搜索一样。他自己2009年4月加入阿里巴巴就是去做搜索,并且从零开始组建了阿里巴巴云搜索的团队,2011年离开阿里后创立了现在的唱吧。“但阿里云搜索是一个比较艰难的产物。”陈华说。
做搜索为防御 附带额外责任
陈华表示,当时阿里做搜索的想法更多是从防御的角度,觉得必须要有一个这样的产品。另外,由于阿里要做云计算,而搜索是云计算最典型的应用,要用到海量数据,所以“顺便”做一个搜索,可以用来测试云计算的可靠性。
一开始阿里云搜索和阿里云计算都属于阿里集团研究院,这两个项目几乎同时启动,可能阿里云云计算项目稍微早一两个月。由于阿里想做的是基于云计算的搜索,搜索需要建立在云计算的基础上,因此阿里云搜索进展比较缓慢。
另外,陈华表示阿里云搜索有额外的责任。因为阿里云搜索的基础引擎会同时被阿里的其他搜索产品使用,比如淘宝搜索和一淘搜索。这就给阿里云搜索增加了很多负担。不过陈华表示,自己不清楚现在这一点是否有变化。
由于阿里集团业务已经非常多,一些部门也有涉及到搜索相关的业务,这就导致阿里做搜索的部门划分很混乱,比如做分词的团队根本不在搜索部门。这些相关团队到后期才逐渐合并到阿里云搜索部门。“直到我离开阿里,搜索部门才基本成型。”陈华说。
云搜索成就云计算
虽然阿里云搜索一直做的很艰难,但是阿里云计算却取得了不错的成绩。陈华现在的唱吧就使用了部分阿里云计算的服务。“我们当时也是一起做的,虽然知道阿里云计算有很多问题,但还是相信它基本的可靠性。”
对于云计算而言,可靠性是最基本也是最重要的指标。但是如果没有用户,云计算的可靠性也无从测算。所以很多时候是阿里云搜索承担了测试阿里云计算可靠性的工作。
在阿里云成立初期,由于可靠性低,即便是阿里集团的一些部门也不愿意使用阿里云的服务。但是阿里云搜索需要基于云计算,所以云计算可靠性问题对阿里云搜索有直接的影响。很多时候陈华所带的搜索团队会去帮云计算的团队修改 bug。
这种云计算和搜索“绑定”的方式,让阿里云计算有了一个最可靠的“合作伙伴”。阿里云搜索的测试也让阿里云计算在可靠性方面不断加强,帮助其在今后获得更多客户。
阿里云搜索可与搜狗匹敌 挑战百度难
阿里云搜索是一个通用搜索,但是很国内主流搜索引擎相比,阿里云搜索目前并没有什么优势。陈华认为,凭借阿里集团的资源,阿里云搜索可以做到和搜狗的市场份额相当,但是要挑战百度就会很困难。
不过,要做好阿里云搜索的前提是,阿里集团要有决心,要对阿里云搜索做出创新,并且还要有一个适合的负责人。
最初做阿里云搜索的时候,阿里集团投入的更多是人力和硬件资源,在市场推广方面并没有给于太多支持。但是现在做搜索引擎,渠道和市场推广已经是重中之重。因此如果要进入搜索市场分一杯羹,阿里云搜索就必须要有投入资源的决心。
在创新方面,阿里云搜索只是有了搜索引擎的基本框架,目前还没有看到什么特殊的优势。搜索质量上两年前接近搜狗,现在搜索质量上仍然有很多改进空间。而搜索功能的创新上有更大的机会,还需要不断尝试。
陈华认为,这里面最重要的是“要找到一个合适的人,比如像搜狗的王小川这样,能为自己去争取资源,并且把这种资源都整合起来,可以直面进攻百度、360、搜狗的人”
目前阿里其实已经有了很多和搜索相关的业务,比如浏览器、输入法和地图。阿里手里也有万网和phpwind,可以快速把搜索框推广出去。如果能将这些资源和产品整合利用,并且投入资源进行推广,阿里在搜索市场完全还可以有所作为的。

js函数定义详解

javascript中有以下这些方法可以定义一个函数。所有这些都是有效的,但是它们在后台如何
实现的则有一些差别。
常用的写法
一般大家都用这个写法来定义一个函数:
CODE:

functionName([parameters]){functionBody};

Example D1:
CODE:

function add(a, b)
{
return a+b;
}
alert(add(1,2)); // 结果 3

当我们这么定义函数的时候,函数内容会被编译(但不会立即执行,除非我们去
调用它)。而且,也许你不知道,当这个函数创建的时候有一个同名的对象也被
创建。就我们的例子来说,我们现在有一个对象叫做“add”(要更深入了解,看
底下函数:对象节。)
匿名函数
我们也可以通过指派一个变量名给匿名函数的方式来定义它
Example D2
CODE:

var add=function(a, b)
{
return a+b;
}
alert(add(1,2)); // 结果 3

这个代码和前一个例子做了同样的事情。也许语法看起来比较奇怪,但它应该更
能让你感觉到函数是一个对象,而且我们只是为这个对指派了一个名称。可以把
它看做和 var myVar=[1,2,3]一样的语句。以这种方式声明的函数内容也一样会
被编译。
当我们指派一个这样的函数的时候,我们并不一定要求必须是匿名函数。在这里
,我作了和ExampleD2一样的事情,但我加了函数名“theAdd”,而且我可以通过
调用函数名或者是那个变量来引用函数
Example D2A
CODE:

var add=function theAdd(a, b)
{
return a+b;
}
alert(add(1,2)); // 结果 3
alert(theAdd(1,2)); // 结果也是 3

使用这种方式来定义函数在面向对象编程中是很有用的,因为我们能像底下这样
使一个函数成为一个对象的属性。
CODE:

var myObject=new Object();
myObject.add=function(a,b){return a+b};
// myObject 现在有一个叫做“add”的属性(或方法)
// 而且我能够象下面这样使用它
myObject.add(1, 2);

我们也能够通过使用运算符new来定义一个函数。这是一个最少见的定义函数的方
式并且并不推荐使用这种方式除非有特殊的理由(可能的理由见下)。语法如下

CODE:

varName=new Function([param1Name, param2Name,...paramNName],
functionBody);

Example D3:
CODE:

var add=new Function("a", "b", "return a+b;");
alert(add(3,4)); // 结果 7

我在这里有两个参数叫做a和b,而函数体返回a和b的和。请注意new Function
(…)使用了大写F,而不是小写f。 这就告诉javascript,我们将要创建一个类
型是Function的对象。 还要注意到,参数名和函数体都是作为字符串而被传递。
我们可以随心所欲的增加参数,javascript知道函数体会是右括号前的最后一个
字符串(如果没有参数,你能够只写函数体)。你没必要将所有东西都写在一行
里(使用或者使用字符串连接符+来分隔长代码)。标记告诉JavaScript在下一
行查找字符串的其余部分。例子如下:
Example D4
CODE:

var add=new Function("a", "b",
"alert" + // 注意 "+"
"('adding '+a+' and ' +b); // 和 ""的不同用法
return a+b;");
alert(add(3,4)); // 结果 7

采用这种方式定义函数会导致函数并没被编译,而且它有可能会比用其它方式定
义的函数要慢。至于为什么,看一下这个代码:
Example D5
CODE:

function createMyFunction(myOperator)
{
return new Function("a", "b", "return a" + myOperator + "b;");
}
var add=createMyFunction("+"); // 创建函数 "add"
var subtract=createMyFunction("-"); // 创建函数 "subtract"
var multiply=createMyFunction("*"); // 创建函数 "multiply"
// test the functions
alert("加的结果="+add(10,2)); // 结果是 12
alert("减的结果="+subtract(10,2)); // 结果是 8
alert("乘的结果="+multiply(10,2)); // 结果是 20
alert(add);

这个有趣的例子创建了三个不同的function,通过实时传递不同的参数来创建一
个新Function。因为编译器没法知道最终代码会是什么样子的,所以new
Function(…)的内容不会被编译。那这有什么好处呢?嗯,举个例子,如果你需
要用户能够创建他们自己的函数的时候这个功能也许很有用,比如在游戏里。我
们也许需要允许用户添加“行为”给一个“player”。但是,再说一次,一般情
况下,我们应该避免使用这种形式,除非有一个特殊的目的。
函数:对象
函数是javascript中的一种特殊形式的对象。它是第一个[b〕类数据类型(class
data type)。这意味着我们能够给它增加属性。这里有一些需要注意的有趣观点

对象的创建
就像刚才提及的,当我们定义一个函数时,javascript实际上在后台为你创建了
一个对象。这个对象的名称就是函数名本身。这个对象的类型是function。在下
面的例子,我们也许不会意识到这一点,但我们实际上已经创建了一个对象:它
叫做Ball。
Example 1
CODE:

function Ball() // 也许看起来有点奇怪,但是这个声明
{ // 创建了一个叫做Ball的对象
i=1;
}
alert(typeof Ball); // 结果 "function"

我们甚至能将这个对象的内容打印出来而且它会输出这个函数的实际代码,
Example2: 点击 alert(Ball);来看看Ball的内容。
属性的添加
我们能够添加给Object添加属性,包括对象function。因为定义一个函数的实质
是创建一个对象。我们能够“暗地里”给函数添加属性。比如,我们这里定义了
函数Ball,并添加属性callsign。
CODE:

function Ball() // 也许看起来有点奇怪,但是这个声明
{ // 创建了一个叫做Ball的对象,而且你能够
} // 引用它或者象下面那样给它增加属性
Ball.callsign="The Ball"; // 给Ball增加属性
alert(Ball.callsign); // 输出 "The Ball"

指针
因为function是一个对象,我们能够为一个function分配一个指针。如下例,变
量ptr指向了对象myFunction。
CODE:

function myFunction(message)
{
alert(message);
}
var ptr=myFunction; // ptr指向了myFunction
ptr("hello"); // 这句会执行myFunction:输出"hello"

我们能够运行这个函数,就好像这个函数名已经被指针名代替了一样。所以在上
面,这行ptr(“hello”); 和myFunction(“hello”);的意义是一样的。
指向函数的指针在面向对象编程中相当有用。例如:当我们有多个对象指向同一
个函数的时候(如下):
Example 4A
CODE:

function sayName(name)
{
alert(name);
}
var object1=new Object(); // 创建三个对象
var object2=new Object();
var object3=new Object();
object1.sayMyName=sayName; // 将这个函数指派给所有对象
object2.sayMyName=sayName;
object3.sayMyName=sayName;
object1.sayMyName("object1"); // 输出 "object1"
object2.sayMyName("object2"); // 输出 "object2"
object3.sayMyName("object3"); // 输出 "object3"

因为只有指针被保存(而不是函数本身),当我们改变函数对象自身的时候,所
有指向那个函数的指针都会发生变化。我们能够在底下看到:
Example 5:
CODE:

function myFunction()
{
alert(myFunction.message);
}
myFunction.message="old";
var ptr1=myFunction; // ptr1 指向 myFunction
var ptr2=myFunction; // ptr2 也指向 myFunction
ptr1(); // 输出 "old"
ptr2(); // 输出 "old"
myFunction.message="new";
ptr1(); // 输出 "new"
ptr2(); // 输出 "new"

指针的指向
我们能够在一个函数创建之后重新分配它,但是我们需要指向函数对象本身,而
不是指向它的指针。在下例中,我将改变myfunction()的内容。
Example 6:
CODE:

function myFunction()
{
alert("Old");
}
myFunction(); // 输出 "Old"
myFunction=function()
{
alert("New");
};
myFunction(); // 输出 "New"

旧函数哪里去了??被抛弃了。
如果我们需要保留它,我们可以在改变它之前给它分配一个指针。
Example 6A:
CODE:

function myFunction()
{
alert("Old");
}
var savedFuncion=myFunction;
myFunction=function()
{
alert("New");
};
myFunction(); // 输出 "New"
savedFuncion(); // 输出 "Old"

不过要小心,象下面这样的例子并不会有作用,因为是创建了另一个叫做
myFunctionPtr的函数而不是修改它。
Example 6B:
CODE:

function myFunction()
{
alert("Old");
}
var savedFunc=myFunction;
savedFunc=function()
{
alert("New");
};
myFunction(); // 输出 "Old"
savedFunc(); // 输出 "New"

内嵌函数
我们还能够在一个函数中嵌套一个函数。下例,我有一个叫做getHalfOf的函数,
而在它里面,我有另一个叫做calculate的函数。
Example 7
CODE:

function getHalfOf(num1, num2, num3)
{
function calculate(number)
{
return number/2;
}
var result="";
result+=calculate(num1)+" ";
result+=calculate(num2)+" ";
result+=calculate(num3);
}
var resultString=getHalfOf(10,20,30);
alert(resultString); // 输出 "5 10 15"

你只能在内部调用嵌套的函数。就是说,你不能这么调用:
getHalfOf.calculate(10),因为calculate只有当外部函数(getHalfOf())在运行
的时候才会存在。这和我们前面的讨论一致(函数会被编译,但只有当你去调用
它的时候才会执行)。
调用哪个函数?
你也许正在想命名冲突的问题。比如,下面哪一个叫做calculate的函数会被调用

Example 8
CODE:

function calculate(number)
{
return number/3;
}
function getHalfOf(num1, num2, num3)
{
function calculate(number)
{
return number/2;
}
var result="";
result+=calculate(num1)+" ";
result+=calculate(num2)+" ";
result+=calculate(num3);
}
var resultString=getHalfOf(10,20,30);
alert(resultString); // 输出 "5 10 15"

在这个例子中,编译器会首先搜索局部内存地址,所以它会使用内嵌的calculate
函数。如果我们删除了这个内嵌(局部)的calculate函数,这个代码会使用全局
的calculate函数。
函数:数据类型及构造函数
让我们来看看函数的另一个特殊功能--这让它和其它对象类型截然不同。一个
函数能够用来作为一个数据类型的蓝图。这个特性通常被用在面向对象编程中来
模拟用户自定义数据类型(user defined data type)。使用用户自定义数据类型
创建的对象通常被成为用户自定义对象(user defined object)。
数据类型
在定义了一个函数之后,我们也同时创建了一个新的数据类型。这个数据类型能
够用来创建一个新对象。下例,我创建了一个叫做Ball的新数据类型。
Example DT1
CODE:

function Ball()
{
}
var ball0=new Ball(); // ball0 现在指向一个新对象
alert(ball0); // 输出 "Object",因为 ball0 现在是一个对象

这样看来,ball0=new Ball()作了什么?new关键字创建了一个类型是Object的新
对象(叫做ball0)。然后它会执行Ball(),并将这个引用传给ball0(用于调用
对象)。下面,你会看到这条消息:“creating new Ball”,如果Ball()实际上
被运行的话。
Example DT2
CODE:

function Ball(message)
{
alert(message);
}
var ball0=new Ball("creating new Ball"); // 创建对象并输出消息
ball0.name="ball-0"; // ball0现在有一个属性:name
alert(ball0.name); // 输出 "ball-0"

我们可以把上面这段代码的第6行看做是底下的代码6-8行的一个简写:
CODE:

function Ball(message)
{
alert(message);
}
var ball0=new Object();
ball0.construct=Ball;
ball0.construct("creating new ball"); // 执行 ball0.Ball
("creating..");
ball0.name="ball-0";
alert(ball0.name);

这行代码ball0.construct=Ball和Example 4中的ptr=myFunction语法一致。
如果你还是不明白这行的含义那就回过头再复习一下Example 4。注意:你也许考
虑直接运行ball0.Ball(“…”),但是它不会起作用的,因为ball0并没有一个叫
做Ball(“…”)的属性,并且它也不知道你究竟想作些什么。
添加属性
当我们象上面那样使用关键字new创建一个对象的时候,一个新的Object被创建了
。我们可以在创建之后给这个对象添加属性(就好像我在上面那样添加属性name
。而接下来的问题就是如果我们创建了这个对象的另外一个实例,我们得象下面
那样再次给这个新对象添加这个属性。)
Example DT3 (creates 3 ball objects)
CODE:

function Ball()
{
}
var ball0=new Ball(); // ball0 现在指向了类型Ball的一个新实例
ball0.name="ball-0"; // ball0 现在有一个属性"name"
var ball1=new Ball();
ball1.name="ball-1";
var ball2=new Ball();
alert(ball0.name); // 输出 "ball-0"
alert(ball1.name); // 输出 "ball-1"
alert(ball2.name); // 哦,我忘记给ball2添加“name”了!

我忘记给ball2添加属性name了,如果在正式的程序中这也许会引发问题。有什么
好办法可以自动增加属性呢?嗯,有一个:使用this关键字。this这个词在
function中有特别的意义。它指向了调用函数的那个对象。让我们看看下面的另
一个示例,这时候我们在构造函数中添加上这些属性:
Example DT4
CODE:

function Ball(message, specifiedName)
{
alert(message);
this.name=specifiedName;
}
var ball0=new Ball("creating new Ball", "Soccer Ball");
alert(ball0.name); // prints "Soccer Ball"

请记住:是new关键字最终使得构造函数被执行。在这个例子中,它将会运行
Ball(“creating new Ball”, “Soccer Ball”);而关键字this将指向ball0。
因此,这行:this.name=specifiedName变成了ball0.name=”Soccer Ball”。
它主要是说:给ball0添加属性name,属性值是Soccer Ball。
我们现在只是添加了一个name属性给ball0,看起来和上一个例子中所做的很象,
但却是一个更好更具扩展性的方法。现在,我们可以随心所欲的创建许多带有属
性的ball而无需我们手动添加它们。而且,人们也希望创建的Ball对象能够清晰
的看懂它的构造函数并且能够轻松找出Ball的所有属性。让我们添加更多属性到
Ball里。
Example DT5
CODE:

function Ball(color, specifiedName, owner, weight)
{
this.name=specifiedName;
this.color=color;
this.owner=owner;
this.weight=weigth;
}
var ball0=new Ball("black/white", "Soccer Ball", "John", 20);
var ball1=new Ball("gray", "Bowling Ball", "John", 30);
var ball2=new Ball("yellow", "Golf Ball", "John", 55);
var balloon=new Ball("red", "Balloon", "Pete", 10);
alert(ball0.name); // 输出 "Soccer Ball"
alert(balloon.name); // 输出 "Balloon"
alert(ball2.weight); // 输出 "55"

嘿!使用面向对象术语,你能够说Ball是一个拥有如下属性的对象类型:name,
color, owner, weight。
将对象赋给属性
我们并没被限制只能添加形如字符串或者数字之类的简单数据类型作为属性。我
们也能够将对象赋给属性。下面,supervisor是Employee的一个属性.
Example DT6
CODE:

function Employee(name, salary, mySupervisor)
{
this.name=name;
this.salary=salary;
this.supervisor=mySupervisor;
}
var boss=new Employee("John", 200);
var manager=new Employee("Joan", 50, boss);
var teamLeader=new Employee("Rose", 50, boss);
alert(manager.supervisor.name+" is the supervisor of "+manager.name);
alert(manager.name+"'s supervisor is "+manager.supervisor.name);

会输出什么呢?
就像你在上面这个例子中看到的那样,manager和teamLeader都有一个supervisor
属性,而这个属性是类型Employee的一个对象。
将函数作为属性
任何类型的对象都可以作为一个属性,回忆一下前面的Example 4(不是Example
DT4),函数也是一个对象。所以你可以让一个函数作为一个对象的一个属性。下
面,我将添加两个函数getSalary和addSalary。
Example DT7
CODE:

function Employee(name, salary)
{
this.name=name;
this.salary=salary;
this.addSalary=addSalaryFunction;
this.getSalary=function()
{
return this.salary;
};
}
function addSalaryFunction(addition)
{
this.salary=this.salary+addition;
}
var boss=new Employee("John", 200000);
boss.addSalary(10000); // boss 长了 10K 工资……为什
么老板工资可以长这么多:'(
alert(boss.getSalary()); // 输出 210K……为什么默认工
资也那么高……:'(

addSalary和getSalary演示了几种将函数赋给属性的不同方法。如果你记得我们
最开始的讨论;我讨论了三种声明函数的不同方式。所有那些在这里都是适用的
,但是上面展示的两个最常用。
让我们看看有什么不同。下面,注意一下9-12行的代码。当这部分代码执行的时
候,函数getSalary被声明。如前面数次提到的,一个函数声明的结果是一个对象
被创建。所以这时候boss被创建(接下来的第19行),而boss里有一个getSalary
属性。
CODE:

function Employee(name, salary)
{
this.name=name;
this.salary=salary;
this.addSalary=addSalaryFunction;
this.getSalary=function()
{
return this.salary;
};
}
function addSalaryFunction(addition)
{
this.salary=this.salary+addition;
}
var boss=new Employee("John", 200000);
var boss2=new Employee("Joan", 200000);
var boss3=new Employee("Kim", 200000);

当你创建这个对象的更多实例时(boss2和boss3),每一个实例都有一份getSalary
代码的单独拷贝;而与此相反,addSalary则指向了同一个地方(即
addSalaryFunction)。
看看下面的代码来理解一下上面所描述的内容。
Example DT8
CODE:

function Employee(name, salary)
{
this.name=name;
this.salary=salary;
this.addSalary=addSalaryFunction;
this.getSalary=function()
{
return this.salary;
};
}
function addSalaryFunction(addition)
{
this.salary=this.salary+addition;
}
var boss1=new Employee("John", 200000);
var boss2=new Employee("Joan", 200000);
// 给getSalary函数对象添加属性
boss1.getSalary.owner="boss1";
boss2.getSalary.owner="boss2";
alert(boss1.getSalary.owner); // 输出 "boss1"
alert(boss2.getSalary.owner); // 输出 "boss2"
// 如果两个对象指向同一个函数对象,那么
// 上面两个输出都应该是“boss2”。
// 给addSalary函数对象添加属性
boss1.addSalary.owner="boss1";
boss1.addSalary.owner="boss2";
alert(boss1.addSalary.owner); // 输出 "boss2"
alert(boss2.addSalary.owner); // 输出 "boss2"
// 因为两个对象都指向同一个函数,(子乌注:原文写are not pointing to
the same function,疑为笔误)
// 当修改其中一个的时候,会影响所有的实例(所以两个都输出“boss2”).
也许不是重要的事情,但这里有一些关于运行类似上面的getSalary的内嵌函数的
结论: 1) 需要更多的存储空间来存储对象(因为每一个对象实例都会有它自己
的getSalary代码拷贝);2) javascript需要更多时间来构造这个对象。
让我们重新写这个示例来让它更有效率些。

Example DT9
CODE:

function Employee(name, salary)
{
this.name=name;
this.salary=salary;
this.addSalary=addSalaryFunction;
this.getSalary=getSalaryFunction;
}
function getSalaryFunction()
{
return this.salary;
}
function addSalaryFunction(addition)
{
this.salary=this.salary+addition;
}

看这儿,两个函数都指向同一个地方,这将会节约空间和缩短构造时间(特别是
当你有一大堆内嵌函数在一个构造函数的时候)。这里有另外一个函数的功能能
够来提升这个设计,它叫做prototype,而我们将在下一节讨论它。
函数:原型
每一个构造函数都有一个属性叫做原型(prototype,下面都不再翻译,使用其原文
)。这个属性非常有用:为一个特定类声明通用的变量或者函数。
prototype的定义
你不需要显式地声明一个prototype属性,因为在每一个构造函数中都有它的存在
。你可以看看下面的例子:
Example PT1
CODE:

function Test()
{
}
alert(Test.prototype); // 输出 "Object"

给prototype添加属性
就如你在上面所看到的,prototype是一个对象,因此,你能够给它添加属性。你
添加给prototype的属性将会成为使用这个构造函数创建的对象的通用属性。
例如,我下面有一个数据类型Fish,我想让所有的鱼都有这些属性:
livesIn=”water”和price=20;为了实现这个,我可以给构造函数Fish的
prototype添加那些属性。
Example PT2
CODE:

function Fish(name, color)
{
this.name=name;
this.color=color;
}
Fish.prototype.livesIn="water";
Fish.prototype.price=20;

接下来让我们作几条鱼:
CODE:

var fish1=new Fish("mackarel", "gray");
var fish2=new Fish("goldfish", "orange");
var fish3=new Fish("salmon", "white");

再来看看鱼都有哪些属性:
CODE:

for (int i=1; i<=3; i++)
{
var fish=eval("fish"+i); // 我只是取得指向这条鱼的指针
alert(fish.name+","+fish.color+","+fish.livesIn+","+fish.price);
}

输出应该是:
CODE:

"mackarel, gray, water, 20"
"goldfish, orange, water, 20"
"salmon, white water, 20"

你看到所有的鱼都有属性livesIn和price,我们甚至都没有为每一条不同的鱼特别
声明这些属性。这时因为当一个对象被创建时,这个构造函数将会把它的属性
prototype赋给新对象的内部属性__proto__。这个__proto__被这个对象用来查找
它的属性。
你也可以通过prototype来给所有对象添加共用的函数。这有一个好处:你不需要
每次在构造一个对象的时候创建并初始化这个函数。为了解释这一点,让我们重
新来看Example DT9并使用prototype来重写它:
用prototype给对象添加函数
Example PT3
CODE:

function Employee(name, salary)
{
this.name=name;
this.salary=salary;
}
Employee.prototype.getSalary=function getSalaryFunction()
{
return this.salary;
}
Employee.prototype.addSalary=function addSalaryFunction(addition)
{
this.salary=this.salary+addition;
}

我们可以象通常那样创建对象:
CODE:

var boss1=new Employee("Joan", 200000);
var boss2=new Employee("Kim", 100000);
var boss3=new Employee("Sam", 150000);

并验证它:
CODE:

alert(boss1.getSalary()); // 输出 200000
alert(boss2.getSalary()); // 输出 100000
alert(boss3.getSalary()); // 输出 150000

这里有一个图示来说明prototype是如何工作的。这个对象的每一个实例(boss1,
boss2, boss3)都有一个内部属性叫做__proto__,这个属性指向了它的构造器
(Employee)的属性prototype。当你执行getSalary或者addSalary的时候,这个对
象会在它的__proto__找到并执行这个代码。
转自:http://www.cnblogs.com/dwyin/archive/2010/07/12/1775744.html

Java开源Apache项目

Commons-Pool:
创建新的对象并初始化的操作,可能会消耗很多的时间。在这种对象的初始化工作包含了一些费时的操作(例如,从一台位于20,000千米以外的主机上读出一 些数据)的时候,尤其是这样。在需要大量生成这样的对象的时候,就可能会对性能造成一些不可忽略的影响。要缓解这个问题,除了选用更好的硬件和更棒的虚拟 机以外,适当地采用一些能够减少对象创建次数的编码技巧,也是一种有效的对策。对象池化技术(Object Pooling)就是这方面的著名技巧,而Jakarta Commons Pool组件则是处理对象池化的得力外援。
Commons-Math:
Math 是一个轻量的,自包含的数学和统计组件,解决了许多非常通用但没有及时出现在Java标准语言中的实践问题。
Commons-Jelly:
Jelly能够把XML转换成可执行代码,所以Jelly是一个基于XML与Java的脚本和处理引擎。 Jelly借鉴了JSP定指标签,Velocity, Cocoon和Xdoclet中的脚本引擎的许多优点。Jelly可以用在命令行,Ant或者Servlet之中。
Commons-FileUpload:
FileUpload 使得在你可以在应用和Servlet中容易的加入强大和高性能的文件上传能力。
Commons-BeanUtils:
Commons-BeanUtils 提供对 Java 反射和自省API的包装。
Commons-Chain:
Chain 提供实现组织复杂的处理流程的“责任链模式”。
Commons-Codec:
Codec 包含一些通用的编码解码算法。包括一些语音编码器, Hex, Base64, 以及URL encoder。
Commons-Discovery:
Commons-Discovery 提供工具来定位资源 (包括类) ,通过使用各种模式来映射服务/引用名称和资源名称。
Commons-EL:
Commons-EL 提供在JSP2.0规范中定义的EL表达式的解释器。
Commons-Jexl:
Jexl是一个表达式语言,通过借鉴来自于Velocity的经验扩展了JSTL定义的表达式语言。
Apache Forrest:
Apache Forrest是一个把来自各种不同的输入数据源转换成用一种或多种输出格式(比如HTML,PDF等)来统一显示的发布系统。它基于Apache Cocoon并分离了内容与内容结构,不仅可以生成静态的文档也可以当作一个动态的服务器。
Apache POI:
Apache POI可以让你使用Java来读写MS Excel ,Word、PowerPoint文件。
MINA:
MINA (Multipurpose Infrastructure for Network Applications)是一个网络应用程序框架。用户利用它可以容易地开发高性能和高伸缩性的网络应用程序。
Commons-Configuration:
Commons-Configuration 工具可以从
*Properties文件
*XML文件
*JNDI
*JDBC 数据源
*System properties
*Applet parameters
*Servlet parameters等来读取相应的信息
Commons-validator:
commons-validator提供了一个简单的,可扩展的框架来在一个XML文件中定义校验器 (校验方法)和校验规则。支持校验规则的和错误消息的国际化。
JXPath:
org.apache.commons.jxpath定义了一个简单的XPath表达式语言的解析器.JXPath应用XPath表达式来操纵各种类型的对象包括:JavaBeans,Maps,Servlet contexts,DOM等。
Commons Betwixt:
Commons Betwixt这个组件提供一个XML自省(introspection)机制用来把Java Bean映射到XML文档.他实现了与标准的Introspector及BeanInfo类相似的XMLIntrospector及XMLBeanIfno类。
Jakarta ORO:
Jakarta ORO一套文本处理工具,能提供perl5.0兼容的正则表达式,AWK-like正则表达式, glob表达式。还提供替换,分割,文件名过虑等功能。
Jakarta Regexp:
Regexp是一个100%纯java正则式处理包,是Jonathan Locke捐给Apache软件基金会的。
Commons-Convert:
Commons-Convert的目标是提供一个简单的包来完成把一种类型的对象转换成另一种.但它目前只支持String到Object或Object到String的转换。
Commons-Email:
Commons-Email的目的是提供一组用于发送Email的API,它基于Java Mail API基础之上并进行了简化。它提供的主要Java类有:SimpleEmail:这个类用于发送简单的文本类型email。MultiPartEmail:这个类允许发送文本消息并附带附件。HtmlEmail:用于发送Html格式的附件并支持内含图片。EmailAttachment:这是一个简单的容器类用于简化附件的处理。
Commons-IO:
Commons IO是Jakarta Commons项目的一个子项目。用来帮助进行IO功能开发.它包含三个主要的领域:Utility classes-提供一些静态方法来完成公共任务.Filters-提供文件过滤器的各种实现.Streams-提供实用的Stream,reader与writer实现。
XML Security:
XML Security项目是为XML安全标准(XML-Signature Syntax and Processing:XML数字签名语法和处理规则;XML Encryption Syntax and Processing:XML加密语法和处理规则)提供Java及C++实现的开源项目。它当前的Java类包中包括了一个成熟的数字签名实现。
ECS:
Jakarta Element Construction Set(ECS是一个使用Java语言和面向对象方法创建标记语言文档(HTML,XML)的开源项目。
Apache Harmony:
Apache Harmony是Apache软件基金会的一个开放源码JavaSE的实现。其目标是完成一个兼容的、独立的JavaSE5 JDK实现,并基于Apache License v2许可发布;和一个由社区开发的模块化的运行时(包括虚拟机和类库)体系结构。
Apache Commons-VFS:
VFS为访问各种不同的文件系统提供了单一的应用程序编程接口(API),大大的简化了应用程序本身代码的复杂度。目前VFS支持FTP、Local Files 、HTTP/HTTPS、SFTP、Temporary Files、Zip、Jar/Tar(uncompressed、tgz or tbz2)、gzip/bzip2、res、ram所有这些文件系统中的文件被封装成了FileObject这个类,文件的读写操作都通过此类来完成。文件的路径采用了URL的方式。
Apache Mahout:
Mahout项目Apache Lucene开源搜索引擎的一个子项目。在其中实现一些常见的用于集群、分类和CP的机器学习算法。
Apache Continuum:
Apache Continuum是一个企业级持续集成服务器。提供的特性包括:自动构建、发布管理、基于角色的权限管理、源码控制系统、与其它流行构建工具相集成。不管你是否需要集中构建,还是开发人员手动控制发布。Continuum都能够帮助你改进质量和维护一个协调的构建环境。
Apache Compress Ant Library:
Apache Compress Ant Library提供了一些Tasks和Types来读写各种类型的压缩文件和存档文件。支持AR、BZIP2、CPIO、GZIP、TAR和ZIP格式。
Apache Directory Studio:
Apache Directory Studio是一个LDAP工具平台,支持连接到任何 LDAP服务器并进行管理和开发工作,但主要还是设计用于Apache Directory Server。功能包括:LDAP浏览器、LDIF编辑器、Schema编辑器、嵌入式 ApacheDS、ACI编辑器。

SVN管理代码

SVN管理代码,本地与服务器有冲突,应该以服务器为准。如果出现一些奇怪的问题,撒手锏就是将相关名为SVN文件夹删除,这里保存着类似浏览器缓存之类的,乱七八糟的东西。
先更新后提交,一定要把冲突解决了,不要全部更新,先对比看同事们都提交了哪些代码,后更新。
配合上jenkins持续集成效果会更好!
PS:一定要把本地代码保存好了,不要丢失了宝贵的劳动成果!

Eclipse快捷键大全

Ctrl+1 快速修复(最经典的快捷键,就不用多说了)
Ctrl+D: 删除当前行
Ctrl+Alt+↓ 复制当前行到下一行(复制增加)
Ctrl+Alt+↑ 复制当前行到上一行(复制增加)
Alt+↓ 当前行和下面一行交互位置(特别实用,可以省去先剪切,再粘贴了)
Alt+↑ 当前行和上面一行交互位置(同上)
Alt+← 前一个编辑的页面
Alt+→ 下一个编辑的页面(当然是针对上面那条来说了)
Alt+Enter 显示当前选择资源(工程,or 文件 or文件)的属性
Shift+Enter 在当前行的下一行插入空行(这时鼠标可以在当前行的任一位置,不一定是最后)
Shift+Ctrl+Enter 在当前行插入空行(原理同上条)
Ctrl+Q 定位到最后编辑的地方
Ctrl+L 定位在某行 (对于程序超过100的人就有福音了)
Ctrl+M 最大化当前的Edit或View (再按则反之)
Ctrl+/ 注释当前行,再按则取消注释
Ctrl+O 快速显示 OutLine
Ctrl+T 快速显示当前类的继承结构
Ctrl+W 关闭当前Editer
Ctrl+K 参照选中的Word快速定位到下一个
Ctrl+E 快速显示当前Editer的下拉列表(如果当前页面没有显示的用黑体表示)
Ctrl+/(小键盘) 折叠当前类中的所有代码
Ctrl+×(小键盘) 展开当前类中的所有代码
Ctrl+Space 代码助手完成一些代码的插入(但一般和输入法有冲突,可以修改输入法的热键,也可以暂用Alt+/来代替)
Ctrl+Shift+E 显示管理当前打开的所有的View的管理器(可以选择关闭,激活等操作)
Ctrl+J 正向增量查找(按下Ctrl+J后,你所输入的每个字母编辑器都提供快速匹配定位到某个单词,如果没有,则在stutes line中显示没有找到了,查一个单词时,特别实用,这个功能Idea两年前就有了)
Ctrl+Shift+J 反向增量查找(和上条相同,只不过是从后往前查)
Ctrl+Shift+F4 关闭所有打开的Editer
Ctrl+Shift+X 把当前选中的文本全部变味小写
Ctrl+Shift+Y 把当前选中的文本全部变为小写
Ctrl+Shift+F 格式化当前代码
Ctrl+Shift+P 定位到对于的匹配符(譬如{}) (从前面定位后面时,光标要在匹配符里面,后面到前面,则反之)
下面的快捷键是重构里面常用的(注:一般重构的快捷键都是Alt+Shift开头的了)
Alt+Shift+R 重命名 (是我自己最爱用的一个了,尤其是变量和类的Rename,比手工方法能节省很多劳动力)
Alt+Shift+M 抽取方法 (这是重构里面最常用的方法之一了,尤其是对一大堆泥团代码有用)
Alt+Shift+C 修改函数结构(比较实用,有N个函数调用了这个方法,修改一次搞定)
Alt+Shift+L 抽取本地变量( 可以直接把一些魔法数字和字符串抽取成一个变量,尤其是多处调用的时候)
Alt+Shift+F 把Class中的local变量变为field变量 (比较实用的功能)
Alt+Shift+I 合并变量(可能这样说有点不妥Inline)
Alt+Shift+V 移动函数和变量(不怎么常用)
Alt+Shift+Z 重构的后悔药(Undo)
编辑
作用域 功能 快捷键
全局 查找并替换 Ctrl+F
文本编辑器 查找上一个 Ctrl+Shift+K
文本编辑器 查找下一个 Ctrl+K
全局 撤销 Ctrl+Z
全局 复制 Ctrl+C
全局 恢复上一个选择 Alt+Shift+↓
全局 剪切 Ctrl+X
全局 快速修正 Ctrl1+1
全局 内容辅助 Alt+/
全局 全部选中 Ctrl+A
全局 删除 Delete
全局 上下文信息 Alt+?
Alt+Shift+?
Ctrl+Shift+Space
Java编辑器 显示工具提示描述 F2
Java编辑器 选择封装元素 Alt+Shift+↑
Java编辑器 选择上一个元素 Alt+Shift+←
Java编辑器 选择下一个元素 Alt+Shift+→
文本编辑器 增量查找 Ctrl+J
文本编辑器 增量逆向查找 Ctrl+Shift+J
全局 粘贴 Ctrl+V
全局 重做 Ctrl+Y
查看
作用域 功能 快捷键
全局 放大 Ctrl+=
全局 缩小 Ctrl+-
窗口
作用域 功能 快捷键
全局 激活编辑器 F12
全局 切换编辑器 Ctrl+Shift+W
全局 上一个编辑器 Ctrl+Shift+F6
全局 上一个视图 Ctrl+Shift+F7
全局 上一个透视图 Ctrl+Shift+F8
全局 下一个编辑器 Ctrl+F6
全局 下一个视图 Ctrl+F7
全局 下一个透视图 Ctrl+F8
文本编辑器 显示标尺上下文菜单 Ctrl+W
全局 显示视图菜单 Ctrl+F10
全局 显示系统菜单 Alt+-
导航
作用域 功能 快捷键
Java编辑器 打开结构 Ctrl+F3
全局 打开类型 Ctrl+Shift+T
全局 打开类型层次结构 F4
全局 打开声明 F3
全局 打开外部javadoc Shift+F2
全局 打开资源 Ctrl+Shift+R
全局 后退历史记录 Alt+←
全局 前进历史记录 Alt+→
全局 上一个 Ctrl+,
全局 下一个 Ctrl+.
Java编辑器 显示大纲 Ctrl+O
全局 在层次结构中打开类型 Ctrl+Shift+H
全局 转至匹配的括号 Ctrl+Shift+P
全局 转至上一个编辑位置 Ctrl+Q
Java编辑器 转至上一个成员 Ctrl+Shift+↑
Java编辑器 转至下一个成员 Ctrl+Shift+↓
文本编辑器 转至行 Ctrl+L
搜索
作用域 功能 快捷键
全局 出现在文件中 Ctrl+Shift+U
全局 打开搜索对话框 Ctrl+H
全局 工作区中的声明 Ctrl+G
全局 工作区中的引用 Ctrl+Shift+G(查找类、方法和属性的引用。这是一个非常实用的快捷键,例如要修改引用某个方法的代码,可以通过【Ctrl+Shift+G】快捷键迅速定位所有引用此方法的位置。
文本编辑
作用域 功能 快捷键
文本编辑器 改写切换 Insert
文本编辑器 上滚行 Ctrl+↑
文本编辑器 下滚行 Ctrl+↓
文件
作用域 功能 快捷键
全局 保存 Ctrl+X
Ctrl+S
全局 打印 Ctrl+P
全局 关闭 Ctrl+F4
全局 全部保存 Ctrl+Shift+S
全局 全部关闭 Ctrl+Shift+F4
全局 属性 Alt+Enter
全局 新建 Ctrl+N
项目
作用域 功能 快捷键
全局 全部构建 Ctrl+B
源代码
作用域 功能 快捷键
Java编辑器 格式化 Ctrl+Shift+F
Java编辑器 取消注释 Ctrl+
Java编辑器 注释 Ctrl+/
Java编辑器 添加导入 Ctrl+Shift+M
Java编辑器 组织导入 Ctrl+Shift+O
Java编辑器 使用try/catch块来包围 未设置,太常用了,所以在这里列出,建议自己设置。
也可以使用Ctrl+1自动修正。
运行
作用域 功能 快捷键
全局 单步返回 F7
全局 单步跳过 F6
全局 单步跳入 F5
全局 单步跳入选择 Ctrl+F5
全局 调试上次启动 F11
全局 继续 F8
全局 使用过滤器单步执行 Shift+F5
全局 添加/去除断点 Ctrl+Shift+B
全局 显示 Ctrl+D
全局 运行上次启动 Ctrl+F11
全局 运行至行 Ctrl+R
全局 执行 Ctrl+U
重构
作用域 功能 快捷键
全局 撤销重构 Alt+Shift+Z
全局 抽取方法 Alt+Shift+M
全局 抽取局部变量 Alt+Shift+L
全局 内联 Alt+Shift+I
全局 移动 Alt+Shift+V
全局 重命名 Alt+Shift+R
全局 重做 Alt+Shift+Y

DOM对象和JQuery对象的区别

jQuery对象和DOM对象使用说明,需要的朋友可以参考下。

1.jQuery对象和DOM对象
第一次学习jQuery,经常分辨不清哪些是jQuery对象,哪些是 DOM对象,因此需要重点了解jQuery对象和DOM对象以及它们之间的关系.
DOM对象,即是我们用传统的方法(javascript)获得的对象,jQuery对象即是用jQuery类库的选择器获得的对象;
复制代码 代码如下:

var domObj = document.getElementById("id"); //DOM对象
var $obj = $("#id"); //jQuery对象;

jQuery对象就是通过jQuery包装DOM对象后产生的对象,它是jQuery独有的。如果一个对象是jQuery对象,那么就可以使用jQuery里的方法,例:
$("#foo").html(); //获取id为foo的元素内的html代码,html()是jQuery特有的方法;
上面的那段代码等同于:
document.getElementById("foo").innerHTML;

注意:在jQuery对象中无法使用DOM对象的任何方法。
例如$("#id").innerHTML 和$("#id").checked之类的写法都是错误的,可以用$("#id").html()和$("#id").attr ("checked")之类的 jQuery方法来代替。同样,DOM对象也不能使用jQuery方法。学习jQuery开始就应当树立正确的观念,分清jQuery对象和DOM对象之间的区别,之后学习 jQuery就会轻松很多的。

2.jQuery对象和DOM对象的互相转换

在上面第一点说了,jquery对象和dom对象是不一样的!比如jquery对象不能使用dom的方法,dom对象不能使用jquery方法,那假如我 jquery没有封装我要的方法,那能怎么办呢?
这时我们可以将jquer对象转换成dom对象

jquery对象转换成 dom对象
jquery提供了两种方法将一个jquery对象转换成一个dom对象,即[index]和get(index)。可能有人会觉得奇怪,怎么是用下标呢,没错,jquery对象就是一个数组对象.
下面代码将演示一个jquery对象转换成dom对象,再使用dom对象的方法
复制代码 代码如下:

var $cr=$("#cr"); //jquery对象
var cr = $cr[0]; //dom对象 也可写成 var cr=$cr.get(0);
alert(cr.checked); //检测这个checkbox是否给选中

dom对象转换成jquery对象
对于一个dom对象,只需要用$()把dom对象包装起来,就可以获得一个jquery对象了,方法为$(dom对象);
复制代码 代码如下:

var cr=document.getElementById("cr"); //dom对象
var $cr = $(cr); //转换成jquery对象

转换后可以任意使用jquery中的方法了.

通过以上的方法,可以任意的相互转换jquery对象和dom对象.
最后再次强调,dom对象才能使用dom中的方法,jquery对象不可以使用dom中的方法,但 jquery对象提供了一套更加完善的工具用于操作dom,关于jquery的dom操作将在后面的文章进行详细讲解.


ps: 平时用到的jquery对象都是通过$()函数制造出来的,$()函数就是一个jquery对象的制造工厂.
建议:如果获取的对象是 jquery对象,那么在变量前面加上$,这样方便容易识别出哪些是jquery对象,例如:
var $variable = jquery对象;
如果获取的是dom对象,则定义如下:
var variable = dom对象 

http://www.cnblogs.com/yellowapplemylove/archive/2011/04/19/2021583.html

处理遗留系统

最近一个同事要离职,而我刚到新公司没多久,需要从他手里接过原来的系统,经过这些天的交接,确实是难。找到三篇文章,应该帮助很大,
第一篇属于方法论(张逸老师),为我们处理遗留系统制定大目标和策略:
http://www.cnblogs.com/wayfarer/archive/2011/10/09/2203651.html
第二篇属于工具类,介绍了处理java遗留系统有哪些工具:
http://tieba.baidu.com/p/1331700739
第三篇是从工程角度来分析该如何处理遗留系统:
http://www.infoq.com/cn/articles/Dealing-with-legacy-code
第四篇文章则是张逸老师碰到的一个案例:
http://www.cnblogs.com/wayfarer/archive/2011/02/18/1957530.html
下面的文章是摘自第一篇文章,加粗部分是我认为的重点:
处理遗留系统,几乎是每个程序员都不可能绕过的一件麻烦事儿。因为时间压力,技能不足以及功能复杂等诸多原因,常常使得遗留系统的代码变得糟糕混乱,可读性与维护性差,无法保证功能的可测试性,纠缠不清的代码让类、方法之间紧紧耦合在一起。如果遗留系统能够正常工作,那么我们还可以置之不理,即使代码接近腐烂的边缘,我们还可以得过且过。倘若我们需要维护遗留系统,或者需要为它添加新的功能,又或者需要将新的系统与遗留系统进行集成,就必须正视遗留系统带来的问题了。
处理遗留系统,首先需要分析和了解遗留系统,尤其这个遗留系统并非你开发时,更需如此。我们可以考虑双管齐下的办法。一是从业务逻辑方面去了解。相比新系统而言,遗留系统的唯一好处就是它往往是可以运行可以使用的。因此,最好的办法是直接运行遗留系统,通过实际操作了解系统的各个功能点、业务流程。这样的直观感受可以最快地帮助你了解该系统:它能够做什么?它能达成什么目标?它的范围是什么?它存在什么问题?其二则需要从系统架构出发,了解遗留系统的逻辑结构和物理分布。可以阅读架构文档和源代码,如果能够咨询遗留系统的设计者或开发人员,就更好。尽快地描绘出遗留系统的轮廓图,可以帮助你从技术的宏观角度剖析遗留系统的结构与组成。再结合你对该系统业务的理解,快速地掌握遗留系统。如果需要阅读源代码,最好能够从主程序入口开始,找到一些主要的模块,了解其大体的设计方式与编码习惯。由于之前对系统架构已有了解,阅读代码时,不应在一开始就去理解代码实现的细节,而应结合架构文档,比对代码实现是否与文档的描述一致,并充分利用自己的技术与经验,找到阅读代码的终南捷径。例如,如果我们知道该系统采用了MVC架构,就可以很容易地根据Url找到对应的Controller对象,并在该对象中寻找业务功能实现的脉络。又例如我们知道系统采用了SSH框架,而我们又非常熟悉SSH框架,就可以基本忽略系统基础设施的部分,直接了解系统的业务实现。如果是Swing系统,而且在界面中混杂了大量的业务逻辑和界面逻辑,则需要找到系统实现的特点,譬如系统的业务都是通过菜单项进行操作,就可以在界面中找到相关的菜单对象,然后根据这些对象的Action操作,一步一步跟踪。甚至可以利用调试的方法,设置断点,来摸清楚系统的运行机制与执行顺序。
分析遗留系统需要有的放矢,根据目标快速锁定范围。例如,如果我们是因为系统性能出现问题,而要去分析遗留系统,就无需过于关心业务逻辑,而应从性能分析入手。考虑数据库的访问,IO操作,缓存机制,资源的使用等诸多方面。这就需要借助一些经验和技术了,当然也可以考虑使用一些工具,用以诊断性能瓶颈。我们曾经在一个项目中,发现遗留系统的性能问题非常严重。根据分析,我们发现系统对字符串的处理存在问题,大量使用了String类型的对象完成字符的拼接。而在进行数据库查询时,很多代码是直接性地一次将相关表的数据“拉”到内存的集合对象中,然后利用过滤器在集合对象中进行筛选。而在对数据进行更改时,又没有很好地利用业务特性,完成一次提交,导致产生多次数据库访问。在系统的某些公共模块中,重复多次加载了Spring的配置文件。还有某些占用了较大资源的对象,对于系统用户而言应该是同一个对象,但却没有设计为单例。因为我们抱着改善遗留系统性能的目的,所以在分析遗留系统时,就应该事先确定可能导致性能损耗的地方,而不是全方位地去了解整个系统。
在维护遗留系统时,需要根据不同的场景做出不同的决策。简言之,我们需要排定优先级。如果时间紧迫,则解决问题是第一要务。尽快通过出错情况和记录日志辨别出错原因,定位出错代码,并解决之,而不是去考虑设计的优雅,代码的重用。我在一次解决报表显示的问题时,采用的就是这种做法。虽然与错误相关的代码相当丑陋,但由于时间紧迫,解决Bug才是最要紧的,所以我基本上没有去修改或改善既有代码的结构,仅仅解决了问题就结束了对遗留系统的处理。这个问题的处理过程在我的另一篇博客《精益求精,抑或得过且过》中详细论述。是的,我在此时选择了得过且过的做法,主要原因就在于优先级列表中,问题的解决才是最高优先级。在这次处理过程中,我部分地利用了copy & paste的做法,并通过引入新方法的方式来解决bug,而不是直接修改出现问题的方法。这是因为该方法的引用点存在多处,由于遗留系统并没有单元测试的保护,我不能草率地修改,否则可能导致一个bug的修复,结果引入更多无法预测的Bug。
这样的处理方式其实是我不甘心的选择。在时间允许的情况下,我会考虑对相关代码进行一些小的重构,例如提取方法或提取类等。虽然这些重构不能改变遗留系统的本质,但至少可以提高代码的可读性,并能在一定程度下去除代码重复。
这一实例实际引出了单元测试的必要性。如果我们的开发都能够在单元测试的呵护下进行,即使当它随着时间的推移,慢慢变成了丑陋的遗留代码,因为有单元测试的保护,在对它进行手术时,成功的几率却会变得更大。因此,认为编写单元测试是浪费时间的观点,事实上是一种短视的做法。缺乏单元测试,就是一种技术债(technical debt)。现在欠下的,将来总是要还的。我们不能忽略软件的维护成本。事实上,在软件成本中,维护成本所占的比例远远大于开发成本。Kent Beck在《实现模式(Implementation Patterns)》一书中认为:“维护的代价很大,这是因为理解现有代码需要花费时间,而且容易出错。知道了需要修改什么以后,做出改动就变得轻而易举了。掌握现在的代码做了哪些事情是最需要花费人力物力的部分,改动之后,还要进行测试和部署。”
当然,在处理遗留系统时,仍然可以进行单元测试。但它需要的技能更高,付出的精力更多。若要完成对遗留系统的单元测试,首要必须解决的就是依赖。解除依赖是面向对象系统开发中似乎亘古不变的话题。无论你翻阅哪一本面向对象著作,都会提到这个问题。依赖的起源其实在于对象的协作。根据单一职责原则,一个类显然不能承担太多的职责。如果一个系统的所有功能都是一个对象完成的,那么这个对象就成了邪恶的“上帝”对象。它的粒度太大,因此难以重用,不够灵活,细节纠缠,无法维护。这样的设计是我们必须避免的。既然一个类不能承担太多职责,系统要完成客户要求的所有功能,就必须让许多对象互相协作,就像人类社会的人际交往一般,于是,就产生了对象之间的依赖。凡事有利必有弊,这种细粒度的对象协作,虽然保证了对象的重用性、灵活性,却也因为协作带来的依赖,导致对象之间存在一定的耦合度,变得难以替换。由于一个对象依赖另一个对象,就使得我们在单元测试时,无法独立地测试我们的目标对象。
依赖虽然不可避免,但如果能够保证对象之间的良好协作,就能够最大程度保证系统的松耦合。对象协作一般可以分为类协作、接口协作与回调协作(回调协作通常可以看做是接口协作的一种特殊方式,关于这三种协作的特点与方式,我会在以后的文章中谈到)。依赖最弱的是接口协作,这也是我们努力达到的目标,它符合“面向接口设计”的基本原则。同时,也能够保证对象的封装性,通过将实现细节隐藏起来,从而降低对象之间的依赖程度。
知易行难,要对付遗留系统中的依赖问题,通常需要付出百倍的努力。Michael Feathers在其大作《修改代码的艺术(Working Effectively with Legacy Code)》中,给出了很多好的解除遗留代码依赖的实践。例如他提出的接缝类型,隐藏依赖,以及影响结构图。解除依赖需要合理地利用封装与抽象,包括对方法参数的封装与抽象。对于遗留系统而言,Feathers提出的影响结构图尤其有用。当你需要修改某个方法时,影响结构图可以帮助你分析该方法的依赖传播途径,避免因为修改对其他代码造成影响。具体做法可以参考该书。当然,现在有很多IDE工具事实上还支持生成依赖图,它可以在一定程度上帮助你寻找依赖的方向与结构。不过,手工方式的分析尤其是通过手绘影响结构图仍然不可缺少,它更能帮助你深入地理解遗留系统。为遗留系统绘制包图(Package Diagram)同样非常重要,它既能帮助你理解遗留系统的结构,又能为我们找到系统中可能存在的双向依赖和循环依赖,找到分解不合理的包,这就为我们的系统重构奠定了良好的基础。
如果遗留系统非常复杂,以至于无法重构,同时我们又需要在遗留系统的基础上增加新的特性和功能,好的做法就是做好新、旧功能之间的隔离。暂时可以不去考虑遗留系统的原有结构和代码(大多数情况,这些遗留系统其实是可以正常工作的),而只需要为新增的功能做好单元测试,并以好的设计原则与编码规范来要求新功能的实现。同时,我们还可以编写一些自动化的回归测试用例(例如使用Cucumber结合Watir为Web系统编写回归测试),保证新增的功能不会影响到原有系统。如果新增功能的实现需要调用原有系统中的某些类或方法,而这些类和方法却又难以分解,则可以考虑参照原有实现编写新的类或方法,新的类一定要做到合理的封装与抽象,保证它的高内聚与松耦合;也可以在新的类中不去真正实现,而是通过运用Adapter模式来重用旧有的类。
只要不是更改开发平台,通常情况下,我们不会考虑重写遗留系统。如果需要重构遗留系统,就必须采取“分而治之,小步前进”的策略。可以首先选择实现较为容易,或者独立性较好的模块进行重构。将遗留系统逐步提取为一些可重用的模块与类,就可以形成“星星之火,可以燎原”的态势。其中,对于原有类或模块的调用方,由于在重构时可能会更改接口,因此可以考虑引入Facade模式或Adapter模式,或者对接口进行另一层的包装,或者对接口进行适配,使系统慢慢被替换,最后演化为一个结构合理的良好系统。在重构时,甚至可以考虑将一些独立性很好的功能,提取为单独进程下的应用或服务,通过Web Service、Socket或Restful的方式进行调用,既保证了重用,又能够使遗留系统变得更简单,成功瘦身。
我们还需谨记的一点是,虽然模块乃至服务的重构对系统的改造更加重要,但我们却不能忽视代码的质量。小到方法的提取,以及变量的命名都非常重要。也许它不会给系统带来根本的改变,但它却能够改善代码的可阅读性,进而提高系统的可维护性。所谓”聚沙成塔“,无论是系统还是模块,其实都是这些类和方法以及变量组成的。
对遗留系统的处理不可能“一蹴而就”,必须遵循循序渐进的做法,逐步改善。处理遗留系统影响深远,成本也非常高,所以我们也不能因为脑袋一发热,就开始气势汹涌地“提枪上阵”,错将风车当做了魔鬼,结果撞得头破血流。必须对整个遗留系统进行审慎的分析,并结合具体情况考虑这项工程的复杂度、成本与预算,了解团队的重构与设计能力。处理遗留系统的前路漫漫,我们固然需要上下而求索的决心与勇气,却也不能在茫然失措中迷失了前进的方向。遗留系统的处理,必须慎之,慎之,再慎之!

classloader-类加载器

什么是类加载器呢?
与普通程序不同的是,Java程序(class文件)并不是本地的可执行程序。当运行Java程序时,首先运行JVM(Java虚拟机),然后再把Java class加载到JVM里头运行,负责加载Java class的这部分就叫做Class Loader。
JVM本身包含了一个ClassLoader称为Bootstrap ClassLoader,和JVM一样,BootstrapClassLoader是用本地代码实现的,它负责加载核心JavaClass(即所有java.*开头的类)。另外JVM还会提供两个ClassLoader,它们都是用Java语言编写的,由BootstrapClassLoader加载;其中Extension ClassLoader负责加载扩展的Javaclass(例如所有javax.*开头的类和存放在JRE的ext目录下的类),ApplicationClassLoader负责加载应用程序自身的类。
当运行一个程序的时候,JVM启动,运行bootstrapclassloader,该ClassLoader加载java核心API(ExtClassLoader和AppClassLoader也在此时被加载),然后调用ExtClassLoader加载扩展API,最后AppClassLoader加载CLASSPATH目录下定义的Class,这就是一个程序最基本的加载流程。

北京“祥云工程”行动计划

新华网北京1月29日电(记者张雪花) 不少人说,云计算产业的发展给城市插上了智慧的翅膀。2010年9月北京市经信委发布《北京“祥云工程”行动计划(2010-2015年)》,提出了北京市云计算产业发展的总体目标,如今两年多过去了,“北京云”发展得怎么样,给“智慧城市”建设及百姓生活带来哪些变化?本网记者就此专访了北京市经济和信息化委员会副主任姜贵平,副总工程师、软件与信息服务处处长姜广智。
谈到北京“祥云工程”的进展,姜贵平说,从2010年到2012年北京云计算产业三年迈了三大步,2010年是布局年,重点开展云基地建设;2011年是应用年,年底推出了祥云十大示范应用平台;2012年是创新年,在云计算产业链上推出了近100款新产品。目前北京云计算产业已形成南部亦庄北部中关村两大聚集区,参与云计算产业的企业已达150多家,很多应用已经落地,百姓可以实实在在感受到云计算带来的变化。2013年将是北京云产业的深耕年。
北京把云计算作为战略新兴产业的一个重要支撑,给予大力扶持。姜贵平说,这几年北京特别注重一些小企业的扶持发展,推动了50多个项目,投资了5个多亿,仅政府引导资金就有6亿多。据姜广智介绍,祥云工程实施以来,社会上的各类风险投资对北京云企业的投资已达86亿元左右。
谈到云应用,姜贵平说,云计算的发展和城市的智慧运行是相辅相成的,在政务应用上,北京率先建成了移动政务,只要有一部手机有网络,不管在当地还是出差都可以办公,此外还建立北京政府信息数据网,聚集了166类政务数据12万条,供相关方面挖掘利用。此外,北京政务云、交通云、安全云、教育云,社区云等模块的发展,也使得城市的运行、百姓的生活变得更加智慧。
“北京作为拥有2000多万人的大城市,有很多错综复杂的问题,这需要一些高端技术和大数据的挖掘,如果在北京用云计算解决这样的问题,它的示范引领作用是非常好的。”谈到北京云计算发展的特点,姜贵平如是说。
2012年3月北京市政府发布《智慧北京行动纲要》,提出到2015年,北京要实现从“数字北京”向“智慧北京”全面跃升。
针对公众关注的云数据中心的建设规模问题,姜广智作了回应,他认为,北京是国家数据中心比较密集的区域,其作为政治中心、经济中心、文化中心,本身就需要大量的数据中心。姜广智同时强调建设数据中心需要根据云计算发展的实际需要,也需要企业对市场的准确判断,反对片面追求数据中心的规模和数量。
《北京“祥云工程”行动计划》提出,2010到2015年通过“千人计划”及“海聚工程”引进30名云计算领军人才。对此,姜广智说,北京近两年通过国家“千人计划”和北京“海聚工程”已引进了22名领军人才,对突破一些发展瓶颈问题起到了非常重要的作用,但依然缺少对新的商业模式和最前沿的技术有研究的高端人才。
http://news.hexun.com/2013-01-29/150722949.html
附:
北京“祥云工程”行动计划
(2010—2015年)
一、前言
云计算是基于互联网、通过虚拟化方式共享资源的计算模式,使计算、存储、网络、软件等资源,按照用户的动态需要,以服务的方式提供。云计算是继个人电脑、互联网之后,信息技术的重大革新,为我市打造世界城市、落实《科技北京行动计划》、加快建设中关村国家自主创新示范区,促进软件和信息服务业的新发展,提供了重大的发展机遇。
为了加快推进北京云计算相关产业的发展,力争在新一轮信息技术的国际竞争中抢占先机,占领高端,形成新优势,市发展改革委、市经济信息化委和中关村科技园区管委会,组织北京云计算领域的骨干企业,制定本行动计划。
二、指导思想和总体目标
(一)指导思想
以科学发展观为指导,以培育新业态、发展新商业模式、构造新产业链为核心,形成应用带动,创新驱动、面向全球的发展模式,建立政府引导、企业主体、产学研联合的发展机制,积极参与国际间竞争合作,有效整合高端人才、风险投资、产业基地、创新性企业等产业发展要素,推动我市云计算产业早起步、快发展、上规模,成为北京战略性新兴产业的突破口。
(二)实施原则
服务引领。面向建设世界城市的信息化要求,以全面提升城市信息化基础设施能力为契机,在政务信息化、社会信息化和经济信息化领域,加快建设一批云应用示范工程,培育云服务市场,引导下游产业发展。
产业链联动。围绕芯片、硬件、网络、运营商、终端及各种云应用,构造完整的云计算产业链条,带动我市信息技术产业的整体提升。
国际同步。积极利用好国际开源社区资源,充分发挥海外华人云计算人才和企业家作用,加大引入和并购国外云计算企业力度,以国际化水准,高起点规划和实施祥云工程。
走自主道路。以技术创新和商业模式创新为动力,掌握关键核心技术,鼓励发展新型业态,支持研制自主的标准和规范,支持探索符合中国客户要求和习惯的服务模式和业务模式。
(三)总体目标
形成技术、产品和服务一体化发展的产业格局,发展一批高效能、高安全、低成本的云服务,聚集一批世界领先、全国领军的云计算企业,形成一批创新性的新技术、新产品、新标准。到2015年云计算的三类典型服务(IaaS、PaaS、SaaS)形成500亿元产业规模,带动产业链形成2000亿元产值,云应用水平居世界各主要城市前列,成为世界级的云计算产业基地。
三、主要任务
(一)重点发展领域
1.云计算适用芯片和软件平台
加快低功耗芯片、新型嵌入式软件系统、云计算软件平台的开发和产业化,为云计算的发展提供产业基础支撑,形成十余家掌握云计算的核心技术的骨干企业群体。
2.云服务产品
以云计算的基础设施服务(IaaS)、平台服务(PaaS)、软件服务(SaaS)等3个服务线为核心,整合我市信息服务业资源,促进现有的电信运营商、软件提供商、信息服务提供商、内容提供商等的转型。形成十余家规模级IaaS企业,发展百家有独特技术价值、良好商业模式的PaaS和SaaS企业。
3.云计算解决方案
充分利用本市系统集成企业在传统软件领域行业解决方案优势,根据云计算技术、产品和服务特点,研发适合各行业的云计算解决方案,继续保持北京在云计算服务时代企业后台软件系统主力供应商的地位。
4.云计算网络产品
以新架构服务器、新一代网络存储系统、下一代网络设备、新型计算单元为重点,积极扩大同国际企业的合作,特别利用好华人企业家的资源,构建我市云计算网络产品体系,创建十余个新的优势品牌。
5.云计算终端产品
积极发展下一代移动通信终端、移动互联网智能设备、平板电脑、电子书、感知终端等多种云计算终端产品,支持形成以新型终端为龙头的、集硬件、软件、服务、内容与一体的云计算产业生态圈,培育百亿元级别的产业链。
(二)重点发展技术方向
1.集中力量攻关云计算核心技术。配合国家科技重大专项的实施,在虚拟化技术、高性能存储技术、新一代搜索引擎、云计算安全技术、网络操作系统、云计算服务测试验证技术上争取有所突破。
2.积极发展云计算同其他新兴技术的融合。支持把云计算技术同物联网应用、下一代互联网应用、三网融合应用相结合,配合新兴技术的发展规划,发展支持智能电网、节能减排、移动互联、位置与导航服务、电子商务服务、大规模视频采集和播发等新兴业务所需要的云计算能力和软件云处理能力。
3.大力发展绿色IT技术。采用新一代低功耗技术,加大与服务器、存储设备及交换机等高耗能设备相关的绿色技术开发和运用;充分利用云计算的集中计算机制,大幅降低信息化应用和软件信息服务业的能耗,达到在云—-端两方面的节能减排目的。
(三)重点工程
1.云应用重大示范工程
以“祥云工程”的实施为抓手和推进平台,在电子政务、重点行业应用、互联网服务、电子商务等主要应用方向上实施一批不同层次和功能的云计算重大工程。推动电子政务全面向云时代转型,统一规划和抓紧建设“政务云”,带动全市云应用的起步。支持社会机构和企业,利用云计算创新的服务模式,建设和运营面向经济和社会发展的公有云,包括行业云和区域云,迅速形成面向市场的社会化应用。
2.云计算产业基地建设工程
在中关村核心区规划建设北京云计算产业基地,加强政府战略引导,聚集一批云计算科研机构和产业链各环节的核心企业;依托产业基地,建设云计算公共测试和验证平台,云计算产品和服务演示体验中心等重大公共服务平台,引导基地建设成为云服务产品创新中心、技术交流中心、应用示范中心和服务运营中心,成为全球有影响力的云计算产业基地。
3.云服务标准规范创制工程
积极参与国际云计算标准工作,组织参与“祥云工程”的骨干企业研究利用好国际上已有的成熟标准和规范,开放性地做好云计算标准体系的构建工作。积极开展自主标准和规范的创制工作。力争在云安全、服务能力与质量、开放接口、体系架构评估认证等环节形成具有自主知识产权的标准。
4.面向云计算的技术改造工程
充分利用存量资源,支持传统的IT企业围绕云计算技术开展新产品开发、新服务体系构建、新技术架构升级等技术改造工作。引导软件和信息系统集成企业通过技术改造,大力发展云平台的咨询设计能力、集成建设能力和云服务能力;运营商、互联网信息服务和内容服务企业,要通过技术创新和模式创新,加快向云服务方向转型。
四、保障措施
(一)成立“祥云工程”联合工作组,加强统筹协调。
市经济信息化委、市发展改革委、中关村管委会联合牵头,组织云计算产业链中的代表性企业成立联合工作组,制定北京云计算产业发展路线图,在政策制定、标准研究、产业联合、科研攻关等方面加强统筹推进力度。
组建“祥云工程”专家委员会,为祥云工程的实施提供决策支持。
举办北京云计算产业发展国际高层论坛,打造政府与国内外企业家互动的高端平台,成为引进国外智力和项目的重要渠道。
(二)建立“祥云工程”重点项目库,加大资金投入力度。
对“祥云工程”的重点项目实行政府全程跟踪服务,定期进行调度协调,择优给予重点支持。将支持祥云工程的资金列入政府统筹重大科技资金和产业资金的支持范围,采取资本金注入、贷款贴息、投资补助等方式,支持云计算的技术改造项目、成果产业化项目、产业基地建设项目等。积极争取祥云工程的重点项目得到国家各种专项资金的支持。充分发挥政府投入对社会投资的引导带动作用,引导风险投资支持北京云计算相关产业。
(三)完善政策体系,创造良好云计算产业发展环境。
将云计算作为我市新时期电子政务的重要模式加以推广,在市区县各级政府部门积极开展云计算应用。
将云服务模式纳入政府采购。在政府部门和公用事业的信息化应用中采购云服务,以政府采购引导云计算的发展。
政府支持建设一批服务中小企业两化融合、产业结构调整的云平台。对于提供北京工业企业、中小企业和高新技术急需的云计算应用服务,如小型企业的信息化平台、工程计算平台、信息情报平台、音视频开发制作平台等,可以根据北京企业使用云服务数量和品质,给以相应的支持。
(四)对接国家资源,积极争取相关项目在北京率先试点应用。
积极争取国家发展改革委、科技部、工信部等国家部委的支持,组织协调本市企业承接云计算相关的国家重大科技攻关和产业化工程,推动国家重大专项在北京落地,对承接国家项目的企事业单位优先给予地方政府的配套支持。整合资源,创造条件争取国家重大工程在北京率先应用试点。
(五)加强高端人才培养和引进力度。
协助企业引进云计算领域国际优秀人才,充分利用好中央“千人计划”和本市“海聚工程”的平台,引进一批掌握前沿技术的创新创业人才,2010年到2015年通过“千人计划”和“海聚工程”引进30名云计算领军人才。
加强对国内云计算人才的引进和奖励政策,将云计算领域纳入人才引进目录,并将重点云计算企业纳入高级人才奖励范围。
(六)发挥中介组织作用,鼓励发展机制创新。
支持云计算企业发起组建云计算产业联盟等中介组织。鼓励中介组织制定行业公约规范,加强行业自律。引导产业链内部各种形式的资源整合和横向联合,创新产业发展机制,形成集团化、组织化的推进模式,培育产业高端创新集群。
五、结语
云计算是新一代信息技术变革的核心,是战略性新兴产业的发展引擎。云计算的发展将改变CPU、存储、服务器、终端、操作系统及应用软件的整条信息产业链,并深远地影响从生产到生活的信息化应用。“祥云工程”作为北京市发展战略性新兴产业的重要工程,将以云计算技术的兴起为新契机,抢占新的战略制高点,全面优化和提升北京信息技术产业,使北京成为中国乃至全球的云计算中心。

welcome-file-list失效

welcome-file-list是一个配置在web.xml中的一个欢迎页,用于当用户在url中输入工程名称或者输入web容器url(如http://localhost:8080/)时直接跳转的页面。碰到一个问题是访问报404找不到的错误,welcome-file-list没有起作用。
welcome-file-list的工作原理是,按照welcome-file的.list一个一个去检查是否web目录下面存在这个文件,如果存在,继续下面的工作(或者跳转到index.html页面,或者配置有struts的,会直接struts的过滤工作)。先去webcontent(这里是Eclipse的工程目录根目录)下是否真的存在index.html这个文件,如果不存在去找是否存在index.jsp这个文件,依次类推。

<welcome-file-list>
	<welcome-file>VideoRankAction!goVideoRank10.action</welcome-file>
</welcome-file-list>

welcome-file不一定是html或者jsp等文件,也可以是直接访问一个action。但要注意的是,一定要在webcontent下面建立一个index.action的空文件,然后使用struts配置去跳转,不然web找不到index.action这个文件,会报404错误。
如何不让地址栏显示后缀,只展示域名:
1、在没进行这样设置之前,当在地址栏中输入域名地址:http://www.xxxxx.com,浏览器解析后,地址栏会出
现http://www.xxxxx.com/xxx/xxx.do。
2、经过如上设置之后,输入http://www.xxxxx.com后,不再会出现后面的后缀。
做法:
1、在web.xml中,定义welcome-file为一个jsp页面:

<welcome-file-list>
        <welcome-file>/index.jsp</welcome-file>
</welcome-file-list>

2、然后在该jsp页面中引入需要显示的action,如:

<c:import url="/xxx.do"/>

注:在表头需要导入jstl包

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>