为什么需要持续集成?

偶尔想起学生时,几个同学一起做项目,虽然不大,但还是分了模块,每个人负责一个或多个模块。每天打开电脑,从版本控制库更新最新代码,运行下程序,回忆昨天完成的并计划今天的任务。突然发现昨天好好的功能,今天突然不work甚至抛出异常,于是大喊一声谁破坏了我的功能?没人回应,只能自己一步一步查看到底那个代码破坏了昨天的功能,然后找到提交的同学,再商量到底该怎么解决。其实当时我们各自实现自己的模块,写(或不写)测试,然后提交代码,各自只关心和测试自己的模块,难免会发生冲突,尤其是两个模块结合的地方,如果被破坏方可能在几个小时,几天甚至更长时间后发觉应用被破坏,或者直到最后项目上线前一分钟才发觉。。。


后来参加工作,知道了持续集成可以解决当时的痛苦,而且可以提供更多。持续集成是XP实践之一,于是,很多人错误认为持续集成是为了实现敏捷编程的。实际上,早于敏捷编程概念的提出,持续集成作为一个best practice就已经被很多公司采用了,只不过作为一个概念,则是由Martin大叔为了推进敏捷所倡导并由此风靡起来。

那么为什么我们需要持续集成或者说持续集成给我带来了什么好处呢?

  • 及早获得系统级的成果或者是可以deploy到production环境的产品。因为代码已经被集成起来了,即使整个系统还不是那么可用,至少比零散的模块让人更有安全感。增强了客户和我们自己的信心。
  • 可以很容易的知道每一个版本之间有什么变化,甚至我们可以很容易的重新build出任何一个时间点上的版本,这点非常重要。工作后第一个项目就遇到了难缠的客户,经常今天告知我们明天需要一个版本部署到生产环境,如果没有持续集成,我们如何得到有信心可以部署到production的build?如果没有持续集成,我们如何提供包含最新功能的可以部署到production的build?偶尔,客户并不需要你提供的最终build,而是包含功能1和2而不包含功能3的build,如果没有频繁的持续集成,我们又怎么快速的得到某一时间的build?
  • 及早的发现和定位错误:比如学生时代的项目,某个人在工作的时候踩进了别人的领域、影响了别人的代码,而被影响的人还不知道发生了什么,于是bug就出现了。这种bug是最难查的,因为问题不是出在某一个人的领域里,而是出在两个人的交流上面。随着时间的推移,问题会逐渐恶化。通常,在集成阶段出现的bug早在几周甚至几个月之前就已经存在了。结果,开发者需要在集成阶段耗费指数倍的时间和精力来寻找这些bug的根源。如果有了持续集成,当持续集成失败了,很容易说明最新加入的代码或者修改的代码引起了错误,当然这需要强大的自动化测试作为后盾。
  • 项目进度的一目了然。频繁的集成使我们可以看到哪些功能可以使用,哪些功能还没有实现。开发人员不用在汇报任务的时候说我完成了多少百分比而烦恼,而PM也不再烦恼程序员说完成了编码的50%到底是个什么概念。

因此引用同事的原话“没有持续集成,说明交付流程是混乱不清晰随机的。”

posted @ 2011-03-31 23:33 *** 阅读(637) | 评论 (1)编辑 收藏

JS-函数读书笔记

1.函数就是对象
函数可以存放变量,数组,对象
函数可以当做参数传递给其他函数
函数可以返回函数
函数可以拥有方法

2.函数的调用
调用运算符是任何一个产生函数值的表达式之后的一对圆括号。
调用一个函数将暂停当前函数的执行,传递控制权和参数给新函数,除了声明时定义的形参,每个函数接收两个附加的参数-this和arguments。
this的值取决于调用的模式:

方法调用模式:函数被保存为对象的一个属性时,称为方法。方法被调用时,this绑定到该对象。
var obj={
    value:0,
    increment:function(){
        this.value+=1;
    }
}
obj.increment();
obj.value;

