在經歷過上一篇的慘不忍睹程式碼之後,就開始思考:為什麼我用物件導向寫出來的程式會是這個樣子?為什麼我寫出來的東西,跟曾經看過的物件導向程式差這麼多?
幸好後來有一段蠻長的空檔,讓我有充裕的時間思考反省這個問題,更慶幸的是也有足夠時間去改正缺失。經過反思之後,終於發現程式慘不忍睹的原因:當時寫程式的第一個反應,幾乎都是「現在要寫哪個步驟的程式」。換句話來說就是有一點見招拆招、打游擊的感覺。
接著就趁那段蠻長的空檔,不斷反覆琢磨、回味之前經常在學習的理論,也就是「術語定義篇」裡的內容,然後調整自己寫程式的思考模式,並開始按照這樣的心法,來思考如何實現我要的功能和效果。也就是往後在寫程式的時候,應該要這樣思考:現在要實現的功能,有哪幾個要做互動的事物?這些事物各別有哪些特徵和行為?最後直接來看改進之後的 code-behind 程式碼。
1 using System; 2 using System.Windows.Forms; 3 4 5 namespace SybaseUtility 6 { 7 public partial class fTransmitter : Form 8 { 9 private Transmitter tt = new Transmitter(); 10 private TransmitterOperation to; 11 private ITransmitting t; 12 13 public fTransmitter() 14 { 15 InitializeComponent(); 16 } 17 18 private void fTransmitter_Load(object sender, EventArgs e) 19 { 20 to = new TransmitterOperation(new Button[] { btnSendAll, btnSendPick, btnRestoreAll, btnRestorePick }, labKeyword, dgvPreview, tsslbStatus); 21 to.CreateComboBoxItem(cbxGenre); 22 } 23 24 private void rbUnSend_CheckedChanged(object sender, EventArgs e) 25 { 26 tt.PreviewState = PreviewState.UnSend; 27 to.ButtonStateForUnSend(); 28 } 29 30 private void rbSended_CheckedChanged(object sender, EventArgs e) 31 { 32 tt.PreviewState = PreviewState.Sended; 33 to.ButtonStateForSended(); 34 } 35 36 private void cbxGenre_SelectedIndexChanged(object sender, EventArgs e) 37 { 38 tt.Genre = cbxGenre.SelectedItem.ToString(); 39 40 switch (tt.Genre) 41 { 42 case "客户资料": 43 t = new CustomersTransmitting(); 44 break; 45 46 case "销售单": 47 t = new SaleListsTransmitting(); 48 break; 49 50 case "维修历史": 51 t = new RepairingTransmitting(); 52 break; 53 54 case "会员卡资料": 55 t = new VIPCardsTransmitting(); 56 break; 57 58 case "积分兑换项目": 59 t = new CreditConvertItemsTransmitting(); 60 break; 61 62 } 63 64 to.ShowFilterText(t); 65 } 66 67 private void btnPreview_Click(object sender, EventArgs e) 68 { 69 Validating v = new Validating(cbxGenre, rbUnSend, rbSended); 70 v.Validate(); 71 72 if (string.IsNullOrEmpty(v.errorMsgText)) 73 { 74 to.Preview(t, tt); 75 } 76 } 77 78 private void tbKeyword_TextChanged(object sender, EventArgs e) 79 { 80 Validating v = new Validating(cbxGenre, rbUnSend, rbSended); 81 v.Validate(); 82 83 if (string.IsNullOrEmpty(v.errorMsgText)) 84 { 85 to.Filtering(t, tt, tbKeyword.Text); 86 } 87 } 88 89 private void btnSendAll_Click(object sender, EventArgs e) 90 { 91 tt.FinishState = FinishState.Updated; 92 if (t != null) to.SendAll(t, tt); 93 } 94 95 private void btnSendPick_Click(object sender, EventArgs e) 96 { 97 tt.FinishState = FinishState.Updated; 98 99 if (t != null) to.SendPick(t, tt); 100 } 101 102 private void btnRestoreAll_Click(object sender, EventArgs e) 103 { 104 tt.FinishState = FinishState.Restored; 105 if (t != null) to.RestoreAll(t, tt); 106 } 107 108 private void btnRestorePick_Click(object sender, EventArgs e) 109 { 110 tt.FinishState = FinishState.Restored; 111 if (t != null) to.RestorePick(t, tt); 112 } 113 } 114 }
很明顯的,慘不忍睹的版本,總共 689 行程式碼,而且是個大雜燴,經常害我寫程式寫到忘了自己是誰;重構之後的程式碼只有 114 行,而且層次清析,哪一段程式在做什麼都簡單明瞭。雖然這個版本的程式已經有很大的改進,但還是有個缺點,就是仍看得到 switch case 的程式片段,這在日後遇到需求變更時,一定得再回來改寫程式,這樣也不是很好的開發體驗。
我要的最終效果,是「不因需求變更而改動用戶端程式碼」,當然這個部份屬於下個階段的重構,因為會牽涉到設計模式,這又是比物件導向更抽象的東西,日後再發文分享心得。