麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 學(xué)院 > 開發(fā)設(shè)計 > 正文

【重構(gòu)學(xué)習(xí)】07數(shù)據(jù)的重構(gòu)

2019-11-14 13:35:36
字體:
供稿:網(wǎng)友

這里的數(shù)據(jù)指的大概就是字段(貌似這章有些東西也是過時了,因為現(xiàn)在的.NET已經(jīng)發(fā)展了很久了,包括java也是)

1、自封裝字段(其實就是屬性啦,過時了)

修改點:直接訪問字段,但是與字段間的耦合關(guān)系逐漸變得笨拙

做法:為這個字段建立一個取值/設(shè)值函數(shù),并且只以這些函數(shù)來訪問數(shù)據(jù)

好吧,現(xiàn)在明白屬性是怎么來的了嗎,就是因為這個原因,所以有的人會干脆和你說,你不要寫公共字段,直接寫公共屬性。

因為公共字段能做的公共屬性都能做,不能做的公共屬性也能做。

public string 我叫公共屬性 { get; set; }

多寫個這東西好用多了,以后萬一有個什么反射的需要,也簡單多了,舉手之勞,所以忘記還有公共字段這回事吧。

2、以對象取代字段

修改點:你有一個字段,需要與其它數(shù)據(jù)和行為一起使用才有意義

做法:用對象取代字段

簡單地說,你玩過DateTime這個類嗎,就是系統(tǒng)的類,其實就是對數(shù)據(jù)的一個封裝啊。

如果沒有這個東西,那么你是不是要用年,月,日(就算沒有時分秒吧),三個字段來表示。搞的多了自然就像自己封裝一個Date類,哪天又多了個按格式輸出的要求,是不是要再寫個格式化函數(shù)

所以那么就把這些好基友都放在一起好了,于是DateTime就成了基友之家。(然而我們需要更多的DateTime樣的基友之家)

3、將值對象改為引用對象

修改點:你從一個類衍生出許多彼此相等的實例,希望將它們替換為同一個對象

做法:將這個值對象變?yōu)橐脤ο?/p>

簡單地說,這里所謂的值對象是指DateTime這種對象,即可以建立很多次,純數(shù)據(jù)的傳遞,而所謂的引用對象是指緩存的東西,比如淘寶界面的分類數(shù)據(jù),因為所有人看到的分類都是一樣的,那么我們不需要每次都從數(shù)據(jù)庫取值

緩存一下就好,不需要每次New一個分類對象,然后調(diào)用查詢函數(shù),而是去找緩存去看是否有了緩存,有了緩存,那么就調(diào)用,沒有緩存那么就再從數(shù)據(jù)庫里面取值。只要緩存一更改,大家看到的分類就都更改了。

好了,就是這么個意思

其中為了適應(yīng)也許引用對象會根據(jù)不同的類型創(chuàng)建一個不同的對象,(就像有個墻類,上節(jié)課Troy教小朋友們?nèi)绾伟岽u砌墻,所以新建正常的墻的對象,這節(jié)課話題換了,Troy教小朋友們碼長城,于是就新建一個長城對象)

當(dāng)涉及到切換子類的時候,可以用這種方法使得客戶端調(diào)用的時候不需要知道還有長城這個類,只需要用墻類里的工廠函數(shù)就好了,當(dāng)然也許你下次還得搞個扛水泥去搞個水泥墻的類,那么在 工廠函數(shù)里再加就好了。

所以提到的一個重構(gòu)方法:

