2006年8月10日

刚刚看到有个哥们儿讲他的客户让他很郁闷,我有点想法,整理如下:

首先,我觉得开发人员遇到这样的郁闷是因为控制需求变更功夫没有做足。原因有几点:
1.涉及需求变更的东西不应该由最终使用的用户和一线开发人员来沟通,这样的沟通费时费力而且不具有权威性。
2.开发人员直接向客户汇报的工作量往往比实际工作量要低,而且低的比较多。原因很简单:客户问开发人员一个功能是否困难的时候,一般技术人员往往只考虑了单项功能的复杂度,而可能对这个需求变更对整个系统的工作量估计不足(比如美工的工作量、该功能引发的管理功能的工作量、测试工作量等等)。
这种情况会对项目产生多个负面影响:a.向客户提供一个低于实际值的工作量,导致客户期望高,而实际无法按时完成导致客户失望大,降低用户满意度。b.因为客户从开发人员口中听到的工作量总是比从项目经理口中听到的工作量低,造成客户对项目组内部不一致,沟通不足的感觉。c.因为客户从开发人员口中听到的工作量总是比从项目经理口中听到的工作量低,引诱客户喜欢直接向开发人员提出需求变更,造成恶性循环,直接导致了项目组没法按时拿到奖金,士气下降。

所以对于客户提出的需求变更,一般技术人员最好的处理方式是:委婉的告诉客户,这个问题需要项目经理来评估。哪怕用户用挑衅、教训的语气和你讲这个功能如何简单,如何如何就可以实现,你都不能告诉他是否可以接受这个变更,更不能说实现需要多长时间。
拒绝了客户之后并不是大功告成,你最好能够早于客户通知自己的项目经理,客户想进行怎样的需求变更,你自己对工作量的评估是怎么样的。这样可以给项目经理一个准备时间,来完善的考虑需求变更的影响。

对于项目经理,尤其是从开发一线转向做项目经理的兄弟,应该主动的从项目全局来考虑一个变更的影响,而不是单纯从技术角度考虑。最好能按照公司的规范和制度以及项目实际情况为自己积累一份check list,以免在考虑需求变更时遗漏一些事项。作为开发方更要强化对于需求变更的控制。
控制需求变更最理想的办法当然是由客户方、开发方的项目经理和需求顾问共同组织CCB(变更控制委员会)
,文档化所有需求变更,双方签字然后归档需求变更。不过这样比较难以实现。但是最起码的要求是,必须由客户方项目经理(也就是甲方最终用户需要把需求变更汇总报告给甲方项目经理)向开发方项目经理提出需求变更,开发方项目经理评估工作量,并文档化需求变更,在与客户方负责人充分沟通后,使用正式方式将沟通结果(最好是打印出来给甲方签字,最起码是要求回执的电子邮件)通知客户。必要的时候需要业务人员协助,比如要求签署附加合同或者新开一个项目等等。

从我做项目几年的经验来看,蛮不讲理的客户不是没有,但是是极少数,大多数客户,尤其是客户方项目经理都是通情达理的人。所以,只要你言之有理,对方都有可能接纳。

posted @ 2006-12-05 17:07 iceboundrock 阅读(1891) | 评论 (12)编辑 收藏

C++ C#/java 有很多区别,其中最大的区别当数对内存的管理。

C++ 中,类的使用者决定了类的实例内存会如何分配,分配在堆上还是栈上。我们先看一段例子程序:

 

#include "stdio.h"

 

class Demo{

public :

    int i;

    char* objName;

    Demo(){

        objName = "Default object.";

        printf("%s, objName = %s\r\n", "Enter Demo default ctor. method.", objName);

       

        i = 1000;

    }

 

    Demo(int ival, char* name){

        printf("%s,i = %d, objName = %s\r\n", "Enter Demo(int ival) ctor method", ival, name);

        i = ival;

        objName = name;

    }

 

    Demo(const Demo& d){

        printf("%s\r\n", "Enter Demo copy ctor method.");

        i = d.i;

        objName = "copied d";

    }

 

