《重构》第三章学习笔记

我们必须培养自己的判断力,来决定在什么时候进行重构。

1.1  Duplicate Code(重复代码)

如果你在一个以上地点看到相同的程序结构,那么将他们合而为一会更好。

1.2  Long Method(过长函数)

拥有短函数的对象会活得比较好,比较长。

间接层所能带来的全部益处:解释能力(可读性),共享能力(重用性),选择能力(?)。

现在OO 语言基本解决了函数调用所产生的开销。

“ 你应该更积极进去的分解函数。我们遵循这样一条原则:每当感觉需要以注释来说明点什么的时候,我们就把需要说明的东西写进一个函数中,并以其用途(而非实现手法)命名。我们可以对一组甚至短短一行代码(拥有复杂逻辑,难以理解)做这件事。哪怕替换后的函数调用动作比函数自身还长,只要函数名称能够解释其用途,我们也该毫不犹豫的这么做。关键不在于函数的长度,而在于“做什么”和“如何做”之间的语义距离。 ”

“如何确定该提炼哪一段代码?一个很好的技巧是:寻找注释。它们通常是指出“代码用途和实现手法间的语义距离”的信号。如果代码需要用注释来说明其用途,那么就要考虑把这段代码提炼成独立的函数,并且用注释来为此函数命名。”

复杂条件式和循环液常常是提炼的信号。

1.3  Large Class(过大类)

如果想利用单一的class 做太多的事情,其内往往会出现太多的 instance 变量。

如果class 中拥有太多的代码,也是“代码重复、混乱、死亡”的绝佳滋生点。

1.4  Long Parameter List(过长的参数列表)

过长的产生导致程序难以理解。

1.5  Divergent Change(发散式变化)

“ 一个class 受多个外界变化的影响 ”,则把这多个变化封装成一个新的类。即“ 将总是一起变化的东西放在一起 ”

针对外界某一变化所有相应的修改,都应该只发生在单一的class 中,而这个 class 的所有内容都应该反映该外界变化。总的思想就是,封装变化。这个地方和设计模式的想法是一致的。

1.6  Shotgun Surgery(散弹式修改)

和发散式变化不同,每次遇到变化,都要在多个class 中进行小的修改以响应之。他们分散在多处,很容易出错。

这里的主要思想是集中变化。

散弹式修改指的是,“ 一种变化引发多个class 的修改 ”,发散式变化指的是“ 一个class 受多个外界变化的影响 ”。

这两种情况下,通过重构, 使“外界变化”和“待修改类”呈一对一关系 的理想境地。

1.7  Feature Envy(依恋情节)

某个函数对其他类的数据的兴趣,高过对host class 的兴趣。即对其他的类的数据的依赖十分大。

1.8  Data Clumps(数据泥团)

数据泥团指的是总是绑定在一起出现的数据。

一个好的评断方法:删除众多数据中的一项数据,其他数据是否是因而失去了意义?如果他们不再有意义:你应该为他们产生一个新的对象。

形成新的对象后,可以根据Feature Envy 将一些操作移至此对象中。

1.9  Primitive Obsession(基本型别偏执)

建立多个很小,但是很灵活的对象。

1.10  Switch Statements( switch 惊悚现身)

使用面向对象编程,要少用switch 和 case 语句。而是用多态来替换它。

1.11  Parallel Inheritance Hierarchies(平行继承体系)

每当你为一个class 增加一个 subclass 的时候,必须为另一个 class 增加一个 subclass 。一般这两个 class 的前缀相同。

1.12  Lazy Class(冗赘类)

类显得多余,没有价值。

1.13  Speculative Generality(夸夸其谈未来性)

这个往往是过度设计的结果:对某种变化的应对,而这种变化没有发生。

1.14  Temporary Field(令人迷惑的暂时值域)

变量只在特定的情形下有效,而并不是所有的情况下有效。很多情况下,这些值域应该不属于此class ,而应该单独的提取成新的类。

1.15  Message Chains(过度耦合的消息链)

用户向一个对象索取另一个对象,然后在向后者索求另一个对象,然后在索求另一个对象——客户与查找过程的航行结构紧密耦合。

1.16  Middle Man(中间转手人)

对象的基本特征之一就是封装——对外部世界隐藏实现细节——封装往往伴随委托。委托的过度运行,就导致了Middle Man 。