將構(gòu)造函數(shù)改為調(diào)用工廠函數(shù),即

 public class Wall{        public Wall(string length){            //啦啦啦        }   }

改為

public class Wall{        public static Wall Create(string type){            if (type=="Great"){                return new GreatWall("一萬里");            }            else{                return new Wall("一米");            }        }        PRotected Wall(string length)        {            //啦啦啦        }    }    public class GreatWall:Wall    {        public GreatWall(string length):base(length)        {            //啦啦啦        }    }

這種方法的用處在于客戶調(diào)用不需要去知道子類,只需要知道type就行了,當(dāng)然具體情況具體對待,你也可以不用這么寫

然后這個所謂的引用變量的穩(wěn)妥一點的做法還是要判斷一下這個對象存不存在,不存在就去取值,存在就直接調(diào)用。

4、將引用對象改為值對象

  是的,你沒有看錯,當(dāng)你千辛萬苦改成了值對象之后,有個需求第二天可能又會讓你改回來。。

  動機:因為你的引用對象很小且不可變,而且不易管理。

  不可變是什么鬼?我看了《重構(gòu)》這個看了半天,最后才弄明白。

  還是用分類緩存來說,淘寶的緩存就是可變的,因為淘寶的緩存假如作為商家的你可以自己去去增加分類,然后刷新它,那么所有的客戶得到的分類都變了。

  那么不可變是就是:

  

  我在本地把百度首頁改成這樣,但是你們這些人并不知道我已經(jīng)在用谷歌了╮(╯▽╰)╭

 

  換句話說,有天,馬老板說咱淘寶分類就這么搞了,無論怎樣就這么多分類了,不許商家去增加分類,那么,這個緩存就是不可變的了,某天他說不要那么多分類了,就假貨和真貨兩個類的時候,那么引用對象足夠小了

  那么這個引用對象也就不需要了,改成值對象就好了,比如像百度一下直接作為文本,寫死在頁面上了。

  好吧,我就是這么理解的。

  正經(jīng)話:引用對象就是客戶公用的,當(dāng)我去改動后,其他客戶看到的也就變了,而值對象不是,當(dāng)我改動后只有我自己這邊變了。(懂了嗎,我覺得自己已經(jīng)講得很清楚了)

5、以對象取代數(shù)組

  修改點:你有一個數(shù)組,其中的元素代表不同的東西

  做法:以對象替換數(shù)組,對于數(shù)組中的每個東西,以一個字段來表示。

  換句話說,有個混蛋把DateTime的年月日寫在一個int date[3];這樣的東西里面,其中date[0]代表年,date[1]代表月,你是否想要打死他?

 6、復(fù)制"被監(jiān)視數(shù)據(jù)"(大白話:別把數(shù)據(jù)放在表現(xiàn)層)

  修改點:你又一些領(lǐng)域數(shù)據(jù)置身于GUI控件中,而領(lǐng)域函數(shù)需要訪問數(shù)據(jù)。

  做法:將該數(shù)據(jù)復(fù)制到一個領(lǐng)域?qū)ο笾小=⒁粋€Observer模式,用以同步領(lǐng)域?qū)ο蠛虶UI對象內(nèi)的重復(fù)數(shù)據(jù)。

  一個分層良好的系統(tǒng),應(yīng)該將處理用戶界面和處理業(yè)務(wù)邏輯的代碼分開。

  動機:(1)你可能需要使用不同的用戶界面來表現(xiàn)相同的業(yè)務(wù)邏輯,如果同時承擔(dān)兩種責(zé)任,用戶界面會變得過分復(fù)雜;(手機端微信和PC端微信)

     (2)與GUI隔離之后,領(lǐng)域?qū)ο蟮木S護和演化都會很容易,你甚至可以讓不同的開發(fā)者負(fù)責(zé)不同部分的開發(fā)。

  作為WEB開發(fā)者,MVC都知道吧,所以這里講得就是業(yè)務(wù)邏輯別寫在表現(xiàn)層,當(dāng)然也包括數(shù)據(jù)。

  做法:

  話說這小節(jié)的做法真的超長,一大堆java代碼,我最終還是沒敢看。

  由于我在客戶端程序這方面基本上就只寫過一些小程序,總是業(yè)務(wù)邏輯和表現(xiàn)層混在一起,所以我就寫個自己經(jīng)常寫的垃圾代碼改一下好了。

  

  功能:

  每個按鈕點一次講一句話

  好了,下面貼上這段典型的混合果汁代碼:

  

private void btnWangyi_Click(object sender, EventArgs e)        {            txtYongBoy.Text += getNewLineString("網(wǎng)易:")+ getNewLineString("我是一個有態(tài)度的媒體,我每天引誘一堆人互噴賺流量");        }        private void BtnWumao_Click(object sender, EventArgs e)        {            txtYongBoy.Text += getNewLineString("五毛(一群整天陰謀論的人):") + getNewLineString("美狗SB,你們這些美分就是成天造謠,臭傻叉,汪汪汪");        }        private void btnMeifen_Click(object sender, EventArgs e)        {            txtYongBoy.Text +=  getNewLineString("美分(一群整天抱怨國家社會父母,自己又沒能力的low逼):")+  getNewLineString("五毛SB,只有我清醒著,你們這群被洗腦的人,我最清醒,我為什么沒被洗腦,因為汪汪汪");        }        private void btnZhengfu_Click(object sender, EventArgs e)        {            txtYongBoy.Text +=  getNewLineString("政府(話說真的想看,網(wǎng)絡(luò)對它不存在匿名,程序員一句代碼的事,太可怕了):") + getNewLineString("我就看看,不說話");        }        private void btnSendFuck_Click(object sender, EventArgs e)        {            txtYongBoy.Text += getNewLineString("Troy:")+ getNewLineString("好好工作,好好賺錢,有能力不喜歡這里分分鐘移民,沒能力出去也是low,要不你去個唐人街去旅游看看,lol");        }        private string getNewLineString(string str){            return str + Environment.NewLine + Environment.NewLine;        }

好了,按照作者的話來講,應(yīng)該是改成接下來這種樣子:

public partial class MainForm : Form    {        private Speak mySpeak;        public MainForm()        {            InitializeComponent();            mySpeak = new Speak(this);        }        private void btnWangyi_Click(object sender, EventArgs e)        {            mySpeak.SendMySound("網(wǎng)易:", "我是一個有態(tài)度的媒體,我每天引誘一堆人互噴賺流量");        }        private void BtnWumao_Click(object sender, EventArgs e)        {            mySpeak.SendMySound("五毛(一群整天陰謀論的人):", "美狗SB,你們這些美分就是成天造謠,臭傻叉,汪汪汪");        }        private void btnMeifen_Click(object sender, EventArgs e)        {            mySpeak.SendMySound("美分(一群整天抱怨國家社會父母,自己又沒能力的low逼):", "五毛SB,只有我清醒著,你們這群被洗腦的人,我最清醒,我為什么沒被洗腦,因為汪汪汪");        }        private void btnZhengfu_Click(object sender, EventArgs e)        {            mySpeak.SendMySound("政府(話說真的想看,網(wǎng)絡(luò)對它不存在匿名,程序員一句代碼的事,太可怕了):", "我就看看,不說話");        }        private void btnSendFuck_Click(object sender, EventArgs e)        {            mySpeak.SendMySound("Troy:", "好好工作,好好賺錢,有能力不喜歡這里分分鐘移民,沒能力出去也是low,要不你去個唐人街去旅游看看,lol");        }        public void UpdateSound(string text){            txtYongBoy.Text=text;        }    }    public class Speak {        private string Alltext;        private MainForm speakForm;        public Speak(MainForm form) {            speakForm = form;        }        private void updateSounds(){            speakForm.UpdateSound(Alltext);        }        public void SendMySound(string speaker,string text){            Alltext += getNewLineString(speaker) + getNewLineString(text);            this.updateSounds();        }        private string getNewLineString(string str)        {            return str + Environment.NewLine + Environment.NewLine;        }    }

 

好吧,雖然上面代碼很渣,但是應(yīng)該足夠清晰向你表示什么叫分離數(shù)據(jù)到業(yè)務(wù)層了。

如果你又興趣可以改寫我上面的渣代碼,比如給MainForm繼承某個接口,然后Speak類里的speakForm是這個接口而不是MainForm你覺得是不是好看多了,如果你繼續(xù)改下去,那么你就會玩觀察者模式了

7、單向關(guān)聯(lián)改為雙向關(guān)聯(lián)

修改點:兩個類都需要使用對方特性,但其間只有一條單向連接。

做法:添加一個反向指針,并使修改函數(shù)能夠同時更新兩條連接。

可以參考我上面的例子,MainForm和Speak就是雙向關(guān)聯(lián)的兩個類,你可能不知道指針是什么,你就理解為引用就行了。(或者自己去看一下C語言)

8、雙向關(guān)聯(lián)改為單向關(guān)聯(lián)

修改點:兩個類之間有雙向關(guān)聯(lián),但其中一個類如今不再需要另一個類的特性。

做法:去除不必要的關(guān)聯(lián)

這句話是我自己的見解,或許是錯誤的,你自己決定是否這么玩:我不太贊成雙向關(guān)聯(lián),單向關(guān)聯(lián)就好了,我在A類要用B類,我自己在A類建個函數(shù),接收B類的對象,到時候調(diào)用B類的對象的方法就好。

大家各玩各的,分開好一點,不要攪到一起。所以如果是我寫正式的代碼,我會盡量把所有的雙向關(guān)系全換成單向的,除非我自己偷懶(我經(jīng)常偷懶⊙﹏⊙‖∣,通常這意味著我或者將來的某人要為此付出代價/(^o^)/),好吧,仁者見仁,智者見智,也許我是錯的,僅僅我自己覺得雙向關(guān)系對我來說很不清晰,會造成耦合。

在這里Martin也說了,雙向關(guān)聯(lián)很有用,但是你必須為它付出代價,那就是維護雙向連接、確保對象被正確創(chuàng)建和刪除而增加的復(fù)雜度。

9、以字面常量來取代魔法數(shù)字

修改點:你有一個字面數(shù)值,帶有特別含義

做法:創(chuàng)造一個常量,根據(jù)其意義為它命名,并將上述的字面數(shù)值替換成這個常量。

舉個簡單例子:

    static void Main(string[] args)        {            Console.Write("請問一天有多少個小時:");            if (24 != Console.Read())            {                Console.WriteLine("你說什么就是什么咯!");            }            else {                Console.WriteLine("答對了");            }        }  

一般大家

const int HourNumOfOneDay = 24;            Console.Write("請問一天有多少個小時:");            if (HourNumOfOneDay != Console.Read())            {                Console.WriteLine("你說什么就是什么咯!");            }            else {                Console.WriteLine("答對了");            }

好吧,這里怎么寫都一樣,其實很好理解,那么你自己想象一下現(xiàn)在你是在一個業(yè)務(wù)很復(fù)雜的系統(tǒng)中,突然出現(xiàn)了一個21.23這樣詭異的數(shù)字,然后與某個變量做比較, 你可能就會覺得這段代碼理解起來有點吃力了,如果出現(xiàn)一大堆,也許你就要花很長時間去研究業(yè)務(wù)了。

10、封裝字段好吧,已經(jīng)過時了,就是屬性

修改點:你的類中存在一個public字段

做法:將它聲明為private,并提供相應(yīng)的訪問函數(shù)

11、封裝集合

修改點:有個函數(shù)返回一個集合

做法:將這個函數(shù)返回該集合的一個只讀副本,并在這個類中提供添加.移除集合元素的函數(shù)

因為現(xiàn)在給你的集合是個引用,所以你可以修改這個集合,而這個集合的擁有者一無所知。

好吧,在我的理解力,他的作用大概就是和封裝字段一樣的性質(zhì),為了數(shù)據(jù)隱藏,并且讓數(shù)據(jù)和行為保持一致性

并且保持?jǐn)?shù)據(jù)嚴(yán)謹(jǐn)性和安全性(好吧,反正我感覺自己應(yīng)該不會用到這項重構(gòu),這個重構(gòu)的作用應(yīng)該是為了防止一些非正常的編碼行為)。