函数调用模式:当一个函数并非对象的属性时,它被当作一个函数来调用,this绑定到全局对象(Window),因此不能共享外部函数对对象的访问权-语言设计的错误。当内部函数被调用时,this应该仍绑定到外部函数的this变量。
解决方案:定义一个变量并给它赋值为this
var myObj={
    value: 0,
    getValue: function(){
        return this.value;
    }
};
myObj.test=function(){
    var that = this
    var helper=function(){
        that.value+=1;
    }();
}
myObj.test();

构造器调用模式:如果一个函数前面带上new来调用,将会创建一个隐藏连接到该函数的prototype成员的新对象,this将会绑定到新对象上。
var Quo = function(string){
    this.status=string;
}
Quo.prototype.get_status=function(){
    return this.status;
}

var myQuo=new Quo("confused");
myQuo.get_status();

apply调用模式
:apply方法让我们构建一个参数数组并用其去调用函数。该方法允许我们选择this的值,apply方法接收两个参数,第一个就是将被绑定给this的值,如果是null则为Window对象,第二个是参数列表。
var array=[3,4];
var sum=add.apply(null,array);

var Quo = function(string){
    this.status=string;
}
Quo.prototype.get_status=function(){
    return this.status;
}
var statusObj={
    status:"OK"
};
Quo.prototype.get_status.apply(statusObj);

var myObj={
    add:function(a,b){
        //参数为null或#ff0000,this引用为Window, double函数的alert证明了这一点
        alert(this.location.href);
        return a+b;
    }
}
var array=[3,4];
var sum=myObj.add.apply(null,array);

2.给类型增加方法:待续

3.闭包-能够读/写函数内部的某些变量的子函数,并将这些变量保存在内存中.

讨论闭包之前先讨论函数作用域
1)函数内的局部变量在函数外部无法访问。
function f(){
    var n=1;
}
alert(n);//error

2)内部函数可以访问外部函数的局部变量。
function f(){
    var n=1;
    function inner(){
        alert(n);
    }
}
因此只要将inner作为返回值,就可以在f外部读取它的内部变量了。
function f(){
    var n=1;
    function inner(){
        alert(n);
    }
    return inner;
}
f()();
闭包就是将函数内部和函数外部连接起来的一座桥梁。如果把父函数当作对象使用,把闭包当作它的公共函数,把内部变量当作它的私有属性。

理解内部函数能访问外部函数的实际变量而无须复制:
var add_handles=function(nodes){
    for(var i=0;i<nodes.length;i++){
        nodes[i].onclick=function(e){
            alert(i);
        }
    }
}

var add_handles=function(nodes){
    for(var i=0;i<nodes.length;i++){
        nodes[i].onclick=function(i){
            return function(e){
                alert(e);
            }
        }(i);
    }
}


闭包用途:
1)读取函数内部的局部变量
2)将变量的值始终保存在内存中,因为f是inner的父函数,而inner被赋给了一个全局变量,导致inner始终在内存中,而inner的存在依赖于f,因此f也始终在内存中,不会在调用结束后被垃圾回收机制回收。
3)通过保护变量的安全实现JS私有属性和私有方法(不能被外部访问)

缺点:闭包使得函数中的变量都被保存在内存中,内存消耗很大,不能滥用闭包。

4.模块
一个定义了私有变量和函数的函数,利用闭包创建可以访问私有变量和函数的特权函数,然后返回这个特权函数。
提供接口却隐藏状态与实现的函数或对象。摒弃了全局变量。


posted @ 2011-02-21 14:38 *** 阅读(345) | 评论 (0)编辑 收藏

重构与设计模式

1. 状态模式
对象行为的行为依赖于它所处的状态,对象的行为随着状态的改变而改变。解决不同状态下,行为不同的问题。

问题:

左边树状结构,选择不同节点,右边Viewer显示该节点下图片;左边树下方search框,右边Viewer显示满足search条件的图片。抽象出Viewer对象,有两个不同状态view和search,不同状态下更新Viewer的方式不同,即
tree.onselect -> state="view"
search -> state="search"