1.17  Inappropriate Intimacy (亲密关系)

两个class 之间的关系过于亲密。比如,花大量的时间探究彼此的 private 成分。

1.18  Alternative Classes with Different Interface(异曲同工的类)

类名不同,但是功能相似。

1.19  Incomplete Library Class(不完美的程序类库)

基础类库无法满足实际的需求。

1.20  Data Class(纯稚的数据类)

它们拥有一些值域,以及用于访问(读写)这些值域的函数,除此之外一无长物。

1.21  Refused Bequest(被拒绝的遗赠)

子类不像继承父类的函数和数据,这往往是继承体系的错误。

如果子类复用父类的行为,但又不愿支持父类的接口,这种情况下Refused Bequest 的坏味道会很强烈。

1.22  Comments(过多的注释)

注释其实是一种香味,更多的情况下它被用作除臭剂:即代码中出现坏味道(设计糟糕的代码),然后用注释“除臭”。这个时候我们应该对这些坏味道的代码进行重构,然后,你会发现注释变成了多余的。

当你感觉需要注释,请先尝试重构,试着让所有的注释都变得多余——代码本身就是自注释的。

注释可以用来记述“为什么做某事”、“打算做某事”、“无十足把握的区域”,而不必记录“怎么做”。

posted @ 2009-10-12 20:15 常高伟 阅读(781) | 评论 (0)编辑 收藏

以前做过一个产品,共分为三层:平台层,应用服务器层,应用层。其中有一个业务流程,实现是在应用层,但它那里的信息不全,需要通过应用服务器层向平台层获取必要的业务数据,然后通过应用服务器层控制业务流程。当时考虑这个结构的时候,主要的出发点就是业务和控制分离,将业务处理从平台层剥离开来。当时,在具体是实施过程中,我们工程师对这种结构抵触心理很强烈。他认为我们的业务开发非常的繁琐,而且经常要贴“狗皮膏药”。

先抛开上面这个实例的设计思路,这里面反映出一个问题:软件开发过程中,软件体系结构同样需要“重构”

结合经典的《重构》,这里简单的写一下软件体系结构重构的定义,原因,设计,方法。仅作抛砖引玉,希望能和大家一起思考。

何谓重构

对软件体系结构的一种调整,目的是在不改变其“外在行为”的前提下,调整其结构,使其易于修改,维护和理解。

为何重构

1、使整个系统易于添加新的功能。为系统添加新功能将会非常的容易。

2、调整系统中各个模块的功能,角色,使整个系统更容易理解。
何时重构

由于系统结构的重构成本非常高,所以要选择一个合适的重构时机。

1、为系统添加功能时重构。此时项目进度压力如果非常大,这放弃此时重构。

2、软件第一个版本开发完毕后重构。在第一个版本开发完毕,第二个版本开发之前,根据第一个版本的开发经验,对系统进行重构。

3、开发出系统原型时进行重构。开发出一个系统的原型的时候,如果发现系统需要重构,这及时的进行,这个时候重构成本较低,但对是否重构决策要求较高。

重构的必要条件

重构之前必须为软件系统建立一个可靠的、自动化的功能测试环境,这样才能有效防止重构带来的危害。好的测试时重构的根本。重构之前,首先检查自己是否有一套可靠的测试机制。这些测试必须有自我检验(selfchecking)能力。

重构与设计

系统架构层次的重构,因为重构的成本相对较高,所以预先设计的程度要相对较深,要尽量考虑系统可能遇到的情况,方案要适当的灵活和强固。尽量减少系统结构的重构。

软件体系结构的重和代码的重构的区别

1、针对的层次不同:一个是系统结构层次的,一个是代码层次的。

2、重构成本不同,系统结构的重构成本相对较高。

3、是否重构的决策者不同。

4、重构的时机不同。

posted @ 2009-10-12 20:14 常高伟 阅读(332) | 评论 (0)编辑 收藏

在学习的不同的阶段我们应该保持的心态:
1、接触之前,拥有一个好奇的心态。与自己原有的知识对比,不要对新的知识产生偏见。比如,原来一直采用瀑布式软件开发,在接触敏捷软件开发之前,不要对敏捷软件开发模式产生偏见:“敏捷开发根本不能够和瀑布式开发相提并论”。偏见会阻碍新知识的学习,而是要对敏捷开发保持一种好奇心:

1)这种理论为什么会提出来?
2)主要为了解决什么问题?
3)现有的理论无法解决此问题吗?
4)它是基于什么样的原理?
5)它是如何运行的?

2、学习了一段时间,拥有一定基础之后,要以一种批判的、怀疑的心态来对待学习的知识。当你拥有了对新知识的一定认识后,有可能会发现它很好解决了你以前遇到的一些非常棘手的问题,或者发现你找到了某一领域问题的完美的解决方案。这个时候,你要让自己坚信,“没有银弹”,没有包治百病的灵丹妙药。任何方案、理论、技术都有其依赖的条件,或者在解决原有问题后,引入的新的问题。此时,你要以一种批判的、怀疑的态度来对待它。要搞清楚:

1)它所依赖的前提条件是什么?
2)应用它的时候有没有什么假设?
3)它在那些场景下适用、那些场景下不适用?
4)它在解决原有问题之后,有没有引入新的问题?

3、当你达到专家级或顶级水平之后,应该持有一种“独孤求败”的心态:寻求新的挑战,争取“百尺竿头更进一步”,就像独孤求败一样,”闯荡江湖,只求一败 “:把每次的失败看成进步的机会。同时保持敬畏、谦虚的心态,你所掌握的知识,也不是”银弹“,也有其局限之处,或者随着环境的发展,已经无法适应环境,需要寻求新的突破。这个时候,你要根据你丰富的实践,提出创新的思路或知识。
总之:
1、在学习新知识、新领域的时候,要保持一个空杯的心态,只有这样才能为自己敞开自我提升之路。
2、偏见导致对新知识的排斥,教条倒是对新知识的盲从。
3、没有绝对的真理,也没有绝对的谬论。绝对的真理让我们盲从,绝对的谬论让我们偏见。

posted @ 2009-10-12 20:13 常高伟 阅读(123) | 评论 (0)编辑 收藏

1  构筑测试体系

如果你想进行重构,首要前提就是要拥有一个可靠的测试环境。

“编写优良的测试程序,可以极大的提高我的编程速度,即使不进行重构也是如此。”

1.1  自我测试代码(Self-testing Code )的价值

“Class 应该包含他们自己的测试代码。”

“每个Class 都有一个测试函数,并用它测试自己这个 Class 。”

确保所有的测试都完全自动化,让它们检查自己的测试结果。

只要写好一点功能,就立即添加测试。

一整组(a suite of )测试就是一个强大的“臭虫”侦测器,能够大大缩减查找“臭虫”所需要的时间。

“实际上,编写测试代码的最有用时机是在开始编程之前。当你需要添加特性的时候,先写相应的测试代码。听起来离经叛道,其实不然。填写测试代码其实就是问自己:添加这个功能需要做什么。编写测试代码还能使你把注意力集中于接口而非实现上头(永远是件好事)。预先写好的测试代码也为你的工作按上一个明确的结束标志:一旦测试代码运行正常,工作就可以结束了。”

构建自我测试的代码。

1.2  JUnit测试框架( Testing Framew )

频繁的运行测试,每次编译请把测试也考虑进去,每天至少执行每个测试一次。

单元测试和功能测试

“每当你接获臭虫提报,请先撰写一个单元测试来揭发这只臭虫。”——如何揭发?这里需要根据报告准确定位。单元测试会对此有帮助吗?

1.3  添加更多的测试

“观察Class 该做的所有事情,然后针对任何一项功能的任何一种可能失败的情况,进行测试。”

“测试应该是一种风险驱动(risk driven )行为,测试的目的是希望找出现在或未来的可能出现的错误。”

“测试的诀窍是:测试你最担心的部分。”

这点和我目前的想法不大相同。我目前的想法是,测试要对程序做100% 的保证,所以,要测试程序可能行为的每一种情况,保证其正确性。按照我的想法,值域的设置和访问函数也是要测试的。作者的意思是,测试代码要用最低的成本,获取最大的收益。这一点,要我在实际的环境中进行抉择。

“编写不是十分完美的测试并实际运行,好过对完美测试的无尽等待。”——我持怀疑态度。

运用测试用例前后执行的函数:tearDown 和 setUp ,保证测试用例之间相互隔离,而非相互影响。