那么我多句嘴吧:別用數(shù)組,用list和dictionary這種集合就好了(我也不知道多久沒用過數(shù)組了 )。

12、以數(shù)據(jù)類取代記錄

修改點:你需要面對傳統(tǒng)編程環(huán)境中的記錄結(jié)構(gòu)

做法:為該記錄創(chuàng)建一個“啞”數(shù)據(jù)對象

好吧,這個東西你可以理解為,如何把一堆數(shù)組轉(zhuǎn)化為類對象,舉個例子:溝通數(shù)據(jù)庫的ORM

13、以類取代類型碼

修改點:類中有一個數(shù)值類型碼,但它并不影響類的行為

做法:以一個類替換該數(shù)值類型碼

好吧,在.NET里你用enum枚舉就好了,非常簡潔易用。

14、以子類取代類型碼

修改點:你有一個不可變的類型碼,它會影響類的行為

做法:以子類去取代這個類型碼

動機:它把對不同行為的了解,從類用戶那兒轉(zhuǎn)移到了類本身。如果加入新的行為變化,只需要添加一個子類就好了,如果不用多態(tài)機制來處理,那么你就要找到每一個條件表達(dá)式,然后逐一修改。

這種做法就是為了消除switch和if這樣的分支語句,所以用子類繼承父類的多態(tài)來處理,如果沒有出現(xiàn)條件表達(dá)式那么就用第13個重構(gòu)行為就好了。