    ~Demo(){

        printf("%s, i = %d, objName = %s\r\n", "Enter Demo dector. method" , i, objName);

    }

};

 

Demo& testMethod0(){

    printf("%s\r\n", "Enter testMethod0.");

    Demo d(0, "d in testMethod0");

    printf("%s\r\n", "Exit testMethod0.");

    return d;

}

 

Demo testMethod1(){

    printf("%s\r\n", "Enter testMethod1.");

    Demo d(1, "d in testMethod1");

    printf("%s\r\n", "Exit testMethod1.");

    return d;

}

 

Demo* testMethod2(){

    printf("%s\r\n", "Enter testMethod2.");

    Demo *d = new Demo(2, "d in testMethod2");

    printf("%s\r\n", "Exit testMethod2.");

    return d;

}

 

int main(int argc, _TCHAR* argv[])

{

    Demo d;

    d = testMethod1();

 

    Demo& d1 = testMethod0();

 

    Demo d2(999, "d1");

 

    Demo* d3 = testMethod2();

 

    printf("d.i = %d\r\n", d.i);

    printf("d1.i = %d\r\n", d1.i);

    printf("d2.i = %d\r\n", d2.i);

    printf("d3.i = %d\r\n", d3->i);

 

    delete d3;

    return 0;

}

 

Output

Enter Demo default ctor. method., objName = Default object.

Enter testMethod1.

Enter Demo(int ival) ctor method,i = 1, objName = d in testMethod1

Exit testMethod1.

Enter Demo copy ctor method.

Enter Demo dector. method, i = 1, objName = d in testMethod1

Enter Demo dector. method, i = 1, objName = copied d

Enter testMethod0.

Enter Demo(int ival) ctor method,i = 0, objName = d in testMethod0

Exit testMethod0.

Enter Demo dector. method, i = 0, objName = d in testMethod0

Enter Demo(int ival) ctor method,i = 999, objName = d1

Enter testMethod2

Enter Demo(int ival) ctor method,i = 2, objName = d in testMethod2

Exit testMethod2.

d.i = 1

d1.i = -2

d2.i = 999

d3.i = 2

Enter Demo dector. method, i = 2, objName = d in testMethod2

Enter Demo dector. method, i = 999, objName = d1

Enter Demo dector. method, i = 1, objName = copied d

 

C# 不同,在 C++ 中,对象声明的时候就已经执行了构造函数,比如上面例子的 main 函数中的第一行, Demo d ,从屏幕上的输出来看,这个时候 Demo class 的默认构造函数会被调用。

接下来的一行代码调用,引出了很有趣的情况,当然也隐藏着不小的问题。这行代码造成了一次构造函数调用,一次拷贝构造函数调用和两次析构函数调用。让我们来具体分析一下:第一次调用构造函数很容易理解,因为在 testMethod1 中我们声明了 Demo d(0) ,退出 testMethod1 ,函数的返回值要赋值给变量 d2 ,这个时候, d2 被拷贝构造函数重新构造了一次。接着 testMethod1 中构造的局部变量被析构,然后,居然拷贝构造函数构造的对象也被析构?等等,看完所有输出,我们发现, objName = copied d 的对象被析构两次,而 objName = Default obj 的对象被构造出之后没有被析构,这里隐藏了很严重的问题,有可能导致内存泄漏、句柄不能被正确关闭等等。另外,拷贝构造函数的执行可能导致潜在的效率问题,考虑一个包含巨大矩阵的对象, copy 这个对象会怎么样?

 

接下来的一行代码, testMethod0 返回一个对象的引用,当然不会导致拷贝构造函数被调用,但是,这样也是有问题的,在函数中声明的局部变量在函数执行完成的时候会被析构,那么直接返回局部变量就可能会出现问题。 testMethod0 退出以后,他内部的 Demo 对象就会自动析构,外面对它的引用当然也无法指向正确的对象了,所以后面程序打印 d.i 的时候,输出了一个莫名其妙的 -2

 