做一个懒惰的程序员——。

考虑可能出错的边界条件,把测试火力集中在那儿。

“测试(优先)可以调高编程速度”,这一点我要在实践中验证一下,如果真是这样,那我就要尝试在我们部门推行这种方法。

“当测试达到一定的程度后,测试效益会呈现递减态势。”所以,你不要期望通过测试找出所有的bug ,而是要通过测试,找出绝大多数的 bug 。

这个地方其实也符合“二八定律”:即20% 的测试可以找出 80% 的 bug ,其余的 80% 的测试可以找出剩下的 20% 的 bug 。我们要做的,就是写这 20% 的测试,而非 100% 的测试。

posted @ 2009-10-12 20:11 常高伟 阅读(140) | 评论 (0)编辑 收藏

上一篇介绍了微内核流程引擎开发背景,这篇介绍它的功能描述。

基本功能:

1、能够通过脚本定义流程,更改流程。

2、对软交换系统应用服务器的所有的接口都可以编辑。

3、异常处理,实现补偿机制。

4、流程要支持:顺序执行,分支处理,跳转执行。

5、脚本中支持简单的数据库操作,比如:记录查询(根据查询结果决定流程),字段查询,记录增删改。


扩展功能:

1、提供多种调用形式:1)动态链接库直接调用;2)socket通信调用;3)远程调用;4)WSDL方式调用。

2、实现一个流程引擎虚拟机。专门处理流程。

3、支持业务以无状态的形式开发。所有的状态在脚本中定义。

4、开发一个流程编辑界面。

5、开发一个脚本编译器,检查脚本的错误。

6、开发一个简单的语言,实现快速流程编辑的功能。这里要实现一个编译器,编译结果就是流程脚本。

7、实现一个方向编译器,从流程脚本到流程开发语言。

上面的这些功能有的已经实现,有的正在实现。后面我会详细描述这些功能的设计与实现。

posted @ 2009-10-12 20:08 常高伟 阅读(238) | 评论 (0)编辑 收藏

我设计的流程引擎是脚步驱动的。脚本中定义了流程执行的环境,流程操作的对象,流程执行的步骤。下面是一个流程脚本的示例:

<?xml version="1.0" encoding="utf-8"?>
<process name="make_call">
    <data type="user_tel">called_number</data>
     <object type="user" id="global_data:called_number" operation="must">obj_user</object>
     //用户对象描述中,号码是必须的,是流程引擎和业务的交互唯一标识,callid是可选的。
    <object type="user" object_num="global_data:called_number" operation="must">obj_user</object>
    <sequence name="make_call">
        <invoke interface="make_call" node="make_call_001" object_user="obj_user" calling_number="6699" original_number="123456" call_type="local_call">

        </invoke>
        <invoke interface="play_voice" node="play_voice_001" object_user="obj_user" play_long="100" play_file="/home/welcome.au">

         </invoke>   
    </sequence>
</process>


脚本的含义

1、process的name属性表示流程的名称,用于在程序中调用。

2、<data type="user_tel">called_number</data>表示定义了一个流程的全局外部变量。有程序在调用流程是作为流程数据传送给流程。这个数据要在后面的流程中使用。

3、<object>部分在流程中定义流程操作的对象。一般分为用户和会场。这里表示是用户。属性“id”表示对象的唯一标识。这里引用的是流程的全局数据:global_data:called_number,也就是在上面定义的数据。属性“operation”表示此对象是可选还是必选。如果是必须,这如果此对象被释放,这流程也要被被结束。否则,不结束。中间的内容表示对象在流程中的唯一标示,这里是obj_user,后面的节点可以通过使用它来操作对象。

4、<sequence>表示顺序调用下面的节点。

5、<invoke >表示调用节点。属性“interface="make_call"”表示此节点调用的接口是make_call。make_call是在代码中定义好的关键字,对应一个软交换系统的接口。属性“node”表示节点的唯一标识,在流程内部唯一,可以在流程跳转的时候使用。 “object_user="obj_user"“表示make_call 接口操作的对象。有<object>创建。 calling_number="6699" original_number="123456" call_type="local_call"表示的是make_call接口调用时的数据。

6、<invoke interface="play_voice"表示对此对象进行放音。

