操屁眼的视频在线免费看,日本在线综合一区二区,久久在线观看免费视频,欧美日韩精品久久综

新聞資訊

    點擊“開發者技術前線”,選擇“星標”

    讓一部分開發者看到未來

    作者:馬非碼 編輯:可可來源:如果你認為這是一個標題黨,那么我真誠的懇請你耐心的把文章的第一部分讀完,然后再下結論。如果你認為能夠戳中您的G點,那么請隨手點個贊。

    把三千行代碼重構為15行

    那年我剛畢業,進了現在這個公司。公司是搞數據中心環境監控的,里面充斥著嵌入式、精密空調、總線、RFID的概念,我一個都不懂。還好,公司之前用寫的老客戶端因為太慢,然后就搞了個的替代,恰好我對Asp.Net還算了解,我對業務的不了解并不妨礙我稱成為這個公司的一個程序員。小公司也有小公司的好,人少,進去很快負責代碼開發。我當然也就搞這個數據中心智能管理系統啦。這個系統非常的龐大,尤其牛逼的是支持客戶端組態,然后動態生成網頁,數據還能通過實時監控(那時我還真就不懂網絡編程)。這個對于當時的我來說微信關注代碼生成器,真真是高、大、上吶!!當時跟著了解整個系統大半個月才算能夠調試,寫一些簡單的頁面。在維護系統的過程中,時不時要擴展一些功能,也就接觸了下面這個類:

    看到沒有,就是當年最最流行的三層架構的產物,對于剛出茅廬的毛頭小子來說,這是多么專業的文件頭注釋,還有反射也就算了,這構造函數還能靜態的,還能私有的?那時剛接觸這么高大上的代碼的我,瞬間給跪了!

    但是,類寫多了,我就感覺越來越別扭,就是下面這段代碼:

    每增加一個表,除了要改接口、要改DAL、要改BLL之外,還得在這個工廠類添加一個方法,真真是累到手抽筋,即使有當時公司了的G工給我推薦的神器——動軟代碼生成器,這粘貼復制的幾遍,也是讓我感覺到異常繁瑣,有時候打鍵盤稍微累了點,還把復制出來代碼改錯了,你妹的,難道這就是程序員該干的事情,不,絕對不是!我想起了一句至理名言:當你覺得代碼重復出現在程序中的時候,就應該重構了。是的,在這句話的指導下,我開始了折騰,決定挑戰這個高大上的代碼,事實證明,思想的力量是無窮的。那么,怎么修改呢,仔細觀察之后,發現其中的生成跟返回的類型非常類似,只是一個是類名,一個是字符串,這兩者之間應該能夠關聯起來。于是了一下(當時GFW還沒猖獗起來哈),隱隱約約就找到了“反射”這兩個字,深入了解之后,確定可以完成。接下來,就是返回的類型了,返回的類型并不固定,但是似乎很有規律……這個似乎好像在哪里見過,對了,模板,C++課程上有講過的,于是再次,了解到了C#中使用了泛型代替了C++中的模板。在學習完泛型和反射之后,并參考了網上的一些文章,我搗鼓出了下面的代碼:

    沒錯,就是它了,三層架構年代最流行的工廠類……

    看著原來滾十幾屏幕的代碼,變成了十多行的代碼,真是爽到了骨子里去了微信關注代碼生成器,太干凈了!唯一讓我擔憂的是,我進公司的時候,幫忙整理公司申請軟件著作權都是需要代碼量的,根據代碼多少行來評估軟件的大小,萬一老板知道了我非但沒有幫公司增加代碼量,還減少了,會不會立即把我開掉?我沒敢給我們老板展示我優秀的成果。所幸,這段代碼非但沒有出過任何問題,還避免了以前同事老是在新增一個類之后,把代碼復制過來,但是沒有正確修改的問題,大大提高了效率。雖然,我沒敢大事宣布我的勞動成果,但是這次成功的修改,則徹底讓我走上了代碼重構的不歸路。看到這里,大家應該知道這個案例是否真實的了吧。我相信,從08年開始的碼農們,看到這種類似的代碼絕對不比我少。那么,我想告訴你們的是什么呢?

    少用代碼生成器

    我們來分析一下,為什么我之前的前輩會寫出上面的代碼。我歸結起來有以下幾點:

    至今為止,還是很多人使用代碼生成器,那么我們應該怎么對待這個問題呢。我認為,代碼生成器確實可以減少你不少工作,但是少用,那些重復性的工作,除了部分確實是沒有辦法的,其他大部分都是可以通過框架解決的,舉例來說,像三層架構,真正需要用到代碼生成器的,也就是Model類而已,其他的完全可以在框架中完成。因此你要竭盡全力的思考怎么在框架中來減少你的重復性工作,而不是依賴于代碼生成器。另外,如果你還是在用相關的代碼生成工具,請重新定義“動軟代碼生成器”的代碼模板,自己寫一個模板;或者使用來完全制定自己的代碼生成,因為動軟給的代碼模板真心亂,比如下面這段代碼:

    for?(int?n = 0; n < rowsCount; n++)
    {
    ????model = new?DBAccess.Model.eventweek();
    ????if(dt.Rows[n]["GroupNo"].ToString()!="")
    ????{
    ????????model.GroupNo=int.Parse(dt.Rows[n]["GroupNo"].ToString());
    ????}
    ????if(dt.Rows[n]["Week0"].ToString()!="")
    ????{
    ????????model.Week0=int.Parse(dt.Rows[n]["Week0"].ToString());
    ????}
    ????if(dt.Rows[n]["Week1"].ToString()!="")
    ????{
    ????????model.Week1=int.Parse(dt.Rows[n]["Week1"].ToString());
    ????}
    }

    首先,你就不能用 var row=dt.Rows[n]替代嗎?其次,直接用int.Parse如果拋出了異常性能得有多低?再次,這段代碼要是有點修改,我不是要每個dt.Rows[n]得改一遍?

    不要重復發明輪子

    我們再來看看其他的一些代碼:

    public?List<string> GetDevices(string?dev){
    ????List<string> devs=new?List<string>();

    ????int?start=0;
    ????for(int?i=0;i????????if(dev[i]=='^'){
    ????????????devs.Add(dev.SubString(start,i));
    ????????????start=i+1;
    ????????}
    ????}

    ????return?devs;
    }

    有沒有很眼熟,沒錯,這就是對.Split()函數的簡單實現。我的前輩應該是從c++程序員轉過來的,習慣了各種功能自己實現一遍,但是他忽略了C#的很多東西。我們不去評判這段代碼的優劣,而實際上他在很長一段時間都運行得很好。我們來看看使用這一段代碼有什么不好的地方:

    那么,我們應該怎樣去避免重復發明輪子呢?我從個人的經歷來提出以下幾點,希望能夠對各位有所幫助:

    這里我再舉一個我自己的例子。在我現有的程序中,我發現我需要越來越多的線程來執行一些簡單的任務,比如在每天檢測一下硬盤是否達到90%了,每天9點要控制一下空調的開啟而在網上6點的時候把空調關掉。

    線程使用越來越多,我越是覺得浪費,因為這些現場僅僅只需完成一次或者有限的幾次,大部分時間都是沒有意義的,那么怎么辦呢?我決定自己寫一個任務類,來完成相關的事情。說干就干,我很快把這個類寫出來了。

    public?abstract?class?MissionBase?: IMission
    {
    ????private?DateTime _nextExecuteTime;
    ????protected?virtual?DateTime[] ExecuteTimePoints { get; private?set; }
    ????protected?virtual?int?IntervalSeconds { get; private?set; }
    ????protected?IEngine Engine { get; private?set; }

    ????public?bool?IsCanceled{get{……}}
    ????public?bool?IsExecuting{get{……}}
    ????public?bool?IsTimeToExecute{get{……}}

    ????public?abstract?bool?Enable { get; }
    ????public?abstract?string?Name { get; }

    ????protected?MissionBase(IEngine engine)
    ????
    {
    ????????ExecuteTimePoints = null;//默認采用間隔的方式
    ????????IntervalSeconds = 60?* 60;//默認的間隔為1個小時

    ????????Engine = engine;
    ????}

    ????///?任務的執行方法
    ????public?void?Done()
    ????
    {
    ????????if?(Interlocked.CompareExchange(ref?_isExecuting, 1, 0) == 1) return;

    ????????try
    ????????{
    ????????????……
    ????????}
    ????????finally
    ????????{
    ????????????Interlocked.CompareExchange(ref?_isExecuting, 0, 1);
    ????????}
    ????}
    ????
    ????///實際方法的執行
    ????protected?abstract?void?DoneReal();
    }

    但是,實際上這個任務方法,并不好用,要寫的代碼不少,而且可靠性還沒有保障。當然,我可以繼續完善這個類,但是我決定搜索一下是否還有其他的方法。直到有一天,我再次閱讀《CLR Via C#》,看到線程這一章,講到了..Timer以及類時,我就知道了,使用Timer類完全可以解決我的這個用盡量少的線程完成定時任務的問題。因為從原理上來說,Timer類無論你聲明了多少個,其實就只有一個線程在執行。當你到了執行時間時,這個管理線程會用來執行Timer中的函數,因為使用的,執行完成之后,線程就馬上回收了,這個其實就完全實現了我所需要的功能。

    等你無法重構的時候再考慮重寫

    我帶過很多優秀的程序員,也與很多優秀的程序員共事過。有一大部分的程序員在看到一套系統不是那么滿意,或者存在某些明顯的問題,就總是忍不住要把整套系統按自己覺得可以優化的方向來重寫,結果,重寫結構往往并不令人滿意。

    系統中確實存在很多不合理的地方,但是有不少的這種代碼,恰恰是為了解決一些特定場景下的問題的。也就是說,所有的規范以及編程的原則,其實也是有條件限制的,他可能在大部分的時候是正確的,能夠指導你完成你的任務,但是,并不是在所有地方都是適用的。比如數據庫范式,但實際中我們的設計往往會考慮冗余,這是違背范式的,但是為什么還有那么多人趨之若鶩呢?因為我們可能需要用空間換時間。

    如果我們一開始就考慮重寫,那么你可能會陷入以下的困境:

    我舉個例子,說明如何通過重構更好的利用現有代碼的。我有一個非常龐大的系統,其中有一塊功能是用于數據采集、存儲、告警管理以及電話、短信等告警通知。大致的結構如下:

    class?MainEngine:IEngine{
    ????public?MainEngine(ConfigSettings config){
    ????????
    ????}

    ????public?void?Start();
    ????public?void?Stop();
    }

    需要增加新的業務功能時,程序員寫的代碼往往是這樣的:首先時修改配置類

    class?ConfigSettings{
    ????public?bool?NewFuncEnable{get;private?set;}
    ????public?ConfigSettings(){
    ????????NewFuncEnable=xx;//從配置文件讀取
    ????}
    }

    接著修改主程序:

    class?MainEngine:IEngine{
    ????private?NewFuncClass newCls=new?NewFuncClass();
    ????public?MainEngine(ConfigSettings config){
    ????}

    ????public?void?Start(){
    ????????if(config.NewFuncEnable)
    ????????????newCls.Start();
    ????}
    ????public?void?Stop(){
    ????????if(config.NewFuncEnable)
    ????????????newCls.Stop();
    ????}
    }

    在修改的過程中,往往是根據配置文件來判斷新功能是否啟用。上面代碼會造成什么問題呢:

    那么我們如何對這段代碼進行重構呢。首先,我們把新功能注冊的代碼抽取出來,通過反射來實現新的功能的注冊。

    private?void?RegisterTaskHandlerBundles()
    ????
    {
    ????????var?bundles = xxx.BLL.Caches.ServiceBundleCache.Instance.GetBundles("TaskHandlerBundle");
    ????????if?(bundles != null?&& bundles.Count > 0)
    ????????{
    ????????????var?asmCache = new?Dictionary<string, Assembly>();
    ????????????foreach?(var?bundle in?bundles)
    ????????????{
    ????????????????try
    ????????????????{
    ????????????????????if?(!asmCache.ContainsKey(bundle.Category)) asmCache.Add(bundle.Category, Assembly.Load(bundle.AssemblyName));
    ????????????????????var?handler = (ITaskHandler)asmCache[bundle.Category].CreateInstance(bundle.ClassName, false, BindingFlags.Default, null,
    ????????????????????????new?object[] { this, bundle }, null, null);
    ????????????????????_taskHandlerBundles.Add(bundle, handler);
    ????????????????}
    ????????????????catch?(Exception e)
    ????????????????{
    ????????????????????NLogHelper.Instance.Error("加載bundle[Name:{0},Assembly:{1}:Class:{2}]異常:{3}", bundle.Name, bundle.AssemblyName, bundle.ClassName, e.Message);
    ????????????????}
    ????????????}
    ????????}
    ????}

    修改代碼

    class?MainEngine:IEngine{
    ????private?NewFuncClass newCls=new?NewFuncClass();
    ????public?MainEngine(ConfigSettings config){
    ????????RegisterTaskHandlerBundles();
    ????}

    ????public?void?Start(){
    ????????_taskHandlerBundles.Start();
    ????}
    ????public?void?Stop(){
    ????????_taskHandlerBundles.Stop();
    ????}
    }

    OK,現在我們再來看看怎么實現原來的新增功能:你只需按規范新建一個類,繼承接口,并實現接口的方法。最后在表中新增一條記錄即可。我們再來看看這么做有什么好處:

    重構的目標之一,就是把框架和業務完全分離。有志于深入了解的同學,可以了解下反射、Ioc和插件話編程等。

    學會單元測試,培養你的重構意識

    可能上面說了這么多,還是有很多人并不理解重構。沒關系,在這里我教你們一個快速入門的辦法,就是單元測試。什么是單元測試,請自行。單元測試有什么要求?就是要求你要把每個方法都弄成盡量可以測試的。盡量讓你的方法變成是可測試的,就是培養你重構意識的利器。在你要求把方法變成可測試的過程,你就會發現你必須得不斷的修改你的方法,讓它的職責盡量單一,讓它盡量的與上下文無關,讓它盡可能通過方法參數的輸入輸出就能完成相關的功能,讓依賴的類都盡量改為接口而不是實例。最終,你就會發覺,這就是重構!而且是在不知不覺中,你重構的功力就會大大提升,你編程的水平也會大大提升!看到這里,有經驗的程序員就會問,你這是在鼓勵我使用TDD嗎?不,不是的。TDD(Test- )鼓勵的是測試驅動開發,未開發之前先編寫單元測試用例代碼,測試代碼確定需要編寫什么產品代碼。這是一種比較先進的開發方法,但是在編程的實踐過程中,我認為它過于繁瑣,很多中小企業很難實施,更別提我們個人開發者。我這里提倡你用單元測試培養你的重構意識,可以說是一種后驅動,用于提高你的重構能力和重構愿望,你完全可以把我的這個方法稱為“TDR(Test- )——測試驅動重構”。當然,在開發之前如果你有意識的讓方法可測試,那么你寫出來的函數將會是比較高質量的代碼。當你的函數都是一個個可重用性高的函數之時,你將會發現,寫代碼其實就像堆積木一樣,可以把一個大型的需求分解成無數細小的功能,很快的把需求實現。以下是一個超大方法中的一段代碼,如果你懂得怎樣讓這段代碼編程一個可測試的方法,那么,恭喜你,你入門了。

    所謂重構

    如果你有耐心看到這里,你應該知道,我并非一個標題黨,而這篇文章也許稱為“如何在編程中應用重構的思想”更為貼切,但是我不想用這么嚴肅的標題。很多編程初學者,或者有多年編程經驗的人都覺得閱讀別人的代碼非常困難,重構更是無從談起,他們要么對這些代碼望洋興嘆,要么就是推翻從來。但是,如果我們有重構的意識,以及在編程的過程中熟悉一些代碼調整和優化的小技巧,你自然而然就會培養出重構的能力。重構,其實很簡單:

    如果你堅持這么去做了,一段時間之后感覺自然就出來了。重構的目的,是讓你的代碼更為精簡、穩定、能夠重用,是最大程度的讓功能和業務分離。在重構的過程中,你的閱讀代碼的能力、寫出優秀代碼的能力以及系統架構能力都會穩步提升。你成為一個優秀的程序員將指日可待。—完—

    前線推出學習交流群一定要備注:研究/工作方向+地點+學校/公司+昵稱(如目標java+上海+上交+可可),根據格式備注,可更快被通過且邀請進群

    掃碼加我微信進群,內推和技術交流,大佬們零距離

    END

    開發者技術前線 ,匯集技術前線快訊和關注行業趨勢,大廠干貨,是開發者經歷和成長的優秀指南。

    歷史推薦

    好文點個在看吧!

網站首頁   |    關于我們   |    公司新聞   |    產品方案   |    用戶案例   |    售后服務   |    合作伙伴   |    人才招聘   |   

友情鏈接: 餐飲加盟

地址:北京市海淀區    電話:010-     郵箱:@126.com

備案號:冀ICP備2024067069號-3 北京科技有限公司版權所有