好吧,你也許仔細(xì)想一下switch還是沒有消除。哈哈,當(dāng)初我糾結(jié)了好久。其實還是很簡單,switch當(dāng)然不會消除,但是它在客戶類調(diào)用的時候已經(jīng)看不到了啊,客戶不需要知道內(nèi)部邏輯,也就是說你寫的代碼,上一級調(diào)用起來會更簡單。

更重要的是,你可能通過這種方式就只需要一次switch語句了,因為你只需要知道創(chuàng)建哪個類的對象就好了

說了這么多,如果你還是不懂那么就來看下面這個簡單例子吧。

class Program    {        static void Main(string[] args)        {            Console.Write("請輸入您要釋放技能的神奇寶貝:");            string name=Console.ReadLine();            new 神奇寶貝().釋放技能(name);            new 神奇寶貝().叫一聲(name);        }    }    public class 神奇寶貝 {        public 神奇寶貝() {                     }        public void 釋放技能(string name){            switch(name){                case "皮卡丘":                    Console.WriteLine("您的精靈釋放十萬伏特");                    break;                case "杰尼龜":                    Console.WriteLine("您的精靈釋放水槍");                    break;                default:                    Console.WriteLine("小火龍釋放火球");                    break;            }        }        public void 叫一聲(string name)        {            switch (name)            {                case "皮卡丘":                    Console.WriteLine("皮卡");                    break;                case "杰尼龜":                    Console.WriteLine("杰尼");                    break;                default:                    Console.WriteLine("呵呵噠");                    break;            }        }    }}