这个脚本的意思是,根据流程输入的号码,创建用户对象,并且发起呼叫,对用户进行放音。


复杂的脚步定义:

上面的是一个简单的示例。为了能够实现流程编辑,要考虑很多的情况,要能够进行分支处理,跳转执行,捕获事件等。

1、分支的实现

    <recive event="user_key" ="" node="receive_key" object_user="obj_user" time_out="10"></recive>
    <switch condition_type="user_key" object="obj_user">
            <case condition="9092">
                <sequence name="d">
                </sequence>
            </case>
            <case condition="time_out">
                <sequence name="d">
                </sequence>
            </case>           
            <otherwise>
                  <sequence name="">
                      <goto node="play_voice_001">goto_001</goto>
                  </sequence>
            </otherwise>
    </switch>

1)<recive event="user_key"表示接受指定用户的按键。如果超过10秒为收到按键则认为用户按键结束。

2)<switch condition_type="user_key"表示一用户的按键为分支条件,进行分支处理。

3)<case condition="9092">表示如果用户的按键式0092的话则进入此分支进行处理。

4)<case condition="time_out">如果超时为收到用户按键,这进入此分支处理

5)<otherwise>如果上面的条件都不满足,则进入此分支处理。


2、跳转的实现:

<goto node="goto_001" next_node="play_voice_001"></goto>

表示此节点是一个跳转节点,要跳转到的下一个节点是play_voice_001。


3、信号捕获的实现:

       <pick name="pick_001" time_out="10">
            <on_event event="on_ring_180" result="success" reason="normal">
                <sequence name="008">
                </sequence>
            </on_event>
            <time_out>
                <sequence name="008">
                </sequence>
            </time_out>
            <otherwise event="on_ring_180:on_ring_183">
                  <sequence name="009">
                    </sequence>
            </otherwise>
        </pick>

1)<pick name="pick_001" time_out="10"><pick>活动会等待一组相互排斥事件中的一个事件的发生,然后执行与发生的事件相关联的活动。它会阻塞业务流程执行,以等待某一特定的事件发生,比如接收到一个合适的消息或超时警报响起。当其中任何一个事件被触发后,业务流程就会继续执行,pick也随即完成了,不会再等待其他事件的发生。

2)<on_event event="on_ring_180" result="success" reason="normal">表示如果收到的on_ring_180,且结果是success,原因是normal。触发此流程的处理。

3)<time_out>表示超时为收到制定事件的处理。

4)<otherwise event="on_ring_180:on_ring_183">表示收到其他的事件,比如:on_ring_180或on_ring_183,都进入此分支处理。

posted @ 2009-10-12 20:08 常高伟 阅读(180) | 评论 (0)编辑 收藏

开发背景

我们公司是主要从事企业语音方面产品的开发,主要产品比如:调度系统,指挥系统,电话会议系统,呼叫中心系统等。这些系统都有一个共同特点,就是涉及到呼叫,放音,收发按键,会场操作。我们的业务产品都是基于我们的软交换系统之上构建的,软交换系统的应用服务器向外提供这些服务。


产生的问题

我们在开发的过程中就发现一个问题,每个产品在此接口上都会做很多重复的开发,特别是在IVR处理上面。

IVR (Interactive Voice Response)即交互式语音应答,可以提高呼叫服务的质量并节省费用。IVR是一种功能强大的电话自动服务系统。

由于我们的系统重新架构,正在开发软交换API接口,所以我们试图解决这个问题。

为了方便上层对底层接口的调用,减少重复开发,产品能够针对用户需求迅速更改,我们提出了事务流的概念。即上层程序设定一个事务流,比如:相对用户9092发起呼叫,用户应答后对用户放音,让用户输入密码,密码验证成功加入制定会场。上层程序设定好这样的事务流后,发送给下层进行处理。下层处理完毕后,回复处理结果。

事务流的概念最终并没有实现,一是我们对事务流的异常处理分析不足;二是采用事务流的概念扩展性,可维护性不是很好。

后来我们在事务流的基础上引进了SOA的概念,即将底层的接口封装为一系列松散耦合的服务,在上层通过对服务的编排实现流程编辑的功能。但是实施SOA困难非常大,一是SOA没有大规模的应用,文档,经验都比较少,实施风险较大;二是实施 SOA要工具的支持,需要投资。这样的话我们宁可重复开发。