if(state="view"){
  updateView(path,start,limit)
}else if(state="search"){
  updateSearch(path,start,limit,searchCriteria)
}

Viewer, search, tree耦合在一起,需要全局变量state。

解决方案:

抽象两个状态对象
viewAction ->  updateView(path,start,limit)
searchAction -> updateSearch(path,start,limit,searchCriteria)

Viewer对象
变量 updateAction
方法 setUpdateAction(action)
方法 update()调用 -> updateAction()

状态改变时,改变所选的状态对象
tree.onselect -> Viewer.setUpdateAction(viewAction)
search -> Viewer.setUpdateAction(searchAction)

Viewer, search, tree解藕,去除了全局变量state,行为局域化。假如以后加入view,search外的其他状态,只需增加newAction状态对象,并在调用处添加Viewer.setUpdateAction(newAction),便于扩展,无需改变现有代码。

2. 不知道该叫什么模式
 
问题:右键事件
if(action=="addTag"){
  addTag()
}
if(action=="replaceTag"){
replaceTag()
}

if(action=="addSubjectTag"){
addSubjectTag()
}

if(action=="addCredit"){
addCredit()
}

增加新事件需要添加新的if语句

--------------------------->
中间过程
var items={
  "addTag":addTag,
  "replaceTag":replaceTag,
  "addSubjectTag":addSubjectTag,
  "addCredit":addCredit
}

perform(){
 items[action]()
}

--------------------------->
事件注册,提供注册接口
var items = {}

perform(){
 items[action]()
}

register(option){
 items.add(option)
}

增加右键事件时,只需自行注册,事件的执行与事件本身完全解藕,同时新事件加入时,只需注册,无需改变现有代码。
regsiter({"addTag":addTag})
regsiter({"replaceTag":replaceTag})
regsiter({"addSubjectTag":addSubjectTag})
regsiter({"addCredit":addCredit})


posted @ 2011-01-23 17:52 *** 阅读(262) | 评论 (0)编辑 收藏

读Roy Fielding的REST风格架构笔记

REST顾名思义:Representational State Transfer(表述化状态转移)。 REST两个基本概念:

  1. 资源(Resource):将信息抽象为资源,任何能够命名的信息(包括数据和功能)都能作为一个资源,一张图片,一份文档,一个服务(如上传图片),一个其他资源的集合等。 资源是到一组实体的概念上的映射,而不是在特定时刻与该映射相关联的实体的映射。例如,“最新上传的图片”是一个值经常变化的映射,但是“2011/1/9上传的图片”的映射是静态。它们是截然不同的资源,即使某一时刻它们可能会映射到相同值的集合。
  2. 表述(Representation): 一个资源当前或预期的状态,资源是一个随时间变化的函数,该函数将时间t映射到一个实体或值的集合,集合中的值可能是资源的表述。REST组件通过URI来获得资源的表述并对资源执行动作,并在组件间传递该表述。

举购物网站系统的例子,products通过加入购物车到orders,经过付款订单到purchase,然后到delivery。其中productsorders是资源,可以通过/products?color=green/orders/2007/11表示;而purchasedelivery是资源productsorders某一时间的状态。应用程序如同资源和表述组成的虚拟的状态机,通过不断的获取资源的表述来转变应用程序的状态,即所谓的表述化状态转移。