修改后:

class Program    {        static void Main(string[] args)        {            Console.Write("請輸入您要釋放技能的神奇寶貝:");            string name=Console.ReadLine();            var 我的神奇寶貝 = 神奇寶貝.Create(name);            我的神奇寶貝.釋放技能();            我的神奇寶貝.叫一聲();        }    }    public abstract class 神奇寶貝 {        public static 神奇寶貝 Create(string name) {            switch (name) {                case "皮卡丘":                    return new 皮卡丘();                case "杰尼龜":                    return new 杰尼龜();                default:                    return new 小火龍();            }                }        public abstract void 釋放技能();        public abstract void 叫一聲();    }    public class 皮卡丘 : 神奇寶貝 {        public 皮卡丘() { }        public override void 叫一聲()        {            Console.WriteLine("皮卡");          }        public override void 釋放技能()        {            Console.WriteLine("您的精靈釋放十萬伏特");        }    }    public class 杰尼龜 : 神奇寶貝    {        public 杰尼龜() { }        public override void 叫一聲()        {            Console.WriteLine("杰尼");        }        public override void 釋放技能()        {            Console.WriteLine("您的精靈釋放水槍");        }    }    public class 小火龍 : 神奇寶貝    {        public 小火龍() { }        public override void 叫一聲()        {            Console.WriteLine("呵呵噠");        }        public override void 釋放技能()        {            Console.WriteLine("小火龍釋放火球");        }    }

修改完畢,首先我們可以看到整個代碼結(jié)構(gòu)清晰了很多,其次,對于在Main函數(shù)里調(diào)用方法的你而言,你覺得你更喜歡哪種調(diào)用方法,最后想象一下寵物小精靈可是有五百多只,而且不只是叫一聲和釋放技能兩個行為,你如果加分支,那就真是毀童年系列了

