<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    2006年11月22日

    剛剛看到有個哥們兒講他的客戶讓他很郁悶,我有點想法,整理如下:

    首先,我覺得開發人員遇到這樣的郁悶是因為控制需求變更功夫沒有做足。原因有幾點:
    1.涉及需求變更的東西不應該由最終使用的用戶和一線開發人員來溝通,這樣的溝通費時費力而且不具有權威性。
    2.開發人員直接向客戶匯報的工作量往往比實際工作量要低,而且低的比較多。原因很簡單:客戶問開發人員一個功能是否困難的時候,一般技術人員往往只考慮了單項功能的復雜度,而可能對這個需求變更對整個系統的工作量估計不足(比如美工的工作量、該功能引發的管理功能的工作量、測試工作量等等)。
    這種情況會對項目產生多個負面影響:a.向客戶提供一個低于實際值的工作量,導致客戶期望高,而實際無法按時完成導致客戶失望大,降低用戶滿意度。b.因為客戶從開發人員口中聽到的工作量總是比從項目經理口中聽到的工作量低,造成客戶對項目組內部不一致,溝通不足的感覺。c.因為客戶從開發人員口中聽到的工作量總是比從項目經理口中聽到的工作量低,引誘客戶喜歡直接向開發人員提出需求變更,造成惡性循環,直接導致了項目組沒法按時拿到獎金,士氣下降。

    所以對于客戶提出的需求變更,一般技術人員最好的處理方式是:委婉的告訴客戶,這個問題需要項目經理來評估。哪怕用戶用挑釁、教訓的語氣和你講這個功能如何簡單,如何如何就可以實現,你都不能告訴他是否可以接受這個變更,更不能說實現需要多長時間。
    拒絕了客戶之后并不是大功告成,你最好能夠早于客戶通知自己的項目經理,客戶想進行怎樣的需求變更,你自己對工作量的評估是怎么樣的。這樣可以給項目經理一個準備時間,來完善的考慮需求變更的影響。

    對于項目經理,尤其是從開發一線轉向做項目經理的兄弟,應該主動的從項目全局來考慮一個變更的影響,而不是單純從技術角度考慮。最好能按照公司的規范和制度以及項目實際情況為自己積累一份check list,以免在考慮需求變更時遺漏一些事項。作為開發方更要強化對于需求變更的控制。
    控制需求變更最理想的辦法當然是由客戶方、開發方的項目經理和需求顧問共同組織CCB(變更控制委員會)
    ,文檔化所有需求變更,雙方簽字然后歸檔需求變更。不過這樣比較難以實現。但是最起碼的要求是,必須由客戶方項目經理(也就是甲方最終用戶需要把需求變更匯總報告給甲方項目經理)向開發方項目經理提出需求變更,開發方項目經理評估工作量,并文檔化需求變更,在與客戶方負責人充分溝通后,使用正式方式將溝通結果(最好是打印出來給甲方簽字,最起碼是要求回執的電子郵件)通知客戶。必要的時候需要業務人員協助,比如要求簽署附加合同或者新開一個項目等等。

    從我做項目幾年的經驗來看,蠻不講理的客戶不是沒有,但是是極少數,大多數客戶,尤其是客戶方項目經理都是通情達理的人。所以,只要你言之有理,對方都有可能接納。

    posted @ 2006-12-05 17:07 iceboundrock 閱讀(1900) | 評論 (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 閱讀(636) | 評論 (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 閱讀(1108) | 評論 (0)編輯 收藏

    自從上次項目中使用C++到現在,已經有一年半沒有再碰過C++了。雖然C++依舊是我心中最向往去使用的語言。
    打算借著這次復習數據結構與算法的機會重拾C++。今天做了兩個容器類,發現很多基礎語法的東西我都已記得經模棱兩可了,sigh,看來真是要努力才行了。

    posted @ 2006-11-22 16:34 iceboundrock 閱讀(205) | 評論 (0)編輯 收藏


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

    Copyright © iceboundrock

    主站蜘蛛池模板: 久久久无码精品亚洲日韩软件| 四虎影院永久免费观看| 亚洲美女又黄又爽在线观看| 特级毛片A级毛片100免费播放| 日本中文一区二区三区亚洲| 四虎影视久久久免费观看| 亚洲av再在线观看| 亚洲高清免费视频| 国产亚洲av片在线观看播放| a级毛片在线视频免费观看| 亚洲人成在线播放网站| a级毛片免费播放| 亚洲网址在线观看你懂的| 18女人腿打开无遮掩免费| 亚洲国产电影在线观看| 成全视频免费高清| 国产亚洲人成在线影院| 国产91精品一区二区麻豆亚洲| 国产情侣久久久久aⅴ免费 | 亚洲中文字幕无码mv| 成人免费淫片在线费观看| 激情小说亚洲色图| 亚洲色WWW成人永久网址| 精品无码人妻一区二区免费蜜桃| 亚洲三级视频在线| 亚洲av无码成人精品区| 十八禁视频在线观看免费无码无遮挡骂过 | 国产成人福利免费视频| 亚洲欧美国产欧美色欲| 亚洲成AⅤ人影院在线观看| 两个人看的www免费视频中文 | 亚洲日韩AV无码一区二区三区人| 国产a级特黄的片子视频免费| eeuss免费天堂影院| 亚洲麻豆精品果冻传媒| 国产又黄又爽又刺激的免费网址| 国产午夜不卡AV免费| 亚洲天堂免费在线| 久久久无码精品亚洲日韩软件| 69xx免费观看视频| 又粗又长又爽又长黄免费视频|