REST风格架构的约束:

  1. 客户-服务器:分离关注点,将用户接口(如用户界面)和数据存储分离,如果接口不变,组件可独立进化。
  2. 无 状态:从客户端道服务器的每个请求必须包含理解该请求所必需的所有信息,不能利用任何存储在服务器上的上下文。提高了系统的可扩展性,其优点有三:可见 性,监视系统不必为了确定一个请求的性质而去查看请求之外的多个请求;可靠性,减轻了从局部故障恢复的任务量,可以快速定位;可伸缩性,不必在多个请求之 间保存状态,允许服务器快速释放资源,并且服务器不必跨请求管理资源。缺点是,由于不能将状态保存在服务器上的共享上下文中,增加了请求中发送的重复数 据,降低网络性能,因此有了约束三。
  3. 缓存:请求响应中的数据显示或隐式的标记为可缓存的不可缓存的。缓存可以为以后相同的请求重用这个响应的数据。但是缓存管理不好,会降低可靠性,导致缓存中陈旧的数据与直接访问服务器得到的数据差别很大。
  4. 统一接口:组件之间要有统一的接口,是REST风格架构区别其他风格架构的核心特征。REST由四个接口约束定义:资源的识别,web-based系统中资源由URI表示,数据库存储系统中资源还可以是XMLJSON等;通过表述对资源执行的动作:表述中包含操作该资源的信息,如增删改查,映射到HTTP协议的GETPOSTPUTDELETE方法;自描述的消息:消息中包含如何处理该消息的信息,如哪个sevlet处理,响应中包含可不可以被缓存等信息;作为应用状态引擎的超媒体。
  5. 分层系统:通过限制组件的行为,即每个组件只能看到与其交互的紧邻层,将架构分解为若干等级层,提高各层的独立性。
  6. 按需代码:客户端知道如何访问资源,但是不知道如何处理它,向服务器发送对于如何处理资源的代码的请求,接收这些代码,然后在本地执行代码。

 

posted @ 2011-01-09 23:20 *** 阅读(2078) | 评论 (0)编辑 收藏

Groovy"=="陷阱

Groovy与Java同是运行在JVM之上,大多数时候可以把Groovy当做Java来用,但是也存在着陷阱。

例如,想重写对象的equals方法,需如下步骤:
1. 使用==判断参数和自身对象引用是否相等,如果相等直接返回true(此判断可以提高性能);
2. 使用instanceof判断参数类型是否正确,如果不正确直接返回false
3. 将参数转换为正确的类型,步骤2已确保了类型
4. 检查参数与自身对象关键字段是否相等
    1)基本类型(除float和double)运用==比较
    2)对象引用,递归调用equals方法
    3)浮点型float,调用Float.floatToIntBits转化为int,用==比较
    4)浮点型double,调用Double.doubleToLongBits转化为long,用==比较

遵循以上步骤,重写简单Money类的equals方法:
1  public boolean equals(Object another) {
2     if (another == thisreturn true
3     if (another instanceof Money) {
4       Money anotherMoney = (Money) another
5       return amount == anotherMoney.amount
6     }
7     return false
8   }

调用
1 new Money(10).equals(new Money(10))

得到异常
java.lang.StackOverflowError
    at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
25)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:
86)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:
234)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:
1049)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:
880)
    at org.codehaus.groovy.runtime.InvokerHelper.invokePogoMethod(InvokerHelper.java:
746)
    at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:
729)
    at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.compareEqual(DefaultTypeTransformation.java:
620)
    at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.compareEqual(ScriptBytecodeAdapter.java:
680)
    at Money.equals(Money.groovy:
10)

最终发现罪魁祸首是“==”,Groovy中对于所有类型(基本类型和引用)调用“==”和“equals()”方法相同,当执行“another == this”时,解释器会调用“another.equals(this)”造成死循环。

如果想比较引用相等,Groovy提供了is方法,即
1  public boolean equals(Object another) {
2     if (another.is(this)) return true
3     if (another instanceof Money) {
4       Money anotherMoney = (Money) another
5       return amount == anotherMoney.amount
6     }
7     return false
8   }

posted @ 2010-12-25 00:29 *** 阅读(745) | 评论 (0)编辑 收藏

Christmas Eve, 我正式开博啦

2010.12.24晚,有点浪漫啊

希望今后在匆匆行走的路上,不要迷失了自我,偶尔,停下来,想想。

posted @ 2010-12-24 21:00 *** 阅读(85) | 评论 (0)编辑 收藏

仅列出标题
共2页: 上一页 1 2 
<2024年4月>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

导航

统计

常用链接

留言簿

随笔档案

搜索

最新评论

阅读排行榜

评论排行榜