最终这个流程编辑的功能并没有完成,但是我们还是在API中实现了一个简单的流程编辑,这个流程编辑是通过硬编码来实现的,和我们的愿望相差较大。


新的思路

后来看了一家平台软件公司的介绍,他们是做企业应用平台的。企业应用中很多的一点就是工作流。他们的平台就是集成了一款开源的工作流引擎:JBPM。第一次接触JBPM,感觉很多思路可以借鉴。后来在网上搜资料的时候搜到几篇研究微内核流程引擎的文章:

揭秘jbpm流程引擎内核设计思想及构架

微内核过程引擎的设计思路和构架

微内核工作流引擎体系架构与部分解决方案参考

这几篇文章是我设计流程引擎的核心基础。看完之后最终决定自己设计一个微内核流程引擎。


使用开源还是自己开发

决定动手开发前,有两种方式,一是使用开源产品,比如JBPM;二是自己动手开发。当时的考虑是:

1、JBPM是用java开发,我们公司的业务产品目前基本上都是c,和java交互不便。

2、JBPM是针对企业工作流设计的,工作流和IVR导航一个重要的区别是,IVR导航对时效性要求很高。要求系统能够及时响应。

3、我不是JAVA出身,当时对java还有些畏难心理。

所以,最终决定自己用C++开发流程引擎。

如果是现在的话,即便是决定自己开发,一会好好研究一下JBPM的源码的。


借鉴的其他思路

1、数据驱动(DD)。在脚本中XML定义流程,程序启动后将流程读入程序,然后供上层程序调用。如果流程更改,这直接修改脚本即可。

2、BPEL。Business Process Execution Language 的缩写,意为业务过程执行语言 ,是一种基于XML 的,用来描写业务过程的编程语言。我的脚步语言参考BPEL的描述。

posted @ 2009-10-12 20:05 常高伟 阅读(369) | 评论 (0)编辑 收藏

在上一家公司的时候,一次新员工培训的主题是员工的工作态度。当时有一个案例:有两个员工,能力不相上下,都合格的完成了工作任务,其中一个员工完成任务的同时中总是抱怨,发牢骚。案例的问题是“给这两个员工进行绩效考评,一个是A,一个是B,你会怎么评?”。

  我记得当时我们都豪不犹豫的把总是抱怨的员工评了B,理由是抱怨对团队氛围和工作效率的危害很大。

  现在想来当初的这个判断很草率,不够深刻。

  管理者如何正确对待员工的抱怨?

  首先应该对员工的抱怨足够重视。其一是抱怨对团队的危害性。其二,可以把员工的抱怨理解成客户的抱怨。余世维在一个讲座中提到,“会抱怨的客户 是好客户”,因为客户的抱怨有助于提升公司的产品和服务。员工从一定意义上来说就是管理者的客户,管理者要提供“服务”来提高客户的工作效率。既然客户的 抱怨有助于提升公司的产品和服务,那么员工的抱怨是否就有助于发现公司流程、制度的缺陷,提升公司的管理水平?其三,管理者的首要任务是发现问题,既然有 员工抱怨,那么不是公司管理出现了问题,就是员工出现了问题,很多时候往往是两者都出现了问题。管理者决不能放过这个发现问题的机会。

  其次,合理的重视员工的抱怨。对员工抱怨的重视不能简单的体现在绩效考核上——用绩效考核的大棒把员工的抱怨打下去。这样做的结果往往是不是员 工不抱怨了,而是在你看不见、听不到的地方,或者心理抱怨。最终的后果是,“防人之口,甚于防川”,日积月累的抱怨会以一种更具有破坏性的形式发泄出来。

  管理者在获知员工的抱怨后应该采取什么样的行动?

  首先是调整好心态和员工开诚布公的沟通。应该以一种平和的(而不是先入为主的)心态和员工进行平等对话,深入的了解员工抱怨的真实原因。为了让员工能够没有任何心理包袱,这样的谈话不要作为绩效考核的依据(一个具有开放讨论的组织文化的团队是不存在这个问题的)。

  其次,找到问题的根源,和员工一起制定一个行动方案。如果是公司的流程、制度或管理出现了问题,管理者则要认真的分析,提出一个解决方案,以此来提升管理水平;如果是员工个人的问题,管理者就应该明确的告诉员工你的期望,目标,以促使员工的态度得到提升。

  最后,告诉员工,你有(比抱怨)更好的选择。如果你对周围的环境感觉不舒服,那就1)找出到底是什么让你感觉不舒服?(what)2)为什么会 让你感觉不舒服(why)?它有什么不合理的地方?3)怎么样能让它更加的合理(how)?4)应该在什么时间(when)、由什么人(who)来采取行 动? 最后,不要忘了把你的思考结果告诉给你的上司,并积极的参与到这项改善的行动中来。我把这种思维方式称为管理者的思维方式。当然,如果你用这种方式来思考 问题,那你就离管理者的角色不远了。

  在双方都采取行动后,如果还是不能接受对方,那么矛盾有可能是不可调和的,比如说性格和企业文化的冲突。此时勉强维护双方的关系对谁的都不好,最好的解决方案是放弃这种劳动关系。

  管理者能够“看到”的抱怨往往只是冰山一角。如果更加深入的了解员工的想法,是一个值得深入探讨的问题。鼓励员工真实的表达自己的想法,培养一种开放讨论的组织文化,消除员工坦露内心世界的制度障碍,或许会对此有些帮助。

  如果说上面的做法一种守势的思维方式,我们也可以换一种攻势的思维方式:积极的引导你的团队成员,以管理者的思维方式来思考问题。这样你的团队将会获得一个良好的自我诊断和自我修复能力。我认为,良好的自我诊断和自我修复能力是卓越团队的本质特征之一。

  总之,治理员工的抱怨,和治水其实是一个道理,重在疏导。只要措施得当,我们绝对可以化弊为利。