15、用策略模式或者狀態(tài)模式取代類型碼

類型碼:為什么你們總是要取代我,我就這么不招人喜歡嗎(#‵′)凸

  上面的兩個模式都是23種設(shè)計模式之一。

  修改點:你有一個類型碼,它會影響類的行為,但你無法通過繼承手法消除它

  做法:以狀態(tài)對象取代類型碼

  以下是兩種你不能通過14來解決的情況:

  1.類型碼的值在對象創(chuàng)建后發(fā)生了改變(皮卡丘在調(diào)用完釋放技能后,自動修改類型碼成了杰尼龜,然后再叫一聲的情況、)

  2.由于某種原因,類型碼宿主,已經(jīng)有了子類(因為你是用子類集成來消滅分支,所以人家有了子類你肯定就不能這么玩了)

  一提起這兩個模式就感覺好厲害的樣子,根本就看不下去了。

  其實很簡單啦,不要管這亂七八糟的東模式西模式的。

  現(xiàn)在的問題在于我沒法用子類消滅分支了,所以解決問題。

  因為我不能繼承現(xiàn)在的Current類,所以我新建一個type類(具體類名你自己取),然后給有幾個類型碼我就繼承幾個子類,看我不就能用繼承的方式來消滅分支了嗎

  那么能解決分支了,剩下的就是把Current類和type類關(guān)聯(lián)起來咯,你在Current類里加一個type的字段myType,然后寫一個設(shè)置type字段的函數(shù),函數(shù)里面就用類型碼來switch區(qū)分創(chuàng)建哪一個type類的子類,這樣就OK了嘛。

  什么設(shè)計模式嘛,根本就不需要記住,是不是?

  臨時百度一下就OK,拿這幾套劍法套路用來裝裝B就好了,正式寫代碼不用想,需要什么就寫什么好了,我管你什么套路,一板磚能解決那扔過去就好了。

  作為一個劍客,目的就是砍倒人,你真的這么在意用什么劍法嗎?

16、以字段取代子類

  修改點:你的各個子類的唯一差別只在“返回常量數(shù)據(jù)”的函數(shù)上

  做法:修改這些函數(shù),使它們返回父類的某個(新增)字段,然后銷毀函數(shù)。

  動機:建立子類的目的是為了增加新特性或變化和行為。有一種變化行為被稱為常量函數(shù),它們返回一個硬編碼的值。

  這個東西有其價值,但是如果子類里面僅僅只有這些常量函數(shù)的話,就沒有足夠的存在價值了,你可以在父類中加一個與常量函數(shù)相對應(yīng)的字段,從而完全去除這樣的子類,如此一來就可以避免因繼承而帶來的額外復(fù)雜性。


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 毛片视频免费观看 | 国产欧美一区二区三区免费看 | 人禽l交免费视频 | 欧美日韩后 | 777sesese| 一区二区三区欧美在线 | 天堂亚洲一区 | 91社影院在线观看 | 成年人视频免费看 | 国产一区二区欧美精品 | 久久久久国产精品久久久久 | av免费在线免费观看 | 精品久久久一二三区播放播放播放视频 | 91九色精品国产 | 欧美一级黄视频 | 中国a级黄色片 | 成人福利视频导航 | 日本黄色免费播放 | 在线亚州| 国产一级毛片视频在线! | ⅴideo裸体秀hd | 国产精品99久久久久久久女警 | 中文字幕天堂在线 | 国内精品免费一区二区2001 | 色淫湿视频 | 国产精品久久久久一区二区 | 日本中文字幕久久 | 色戒在线版 | 国产成人自拍视频在线 | 毛片视频网站 | 99精品视频一区二区 | 久久精品视频16 | 女人一级一级毛片 | 中国杭州少妇xxxx做受 | 欧美视频国产 | 欧美wwwsss9999| 精品国产视频一区二区三区 | 色婷婷一区二区三区 | 欧美成年人在线视频 | 欧美性生活久久久 | 青草av.久久免费一区 |