效率最好的方法当数返回指针了,它不会导致对象复制,如果使用得当,也不会导致内存泄漏或者句柄泄漏。 testMethod2 演示了这种情况,当然,你需要手工删除在 testMethod2 中创建的对象。

 

 

posted @ 2006-11-25 16:08 iceboundrock 阅读(630) | 评论 (1)编辑 收藏

关于const,C++的const是一个非常非常麻烦的关键字,但是如果你不用,也会带来一些麻烦。

下面一段简单的程序,演示了const变量,const指针的奇妙关系

 

 1 #include  " stdafx.h "
 2
 3
 4 int  _tmain( int  argc, _TCHAR *  argv[])
 5 {
 6   const   int  constInt1  =   1 ;
 7
 8   const   int   * constIntPoint  =  NULL;
 9
10   int   * IntPoint  =  NULL;
11
12  constIntPoint  =   & constInt1;
13
14   const   int  constInt2  =   2 ;
15
16   int  Int3  =   3 ;
17  
18   // IntPoint = &constInt2;  // Error 1
19
20
21  constIntPoint  =   & Int3;
22
23   // (*constIntPoint)++;  // Error 2
24
25  printf( " constInt1=%d\r\n " , constInt1);
26  printf( " constInt2=%d\r\n " , constInt2);
27  printf( " Int3=%d\r\n " , Int3);
28
29  printf( " constIntPoint point to %d\r\n " * constIntPoint);
30   return   0 ;
31 }

32
33


最简单最清晰的const使用方法就是声明const变量了,变量需要在生命的地方立即初始化,初始化完成之后就不能再改了。

如果你用同样的思路来看待const指针,你会发现你错的很严重,你看,这个constIntPoint换了几个目标依然生龙活虎,编译器很愉快的接受了这段代码,连个warn都没有。
原来const指针是指向const变量的指针,而不是说指针本身是const的。无

ok,const变量不能直接修改,难道我取到他的地址,再来修改都不行么?不行,编译器会直接告诉你,无法把一个const的指针转换成普通指针,

Error 1 error C2440: '=' : cannot convert from 'const int *__w64 ' to 'int *' 

论一个变量原来是否被声明成const,你用一个const指针指向它,然后使用*运算符号取出这个变量试图进行修改的操作都是不允许的,参考代码中被注释掉的Error2。

Error 2 error C3892: 'constIntPoint' : you cannot assign to a variable that is const 

posted @ 2006-11-22 17:03 iceboundrock 阅读(1096) | 评论 (0)编辑 收藏

自从上次项目中使用C++到现在,已经有一年半没有再碰过C++了。虽然C++依旧是我心中最向往去使用的语言。
打算借着这次复习数据结构与算法的机会重拾C++。今天做了两个容器类,发现很多基础语法的东西我都已记得经模棱两可了,sigh,看来真是要努力才行了。

posted @ 2006-11-22 16:34 iceboundrock 阅读(200) | 评论 (0)编辑 收藏

     摘要: 转贴自:http://blog.csdn.net/pongba/archive/2003/10/24/19130.aspx,作者:刘未鹏 首先,C++标准中提到,一个编译单元[translation unit]是指一个.cpp文件以及它所include的所有.h文件,.h文件里的代码将会被扩展到包含它的.cpp文件里,然后编译器编译该.cpp文件为一个.obj文件,后者拥有PE[Portable ...  阅读全文

posted @ 2006-10-08 13:31 iceboundrock 阅读(369) | 评论 (0)编辑 收藏

办公软件,wps 2005个人版。
电子邮件:Thunderbird
开发工具:
.net 1.1就用SharpDevelop
.net 2.0就用VS Express系列
辅助工具有:Reflector看.net类库、Snipt Code
java和j2ee用Eclipse+WTP。
.net framewok SDK、Windows SDK和jdk也都是免费的,里面也有文档。
文本比较工具用WinMerge

