大话设计模式(九 反射——程序员的快乐!)
(續上篇)
?????????“到底如何去改良策略模式呢?”小菜懇切地問道。
???????? “你仔細觀察過沒有,你的代碼,不管是用工廠模式寫的,還是用策略模式寫的,那個分支的switch依然去不掉。原因在哪里?”大鳥反問道。
????????? “因為程序里有下拉選擇,用戶是有選擇的,那么程序就必須要根據用戶的選擇來決定實例化哪一個子類對象。無論是在客戶端窗體類編程還是到工廠類里編程,這個switch總是少不掉的。問題主要出在這里。”小菜十分肯定的說。
???????? “是呀,”大鳥道,“所以我們要考慮的就是可不可以不在程序里寫明‘如果是打折就去實例化CashRebate類,如果是返利就去實例化CashReturn類’這樣的語句,而是在當用戶做了下拉選擇后,再根據用戶的選擇去某個地方找應該要實例化的類是哪一個。這樣,我們的switch就可以對它說再見了。”
????????“聽不太懂哦,什么叫‘去某個地方找應該要實例化的類是哪一個’?’小菜糊涂地說。
??????? “,我要說的就是一種編程方式:依賴注入(Dependency Injection),從字面上不太好理解,我們也不去管它。關鍵在于如何去用這種方法來解決我們的switch問題。本來依賴注入是需要專門的IoC容器提供,比如spring.net,顯然當前這個程序不需要這么麻煩,你只需要再了解一個簡單的.net技術‘反射’就可以了。”
??????? “大鳥,你一下子說出又是‘依賴注入’又是‘反射’這些莫名其妙的名詞,我有點暈哦!”小菜有些犯困,“我就想知道,如何向switch說bye-bye!至于那些什么概念我不想了解。”
??????? “心急討不了好媳婦!你急什么?”大鳥嘲笑道,“反射技術看起來很玄乎,其實實際用起來不算難。”
?????? “請看下面的兩個樣例:
1//實例化方法一???2//原來我們把一個類實例化是這樣的
3Animal?animal=new?Cat();??//聲明一個動物對象,名稱叫animal,然后將animal實例化成貓類的對象
4
5//實例化方法二
6//我們還可以用反射的辦法得到這個實例
7using?System.Reflection;//先引用System.Reflection
8//假設當前程序集是AnimalSystem,名稱空間也是AnimalSystem
9Animal?animal?=?(Animal)Assembly.Load("AnimalSystem").CreateInstance("AnimalSystem.Cat");
?其中關鍵是
Assembly.Load("程序集名稱").CreateInstance("名稱空間.類名稱")
那也就是說,我們可以在實例化的時候,再給計算機一個類的名稱字符串,來讓計算機知道應該實例化哪一個類。”大鳥講解道。
?????? “你的意思是,我之前寫的‘cc.setBehavior(new CashNormal());’可以改寫為‘cc.setBehavior((CashSuper)Assembly.Load("商場管理軟件").CreateInstance("商場管理軟件.CashNormal")’,不過,這只不過是換了種寫法而已,又有什么神奇之處呢?”小菜依然迷茫。
??????? “分析一下,原來new CashNormal()是什么?是否是寫死在程序里的代碼,你可以靈活更換嗎?”大鳥問。
????????“不可以,那還換什么,寫什么就是什么了唄。”
????????“那你說,在反射中的CreateInstance("商場管理軟件.CashNormal"),可以靈活更換‘CashNormal’嗎?”大鳥接著問。
??????? “還不是一樣,寫死在代碼…………等等,哦!!!我明白了。”小菜一下子頓悟過來,,興奮起來。“因為這里是字符串,可以用變量來處理,也就可以根據需要更換。哦,My God!太妙了!”
???????“哈哈,博客園中的有篇博文《四大發明之活字印刷——面向對象思想的勝利》中曾經寫過,‘體會到面向對象帶來的好處,那種感覺應該就如同是一中國酒鬼第一次喝到了茅臺,西洋酒鬼第一次喝到了XO一樣,怎個爽字可形容呀。’,你有沒有這種感覺了?”
??????? “嗯,我一下子知道這里的差別主要在原來的實例化是寫死在程序里的,而現在用了反射就可以利用字符串來實例化對象,而變量是可以
更換的。”小菜說道。
??????? “由于字符串是可以寫成變量,而變量的值到底是CashReturn(返利),還是CashRebate(打折),完全可以由誰決定?”大鳥再問。
???????? “當然是由用戶在下拉中選擇的選項決定,也就是說,我只要把下拉選項的值改成這些算法子類的名稱就好了,是吧?”
??????? “你說得對,不過還不是最好。因為把comboBox的每個選項value都改為算法子類的名稱。以后我們要加子類,你不是還要去改comboBox嗎?繼續往下想,現在我們的代碼對有誰依賴?”
??????? “對下拉控件comboBox的選項有依賴。”
????????“那么怎么辦,這個控件的選項可不可以通過別的方式生成。比如利用它的綁定?”
??????? “你的意思是讀數據庫?”
????????“讀數據庫當然最好了,其實用不著這么麻煩,我們不是有XML這個東東嗎,寫個配置文件不就解決了?”
??????? “哦,我知道你的意思了,讓它去讀XML的配置文件,來生成這個下拉列表框,然后再根據用戶的選擇,通過反射實時的實例化出相應的算法對象,最終利用策略模式計算最終的結果。好的好的,我馬上去寫出來。我現在真有一種不把程序寫出來就難受的感覺了。”小菜急切的說。
?????? “OK,還有一個小細節,你的CashRebate和CashReturn在構造函數中都是有參數的,這需要用到CreateInstance()方法的重載函數,不會用去查幫助吧!”
?????? “好嘞!你別走哦,等我,不見不散!”小菜向外跑著還叫道。
??????? 大鳥搖頭苦笑,嘴里嘟囔著:“這小子,忒急了吧!還不見不散呢,難道真沒完沒了啦!”
一個小時后,小菜交出了商場收銀程序的第五份作業。
客戶端主要代碼:
using System.Reflection;DataSet ds;//用于存放配置文件信息double total = 0.0d;//用于總計private void Form1_Load(object sender, EventArgs e){//讀配置文件ds = new DataSet();ds.ReadXml(Application.StartupPath + "\\CashAcceptType.xml");//將讀取到的記錄綁定到下拉列表框中foreach (DataRowView dr in ds.Tables[0].DefaultView){cbxType.Items.Add(dr["name"].ToString());}cbxType.SelectedIndex = 0;}private void btnOk_Click(object sender, EventArgs e){CashContext cc = new CashContext();//根據用戶的選項,查詢用戶選擇項的相關行DataRow dr = ((DataRow[])ds.Tables[0].Select("name='" + cbxType.SelectedItem.ToString()+"'"))[0];//聲明一個參數的對象數組object[] args =null;//若有參數,則將其分割成字符串數組,用于實例化時所用的參數if (dr["para"].ToString() != "")args = dr["para"].ToString().Split(',');//通過反射實例化出相應的算法對象cc.setBehavior((CashSuper)Assembly.Load("商場管理軟件").CreateInstance("商場管理軟件." + dr["class"].ToString(), false, BindingFlags.Default, null, args, null, null));double totalPrices = 0d;totalPrices = cc.GetResult(Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text));total = total + totalPrices;lbxList.Items.Add("單價:" + txtPrice.Text + " 數量:" + txtNum.Text + " "+cbxType.SelectedItem+ " 合計:" + totalPrices.ToString());lblResult.Text = total.ToString();}
配置文件?CashAcceptType.xml 的代碼
?1<?xml?version="1.0"?encoding="utf-8"??>?2<CashAcceptType>
?3????<type>
?4????????<name>正常收費</name>
?5????????<class>CashNormal</class>
?6????????<para></para>
?7????</type>
?8????<type>
?9????????<name>滿300返100</name>
10????????<class>CashReturn</class>
11????????<para>300,100</para>
12????</type>
13????<type>
14????????<name>滿200返50</name>
15????????<class>CashReturn</class>
16????????<para>200,50</para>
17????</type>
18????<type>
19????????<name>打8折</name>
20????????<class>CashRebate</class>
21????????<para>0.8</para>
22????</type>
23????<type>
24????????<name>打7折</name>
25????????<class>CashRebate</class>
26????????<para>0.7</para>
27????</type>
28</CashAcceptType>
實現的界面同之前一樣(可點擊使用)
?
????????? “大鳥,我再次搞定了,這會是真的明白了。”小菜說。
?????????“說說看,你現在的理解!”大鳥問。
???????? “無論你的需求是什么,我現在連程序都不動,只需要去改改XML文件就全部擺平。比如你如果覺得現在滿300送100太多,要改成送80,我只需要去XML文件里改就行,再比如你希望增加新的算法,比如積分返點,那我先寫一個返點的算法類繼承CashSuper,再去改一下XML文件,對過去的代碼依然不動。總之,現在是真的做到了程序易維護,可擴展。”小菜得意地壞笑道,“吼吼!此時商場老板以為要改一天的程序,我幾分鐘就搞定,一天都可以休息。反射——真是程序員的快樂呀!”
?????? “在做夢了吧,你當老板是傻瓜,會用反射才是正常水平,不會用的早應該走人了。”大鳥打擊了小菜的情緒,“不過呢小菜的確是有長進,不再是小菜鳥了。那你說說看,現在代碼還有沒有問題。”
??????? “還有不足?不會吧,我都改5次了,重構到了這個地步,還會有什么問題?”小菜不以為然。
?????? “知足是可以常樂,但知足如何能進步!你的代碼真的沒有問題了,比如說,你現在把列表是打印在了listBox列表框中,我現在還需要輸出到打印機打印成交易單據,我還希望這些清單能存入數據庫中,你需要改客戶端的代碼嗎?”
?????? “這個,你這是加需求了,更改當然是必須的。”
?????? “更改是必須的沒有錯,但為什么我只是要對交易清單加打印和存數據,就需要去改客戶端的代碼呢?這兩者沒什么關系吧?”大鳥說。
?????? “啊,你的意思是…………”
??????? “別急著下結論,先去好好思考一下再說。”大鳥打斷了小菜。
(待續)
本文源代碼
出處:http://www.cnblogs.com/cj723/archive/2007/03/22/683951.html
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的大话设计模式(九 反射——程序员的快乐!)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 大话设计模式(八 用“策略模式”是一种好
- 下一篇: 大话设计模式(十 会修电脑不会修收音机?