2011年4月27日 星期三

有成功在公司內導入OO觀念的案例嗎

作者: TonyQ (沉默是金。) 看板: Soft_Job
標題: Re: [請益] 請問有成功在公司內導入OO觀念的案例嗎?
時間: Sat Apr 16 07:15:48 2011


        我覺得是這樣的啦,講一些我自己的看法。


        我們先講 refactor 已經存在的專案這件事,

        從無到有打造一個專案以後再講。

        -----------------------------------


        以一個現有的上線的,經過客戶run過驗證過穩定的系統來講,

        不管他 code 再怎麼爛, cp 再怎麼多,

        只要沒有客戶回報bug,沒有顯著會造成程式重大影響的bug,

        而且沒有任何bug report 要修或者是 code enhance,


        然後又沒有正規的 QA 計畫或者能說服客戶的心戰喊話,

        不管做什麼重構或甚至是砍掉重練,都是不實際的。



        改了之後程式爛掉的機率超高,如果沒有正式大規模的測試,

        爛掉的可能性不小,以我在美國的例子,

        我們客戶 QA team 有 60 人,連測了三個月還是可以在上線後,

        測到沒測到的bug,專案要上線的最後一個月,

        別說是refactor ,修bug 你 credit 不夠人家還不願意讓你碰。




        事實上重構通常是減少資訊的作法,

        有些地方我們主觀覺得兩段程式碼很像,

        不代表他們的行為完全一樣,特別是原本code的寫法就很爛,

        充斥一堆global變數的時候,一樣的code可能跑出完全不一樣的東西。


        重構這樣的程式碼有很高的風險。


        -----------------------------------

        問題在於,你的客戶/公司願不願意承受這樣的風險,

        如果你沒把握說服你的客戶/公司,那就別動它。

        沒有任何人會願意對你將來 [1;33m「號稱的」好maintain買單 [1;37;41m現在的bug;



        對於老舊專案,什麼時候適合重構?


        有任何「藉口」可以說服客戶/公司這東西需要異動的時候


                比方說:1.系統隱性不明顯的bug很多,可以試著說服使用者,
                          是程式碼的問題。(通常也是啦)

                        2.客戶修bug時,可以在bug影響的範圍內重構,
                          因為修bug本來就要測試,客戶通常可以接受。

                          時程我覺得把修bug的重構排進必須是可以接受的,

                          不過當然不要為了修個幾個顯示文字(改個String),
                          重構一整個 Business object...注意比例原則。

                        3.客戶加新功能時

                          這其實就是重構預期在maintain時要作得事情,
                          通常我自己會進行重構是在這個時間點,
                          針對我可預期的需求重新整理我的架構來配合我的實作。


        然後良心建議,越爛的專案越不要建議砍掉重練,
        除非你有夠完整的spec告訴你所有的狀況跟可能性跟夠好的 QA,

        因為爛專案的所有 state 是藏在那堆密密麻麻的 code裡面。


        特別是專案一大,你根本就很難掌握原本的專案到底有多少種狀態,
        多少個 function,多少個feature ,

        有些你看起來只被一個地方call ,還沒有傳入參數的東西,
        竟然跑起來可以隨著 global field 有著看我八十二變的效果,

        對這種 code 砍掉重練,又沒有人知道需求長怎樣的時候,


        本來難 maintain 不是你的錯,但是這樣一建議,
        又沒辦法把東西改好,又不能作到一半說不做;

        這時候這坑就挖大了......


        但老話一句,客戶/公司願意買單就沒問題,
        但,你有本事跟那個 credit 說服或保證能把這專案作到怎樣嗎?


        我最近一次作這種事情時,我給的保證是一個頁面一萬行九個檔案js ,
        跑起來要 20s的東西,在我修改完之後只需要 5s ,時間三天;

        加上我有我的 credit,所以客戶他看我提供的評估後,
        願意放手讓我作,事實上我也達到我的目標把他做好。

        問題是,你敢不敢開這種支票?


[1;33m        對現況無法完全掌握,對自己的 code 不敢負責,抱著改來試試看的人,

[1;33m        我也不覺得他有資格談重構。


        -----------------------------------

        而且重點在於,重構跟修改功能時的兩頂帽子要帶清楚,

        可能的 unit 測試要盡量作確實,

        至少每隔幾個step,自己要測過一次功能,


        你可以不用測過所有的情境,但至少你重構時,

        心中至少要有一個使用者情境,
        而你測試的時候至少要測過你測試時的情境。


[1;33m        這幾點作不到的人,我覺得更是沒資格談重構。

        -----------------------------------


        不要跟我鬼扯什麼「這段code改這樣不會爛啦,很清楚啊」,

        「這東西這麼簡單,不需要測試啦。」。


        有一定比例的程式bug幾乎都是肇因在設計時的自我感覺良好,

        如果你沒有有效驗證方案,就想去改現在已經會動的 code,

        還大言不慚的說這樣應該沒問題,那只是更多的災難。


        即使你的客戶買單,但整個系統被改到連設計師都沒辦法保證,

        在它修改的情境上會動,基本上沒人會接受的...

        -----------------------------------


        我看過一個case 是要簡化一個複雜的幾十個 if-else ,

        原本的目的只為了不同狀況下,要給不同 error message 。


        正常來講,把這個 case 的 key - value 弄成 map 來處理就好了。

        他老兄偏偏要用多型,做出幾十個 message class ,

        實作Message Interface......


        也不是不行啦,但問題是要新增message時的成本就變高了。



        重構這件事情,並沒有標準答案,每個人心中有一把尺,

        怎麼重構是比較好maintain的,其實真的很難說,

        我自己也是做過非常多次設計時感覺良好,跑起來發現爛設計的東西。


        到現在我重構時基本上也只確保幾件事,

        1.共用/常用的東西抽成 util

        2.variable 的來源跟生命週期一定要夠清楚,
          global field 除非是read-only,否則一律改成參數傳入。

        3.naming 不能太扯 (ex. method1 , method2 ,method3...etc)


        -----------------------------------

        推進拉出抽方法,我覺得這牽扯到主幹的層次,
        基本上我是這樣看得,我main/servlet thread 進來之後,


        基本上我的第一二三層 stack,
        以這例子就是main或servlet 的 get/post ,

        我沒辦法接受你在這裡面操作太底層或太複雜的資料操作,
        這層應該只有業務邏輯。


        比方說,常見的參數解析,我今天給了一個 data 叫 "user=1,2,3" ,
        你在這層給我寫 sql query 直接去q這三個user 沒包方法,我會翻臉。

        像是
        main(request){

                List list = session.createQuery(
                        "select * from user where id in "+
                                request.getParameter("user")).list();

                //為方便說明,我們先忽略sql injection。

        }


        以這樣的例子,我會寧願寫成這樣或者另外包context

        main(request){
                List list = getUser(request);
        }

        當我真的想知道 list 到底是怎麼來的,sql 怎麼下的時候,
        我自然會去看getUser 方法怎麼實作,

        但是大部分我看第一二三層的 stack 時,
        我看的只是他到底做了什麼事情,有哪些主要流程,

        而不是怎麼去拿到一堆 user 這種枝微末節。



        -----------------------------------


        扣掉在主幹上的東西,假設這東西是lib 或 util ,
        或者是很低階很低階的調用。


        如果這東西跟其他物件的互動性不大,設定的參數很少,
        那這個method基本上你愛寫多肥就寫多肥,抽不抽,我無所謂。


        當然,
  [1;33m       不要亂用 global field , variable 不要亂來,naming不要我看不懂...


        難懂的地方頂多上上註解,不見得非要拆成幾十個碎瑣的小method,
        端看語意能否直覺看懂。(這其實通常取決於設計者的經驗)


        我就把他當個黑箱用,之後有要 maintain 或幹嘛,
        再針對修改的部份單獨抽方法/field作設定或者改實作。


        一個專案總是會有髒掉的東西,重點在於這些髒掉的東稀有沒有好好的管理,
        而不是怎麼去杜絕這些髒掉的東西,很多時候為了看起來不髒反而更髒。


        -----------------------------------


        然後講到這裡,話題再繞回所謂的老人跟新人。

        我覺得老人的問題其實很複雜,

        所謂的老人看起來也只是一種大家心中討厭的那個形象,

        所以大家都不喜歡老人的作法,但是我覺得有些點必須提出來講一下。


        我先說前提是這個專案跑得夠久,因為這樣才會有所謂待很久的老人。


        對一個很多 developer 的 team 來講,有幾件事情是重要的:


        1.風格統一,至少同一小區塊/子專案的東西要風格統一。

          developer 跟 developer 之間靠慣例溝通是很重要的,
          比方說同樣的命名規則可以幫助你一看到命名就知道他想表達什麼。

          有些現存的程式碼已經很多的,建議還是要先遷就現有的風格,
          要嘛你一次把這些風格全部換新,要嘛就是留著。

          舉個例子,今天你看到

          publishMessage1()
          publishMessage2()
          publishMessage3()
          publishMessage4()
          publishMessage5()
          publishMessage6()

          好死不死你今天被 assign 到要作一個新的 publishMessage實作。
          如果你沒辦法(技術上/權限上)去修改publishMessage1~6 的naming,

          那即使你再不甘願,麻煩你命名跟著寫成 publishMessage7() ,


          而不要自己很高興的就開了個 interface 寫策略模式,
          java doc 還得意的寫著 publishMessage 7以後實作請用新招。


          這就是很多情況爛 code 繁衍的過程,but 對那些已經存在這專案,
          而且已經存在很久的 rd 來講,這是他們認知的慣例,

          你可以把所有人都拉來開會並曉以大義,
          如果他們不買單,你自己又硬要做,

          這樣做下去也只是作半套,而且事實上這樣做下去,
          並沒有增加maintain的好處,反而是增加了閱讀程式碼的複雜度。



          風格統一是很重要的事情,在一個專案中有太多風格,
          本身就是一種腐化。


        2.有些東西看起來像是 cp ,但實際上不是 cp

          比方說 for loop 都長很像,但是每個 for loop 巧妙各有不同,

          有些 code 要看仔細,有些不同的小變數或什麼,

          能拆成參數的盡量拆,但是也是會有碰到看起來很像,

          重構拆成共同的方法一換之後發現不會動的事情發生,

          因為他們就是不一樣...


        3.需求還是現實的

          有時候你看不懂專案,只是因為你程度不夠。

          我打個比方, perl/js 的程式很難看懂,但是高手還是可以看懂的,

          像我一開始也覺得 ruby 是很複雜的東西,


          以前轉譯過一隻 ruby ap 到asp,覺得怎可以寫的這麼複雜這麼亂,

          後來自己寫過 ruby ,再回去看發現其實很多事情都很清楚。



          不同領域的 bad smell 可能會不一樣,

          比方說 js 的 coding rule 跟其他語言可能會完全不一樣,
          沒有多少 pattern 是真正一看到就可以當作 bad smell的東西,

          比方說一個很複雜的 if-else ,有些時候你也就是需要,
          或者是重不重構是無所謂的。


          一個專案一定會有爛code,但問題在於這個爛code有沒有被妥善封裝,
          有包垃圾躺在垃圾桶,沒關係,還好控制,頂多把垃圾桶倒掉;

          不要有野狗跑去把他撕爛拉出來丟的整個社區都是就好了。


          以 java 來講,真的可以當作一定是 bad smell的範本,
          你可以試著用 findbug 這隻eclipse plug-in 去掃掃看,

          通常都是一些沒init array ,或者是 dead code 這種很底層的爛code。


          所以,不要急著看到黑影就開槍。


          如果有人可以真的一直解決問題,而且不是只解決user回報的問題,
          也不只是呼隴 user 給它一個只能解決特定測資的解,(用其他測資照爛)


          那其中必有玄機,不妨虛心討教彼此寫法差異,
          有時候真的只是程度有落差,所以看不懂所謂架構上的美學。


          牽扯到重構,牽扯到設計,其實真的常常會有這種事情,
          以挑戰取代批判(用這個會不會比較好?vs 你這設計不對吧?),

          多討論、多溝通,可以比較有機會學會東西。

          -----------------------------------


          其實要寫寫不完啦,還有很多有關重構這件事情可以寫的,

          不過說真的,我覺得真實世界重要的事情,

          你難以避免爛 code ,因為容易有新人,容易有習慣不好的人,
          即使是你也可能一時失心瘋就寫出爛 code ,

          但問題是,每個人寫的 code 所影響的範圍,
          跟他的品質是不是成正比,還有就是你能不能接受。



          我在美國的時候就很幹,核心程式碼都是亂寫的,
          都是針對特殊情境測資會過,換個測資就爛一堆的;

          這種時候怎麼辦?


          拼命找測資釘開發者啊,
          你 code 再怎麼爛,至少我們測過大部份正常情境會動,

          好吧,勉強 accept 。

          It's kind of real world.


          事實上這個世界的強者就是不夠多,甚至可能連自己都不夠強,
          你能怎麼辦?做好自己該做的事情就好了。


          說真的,只要認真去讀 code ,
          這世界上沒多少 code 是看不懂的,只是要花多少時間而已...

          連壓縮過的程式碼都有工具可以還原了。


          壓過的 js 檔是我看過最難懂的 code ,
          不過我的工作常要跟這樣的 code 相處,久而久之也就知道該怎麼處理了。



          囉哩八唆的寫了一堆,不過我覺得,
          很多事情不是單純價值觀的是或非,很多細節值得去討論。


--
這篇我用詞比較激烈一點,當然,一樣是論戰隨意,請別客氣。:P

--

[1;36;44m      網頁上拉近距離的幫手              實現 GMail豐富應用的功臣  

[1;36m        數也數不清的友善使用者體驗      這就是javascript

[1;30m                                                歡迎同好到 AJAX 板一同討論。

--
※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 111.81.83.73
→ TonyQ:重構其實不單純只是改善程式碼,更多的是團隊開發準則...     04/16 07:18
※ 編輯: TonyQ           來自: 111.81.83.73         (04/16 07:35)
※ 編輯: TonyQ           來自: 111.81.83.73         (04/16 07:42)
推 Falldog:現實跟理想層面還是有差的                                04/16 08:05
推 ericinttu:好文推一下.                                           04/16 09:14
推 alongalone:好....                                               04/16 09:19
推 jain00:真是推的沒話說,除了新案子,很難跟客戶說要改~            04/16 09:21
→ jain00:現在我們寫網頁至少會要求MVC分開!                         04/16 09:23
→ TonyQ:說到這件事 MVC「怎麼分開」就又是另一篇萬言書了...         04/16 09:27
推 twk:感覺重構最難的, 在怎麼取得時間和進度平衡的點~               04/16 09:29
→ twk:事情永遠做不完, 從沒遇過需要管進度的專案管理者願意額外給    04/16 09:30
→ twk:時間讓你重構, 即使project已經爛到不行都一樣~                04/16 09:31
→ TonyQ:我會說,如果你看它是「額外」,那你永遠要不到時間。        04/16 09:32
→ twk:真的只能剛好要開發或者要解的部份, 好好針對這部份清出一條路  04/16 09:32
→ TonyQ:要就要想辦法讓他不是「額外」,自然的融入你的開發內(笑)  04/16 09:33
→ TonyQ:而且重構只是個過程,目的不在重構,目的在有效解決問題...   04/16 09:33
推 twk:"有效解決問題" <--- 這句說的真好~                           04/16 09:38
→ TonyQ:我一直覺得重構一種截彎取直的作法。                        04/16 09:39
推 andymai:推 know how和設計本來就該相輔相成~少了know how的設計只  04/16 09:42
→ andymai:是工程師自我感覺良好~少了設計只有know how~那就是工程師  04/16 09:44
→ andymai:的災難~而在設計時也該考慮是否套用pattern~如果連寫個Hel  04/16 09:46
→ andymai:lo World都要套~那就是走火入魔了~設計時有些東西還是得避  04/16 09:47
→ andymai:開無意義的作法~不然只是找自己麻煩                       04/16 09:48
推 hentai:推~~ 這文章講了很多實際面的東西呢!!                      04/16 09:51
推 yangyr:好文推~ 現實是這樣啊                                     04/16 10:56
→ iincho:不用講這麼多,要重購先問有沒有regression test...         04/16 11:43
→ iincho:這邊的講法還是重在工程師單打獨鬥武林高手的範圍...        04/16 11:47
→ iincho:問題大部分的狀況是你根本沒這麼多武林高手可以用....       04/16 11:47
→ iincho:真正的解法應該是要在制度面上就埋好梗降低重構的成本...    04/16 11:48
→ iincho:不然永遠會陷在"只有高手看懂code才能改"這種鳥蛋狀況       04/16 11:48
→ iincho:話說台灣公司應該也沒多少有搞regression test就是          04/16 11:52
→ iincho:這種狀況下就極度依賴工程師能力希望搞了A不會爆掉B...      04/16 11:53
推 guest0710:哭..就是自己弱阿= ="                                  04/16 11:53
推 Apohades:好文推 感謝發文                                        04/16 11:58
推 lovdkkkk:推這篇 也推制度重要                                    04/16 12:01
→ lovdkkkk:爛制度會發生 SA SD 拉PG進會議室問spec怎麼開的鳥事      04/16 12:03
→ lovdkkkk:這或許還算好 亂開要PG照做更糟                          04/16 12:04
→ andymai:所以pg自己也要累積一定程度的know how來挑戰SA和SD XDDD   04/16 12:09
→ andymai:挑戰是嚴重了點~起碼雙方要溝通、有共識~才做得下去        04/16 12:10
→ andymai:遇過一個pg只想傻傻的照著SD做~不想知道know how~也不想研  04/16 12:14
→ andymai:究pattern~有點年紀又固執~一旦有問題要溝通還是要人命!!!  04/16 12:16
→ andymai:                                           真           04/16 12:17
→ TonyQ:我所謂的正規的QA計畫中有包含 Regression Test.             04/16 12:34
推 erik777:這篇可以M起來收精華區了 @@                              04/16 12:50
推 lovdkkkk:簡單地說, 改完能秀給人看 功能不變 bug 變少 thats all   04/16 12:58
→ lovdkkkk:要說有沒有什麼測試等等 天知道我多希望能有 (但不從人願  04/16 13:00
→ lovdkkkk:我還希望我上頭能有 1SA 1SD 能扛這事 但只有 3PM         04/16 13:01
→ lovdkkkk:環境 制度 常使事情變得瘋狂                             04/16 13:01
→ lovdkkkk:能在嚴謹且良好的環境按部就班去做 是很幸福的一件事      04/16 13:02
推 truesword:好文推~ it's kind of real world!                      04/16 21:56
→ Davidjcan:好文!!                                                04/17 03:29

沒有留言:

張貼留言

您好.本資料庫並非第一手資料.如果你有對文章作者的詢問,意見與需求,請自行找尋文章作者並提供意見,謝謝.