版本控制:
CVS客户端用Eclipse内置的或者TortoiseCVS
SVN客户端用SVN的Eclipse插件和TortoiseSVN
CVS服务器用CVS NT
SVN服务器用SVN

Debug Release版本或者生产环境的.net程序或者C++程序程序可以用Windbg。SysInternals的一系列工具也是排错利器啊。
压力测试用JMeter和MS Web Stress Tool都还不错。
查看IE中的Http头可以用ieHttpHeaders

抓取网络包可以用Ethereal
 
文本编辑器:Notepad++、Notepad2 ,如果编辑Python代码,用Vim更好。

浏览图片就用系统内置的那个,Picasc2也不错,图片处理可以用MS Paint、Paint.Net或者GIMP。

听歌用foobar2000 ,Winamp也是免费的,Windows Media Player和iTunes虽然比较大,但是功能齐全也算不错的选择。
压缩解压缩用:7-zip
聊天就更多了,msn/qq/gtalk都是免费的。下载用FlashGet和Gigaget。
任务列表用的是codeproject上的一个免费工具,todolist。 制定项目计划可以用openworkbench。
看文档一般都是chm的,pdf还是用acrobat reader 7.08

连接Telnet服务器或者SSH服务器可以用putty。FTP、SFTP客户端用WinSCP。
Mindmap用FreeMind

posted @ 2006-09-25 22:13 iceboundrock 阅读(425) | 评论 (0)编辑 收藏

圈子圈套 1

圈子圈套 2

圈套玄机

输赢

posted @ 2006-08-31 18:38 iceboundrock 阅读(171) | 评论 (0)编辑 收藏

    网络上对于这个问题的解决方案还是非常多的,但是大多都要求 copy 一些文件到 Windows 或者 System32 目录中,显得不够优雅。 我参考了 php 的用户手册和 MySQL 的网站,找到了一个不需要拷贝文件的解决方案。
    首先是准备步骤,具体如下:

1. php 5.1.4 的压缩包解压到 D:\php ,复制 php.ini-recommended php.ini

2. 安装 apache2.0.59

3. 安装 MySQL5

4. 备份“ Apache Group\Apache2\conf ”文件夹

5. MySQL 网站下载 MySQL Connector/PHP 的文件,下载 mysql extension (PHP 5.1.4) for MySQL Server 5.0.22 的那个包。

 

下面说一下如何配置。

修改 Apache httpd.conf 文件

1.       加入 PHPIniDir 变量;设定加载 php5 模块的路径;设定 .php 文件类型的处理方式。具体代码如下:

#php config

LoadModule php5_module "d:/php/php5apache2.dll"

AddType application/x-httpd-php .php

PHPIniDir "D:/php/"

2.       index.php 加入 DirectoryIndex 变量中。具体代码如下:

DirectoryIndex index.html index.html.var index.php

更新 MySQL Connector/PHP

解压 MySQL Connector/PHP 压缩包到 D:\php ,并将 php_mysql.dll 拷贝到 D:\php\ext

修改 php.ini

1.  修改 include_path = ".;d:\php\PEAR"

2.  修改 extension_dir = "D:\php\ext"

3.  取消 extension=php_mysql.dll 前面的分号

4.  取消 extension=php_gd2.dll 前面的分号

 

配置到这里就完成了,为了测试效果,可以在“ Apache Group\Apache2\htdocs ”文件夹中建立一个名为 phpinfo.php 的文件,内容如下:

<?php

$link=mysql_connect('localhost','test','test'); // 用户和密码 , 请根据你自己的情况改好

if(!$link) echo "fail";

else echo "success";

mysql_close();

echo phpinfo();

?>

 

然后访问 http://localhost/phpinfo.php ,如果页面最顶端上打印出 success ,说明设定成功,否则可以根据 phpinfo 的结果看看问题所在。

 

 

posted @ 2006-08-10 01:44 iceboundrock 阅读(391) | 评论 (0)编辑 收藏


posts - 10, comments - 15, trackbacks - 0, articles - 0

Copyright © iceboundrock