posted @ 2009-10-11 08:37 常高伟 阅读(205) | 评论 (0)编辑 收藏

在学习的不同的阶段我们应该保持的心态:

1、接触之前,拥有一个好奇的心态。与自己原有的知识对比,不要对新的知识产生偏见。比如,原来一直采用瀑布式软件开发,在接触敏捷软件开发之前,不要对 敏捷软件开发模式产生偏见:“敏捷开发根本不能够和瀑布式开发相提并论”。偏见会阻碍新知识的学习,而是要对敏捷开发保持一种好奇心:

1)这种理论为什么会提出来?
2)主要为了解决什么问题?
3)现有的理论无法解决此问题吗?
4)它是基于什么样的原理?
5)它是如何运行的?


2、学习了一段时间,拥有一定基础之后,要以一种批判的、怀疑的心态来对待学习的知识。当你拥有了对新知识的一定认识后,有可能会发现它很好解决了你以前 遇到的一些非常棘手的问题,或者发现你找到了某一领域问题的完美的解决方案。这个时候,你要让自己坚信,“没有银弹”,没有包治百病的灵丹妙药。任何方 案、理论、技术都有其依赖的条件,或者在解决原有问题后,引入的新的问题。此时,你要以一种批判的、怀疑的态度来对待它。要搞清楚:

1)它所依赖的前提条件是什么?
2)应用它的时候有没有什么假设?
3)它在那些场景下适用、那些场景下不适用?
4)它在解决原有问题之后,有没有引入新的问题?


3、当你达到专家级或顶级水平之后,应该持有一种“独孤求败”的心态:寻求新的挑战,争取“百尺竿头更进一步”,就像独孤求败一样,”闯荡江湖,只求一败 “:把每次的失败看成进步的机会。同时保持敬畏、谦虚的心态,你所掌握的知识,也不是”银弹“,也有其局限之处,或者随着环境的发展,已经无法适应环境, 需要寻求新的突破。这个时候,你要根据你丰富的实践,提出创新的思路或知识。

总之:
1、在学习新知识、新领域的时候,要保持一个空杯的心态,只有这样才能为自己敞开自我提升之路。
2、偏见导致对新知识的排斥,教条倒是对新知识的盲从。
3、没有绝对的真理,也没有绝对的谬论。绝对的真理让我们盲从,绝对的谬论让我们偏见。

posted @ 2009-10-11 08:31 常高伟 阅读(67) | 评论 (0)编辑 收藏

仅列出标题
共2页: 上一页 1 2 

posts - 19, comments - 0, trackbacks - 0, articles - 0

Copyright © 常高伟