值得永久收藏的 C# 設(shè)計(jì)模式套路(二)
設(shè)計(jì)模式套路,第二彈。
?
在第一篇中,主要寫(xiě)了創(chuàng)造模式相關(guān)的幾種套路。如果你是剛看到這個(gè)文章,建議你先去看看第一篇?傳送門(mén)。
這一篇,我們說(shuō)說(shuō)結(jié)構(gòu)模式相關(guān)的套路。
結(jié)構(gòu)模式,主要作用是將類(lèi)型、對(duì)象和其它內(nèi)容放在一起,以創(chuàng)建更大的結(jié)構(gòu),同時(shí),又可以保持高度的靈活性和最佳性能。
也是像上一篇一樣,一個(gè)一個(gè)來(lái)說(shuō)。
一、適配器模式
適配器這個(gè)名字非常好理解,就像我們充電器的插頭,是用來(lái)協(xié)調(diào)兩個(gè)不同的東東之間的通信,并讓他們互相理解。
代碼也很簡(jiǎn)單:
public?class?AnotherType
{
????public?string?GetAuthorInfo()
????{
????????return?"I?am?WangPlus";
????}
}
public?interface?IAdapter
{
????string?GetInfo();
}
public?class?Adapter?:?IAdapter
{
????private?readonly?AnotherType?_anotherType;
????public?Adapter(AnotherType?anotherType)
????{
????????_anotherType?=?anotherType;
????}
????public?string?GetInfo()
????{
????????return?_anotherType.GetAuthorInfo();
????}
}
public?class?Example
{
????public?void?Test()
????{
????????var?adapter?=?new?Adapter(new?AnotherType());
????????Console.WriteLine(adapter.GetInfo());
????}
????//?result:
????//?I?am?WangPlus
}
沒(méi)想到吧?這樣的代碼經(jīng)常寫(xiě),居然也是個(gè)模式。所以呀,還是我上一篇提到的說(shuō)法:先有內(nèi)容,然后才有概念和標(biāo)準(zhǔn)。套路一直在,只是很多人不知道他叫什么。
二、橋模式
這個(gè)也好理解,就是在兩個(gè)東西之間搭了一個(gè)橋。
正常使用時(shí),是把實(shí)體類(lèi)與接口和抽象分離開(kāi)。有一個(gè)非常明顯的好處,是幾個(gè)實(shí)現(xiàn)可以使用不同的技術(shù)。
理解概念有點(diǎn)難,還是看代碼:
public?interface?IBridgeType
{
????void?GetInfo();
}
public?class?BridgeA?:?IBridgeType
{
????public?void?GetInfo()
????{
????????Console.WriteLine("I?am?WangPlus");
????}
}
public?class?BridgeB?:?IBridgeType
{
????public?void?GetInfo()
????{
????????Console.WriteLine("I?am?another?WangPlus");
????}
}
public?interface?IBridge
{
????public?IBridgeType?bridgeType
????{
????????get;
????????set;
????}
????void?GetInfo();
}
public?class?BridgeType?:?IBridge
{
????public?IBridgeType?bridgeType
????{
????????get;
????????set;
????}
????public?void?GetInfo()
????{
????????bridgeType.GetInfo();
????}
}
public?static?class?BridgeExample
{
????public?static?void?Test()
????{
????????var?bridgeType?=?new?BridgeType();
????????bridgeType.bridgeType?=?new?BridgeA();
????????bridgeType.GetInfo();
????????bridgeType.bridgeType?=?new?BridgeB();
????????bridgeType.GetInfo();
????}
????//?result:
????//?I?am?WangPlus
????//?I?am?another?WangPlus
}
BridgeA 和 BridgeA 是兩個(gè)實(shí)現(xiàn),這兒就是上面說(shuō)的不同的技術(shù),用不同的技術(shù)實(shí)現(xiàn)了同一個(gè)接口。然后通過(guò) IBridge 橋接到一個(gè)實(shí)現(xiàn)中。
使用時(shí),使用不同的實(shí)現(xiàn),但用相同的結(jié)構(gòu)進(jìn)行調(diào)用。在有需要時(shí),我們可以根據(jù)場(chǎng)景做出無(wú)數(shù)個(gè) BridgeN ,來(lái)實(shí)現(xiàn)黑盒類(lèi)似但白盒完全不同的實(shí)體。
三、復(fù)合模式
聽(tīng)著就大就復(fù)雜。沒(méi)錯(cuò),所有叫復(fù)合的東西,都會(huì)形成一個(gè)樹(shù)狀結(jié)構(gòu)。這好像是編程中的一個(gè)默認(rèn)約定?
復(fù)合設(shè)計(jì)模式,就是把對(duì)象放在一個(gè)更大的樹(shù)狀結(jié)構(gòu)的對(duì)象中,以多層次結(jié)構(gòu)來(lái)呈現(xiàn)對(duì)象,以統(tǒng)一方式處理對(duì)象。
看看這個(gè)復(fù)雜代碼的套路:
public?abstract?class?Mobile
{
????protected?string?Name;
????protected?Mobile(string?name)
????{
????????Name?=?name;
????}
????public?virtual?void?Add(Mobile?mobile)
????{
????????throw?new?NotImplementedException();
????}
????public?virtual?void?GetTree(int?indent)
????{
????????throw?new?NotImplementedException();
????}
}
public?class?MobileMemory?:?Mobile
{
????public?MobileMemory(string?name)?:?base(name)?{?}
????public?override?void?GetTree(int?indent)
????{
????????Console.WriteLine(new?String('-',?indent)?+?"?"?+?Name);
????}
}
public?class?MobileModel?:?Mobile
{
????private?readonly?List?_mobiles?=?new?List();
????public?MobileModel(string?name)?:?base(name)?{?}
????public?override?void?Add(Mobile?mobile)
????{
????????_mobiles.Add(mobile);
????}
????public?override?void?GetTree(int?indent)
????{
????????Console.WriteLine(new?String('-',?indent)?+?"+?"?+?Name);
????????foreach?(var?mobile?in?_mobiles)
????????{
????????????mobile.GetTree(indent?+?2);
????????}
????}
}
public?static?class?Example
{
????public?static?void?Test()
????{
????????var?brand?=?new?MobileModel("IPhone");
????????var?model13?=?new?MobileModel("13Pro");
????????var?model12?=?new?MobileModel("12Pro");
????????var?memory512G?=?new?MobileMemory("512G");
????????var?memory256G?=?new?MobileMemory("256G");
????????model13.Add(memory256G);
????????model13.Add(memory512G);
????????model12.Add(memory256G);
????????model12.Add(memory512G);
????????brand.Add(model12);
????????brand.Add(model13);
????????brand.GetTree(1);
????}
????//?result:
????//?---+?12Pro
????//?-----?256G
????//?-----?512G
????//?---+?13Pro
????//?-----?256G
????//?-----?512G
}
這個(gè)套路確實(shí)稍微有點(diǎn)復(fù)雜。補(bǔ)充解釋一下:
MobileMemory 和 MobileModel,是為了表現(xiàn)多種對(duì)象,沒(méi)有特殊含義,里面的區(qū)別就是 GetTree() 里打印出來(lái)的字符不同。
需要清楚理解的部分是 MobileModel 里構(gòu)建的 _mobiles,他是一個(gè)頂層抽象類(lèi)的數(shù)組。
這個(gè)模式最重要的結(jié)構(gòu),是用抽象類(lèi)去組織數(shù)據(jù),用實(shí)體類(lèi)去操作功能。
另外,如果你的開(kāi)發(fā)功力夠,在這個(gè)架構(gòu)中,實(shí)體本身也可以是復(fù)合對(duì)象。
四、裝飾模式
這也是一個(gè)常用的模式。通過(guò)對(duì)抽象或接口的擴(kuò)展,來(lái)加入對(duì)象功能。
而且這個(gè)套路的代碼特別好理解:
public?interface?IMobile
{
????public?string?Brand
????{
????????get;
????}
????public?string?Model
????{
????????get;
????}
????public?abstract?void?GetInfo();
}
public?class?IPhone?:?IMobile
{
????public?string?Brand?=>?"Apple";
????public?string?Model?=>?"13Pro";
????public?void?GetInfo()
????{
????????Console.WriteLine(this.ToJson());
????}
}
public?class?IPhoneWithMemory?:?IMobile
{
????private?readonly?IMobile?_mobile;
????public?IPhoneWithMemory(IMobile?mobile)
????{
????????_mobile?=?mobile;
????}
????public?string?Brand?=>?"Apple";
????public?string?Model?=>?"13Pro";
????public?string?Memory?=>?"512G";
????public?void?GetInfo()
????{
????????Console.WriteLine(this.ToJson());
????}
}
public?static?class?Example
{
????public?static?void?Test()
????{
????????var?iphone?=?new?IPhone();
????????iphone.GetInfo();
????????var?iphoneWithMemory?=?new?IPhoneWithMemory(iphone);
????????iphoneWithMemory.GetInfo();
????}
????//?result:
????//?{"Brand":"Apple","Model":"13Pro"}
????//?{"Brand":"Apple","Model":"13Pro","Memory":"512G"}
}
從上邊的 IMobile 接口開(kāi)始,每一個(gè)實(shí)體都是對(duì)前一個(gè)實(shí)體的補(bǔ)充和完善。
這種寫(xiě)法,在團(tuán)隊(duì)項(xiàng)目中很常見(jiàn),可以在確保不對(duì)別人的內(nèi)容進(jìn)行修改的基礎(chǔ)上,擴(kuò)展新的功能。不用改別人的代碼,又能補(bǔ)充進(jìn)去新的內(nèi)容。有沒(méi)有被爽到?
五、外觀(guān)模式
這個(gè)模式名稱(chēng)起得不知所云。不過(guò)意思和代碼倒是很簡(jiǎn)單,就是把其它的接口、類(lèi)、框架等的復(fù)雜系統(tǒng)匯集起來(lái),讓人能簡(jiǎn)單使用。
代碼一看就懂:
public?class?Facade
{
????private?readonly?Mobile?_mobile;
????private?readonly?Laptop?_laptop;
????public?Facade(Mobile?mobile,?Laptop?laptop)
????{
????????_mobile?=?mobile;
????????_laptop?=?laptop;
????}
????public?void?GetInfo()
????{
????????_mobile.GetInfo();
????????_laptop.GetInfo();
????}
}
public?class?Mobile
{
????public?void?GetInfo()
????{
????????Console.WriteLine("I?am?mobile");
????}
}
public?class?Laptop
{
????public?void?GetInfo()
????{
????????Console.WriteLine("I?am?laptop");
????}
}
public?static?class?Example
{
????public?static?void?Test()
????{
????????var?mobile?=?new?Mobile();
????????var?laptop?=?new?Laptop();
????????var?facade?=?new?Facade(mobile,?laptop);
????????facade.GetInfo();
????}
????//?result:
????//?I?am?mobile
????//?I?am?laptop
}
這個(gè)模式,在開(kāi)發(fā)中也用得比較多。尤其在團(tuán)隊(duì)項(xiàng)目中,會(huì)經(jīng)常用到,原因跟上面一樣。
六、輕量級(jí)模式
嗯,就是輕的意思。這個(gè)輕,不是寫(xiě)的少,而是內(nèi)存使用少。
所以這個(gè)模式的主要優(yōu)勢(shì),就是節(jié)省內(nèi)存。
這個(gè)模式?jīng)]辦法給出簡(jiǎn)單的套路。他本身是一種想法,是一種寫(xiě)在代碼中的思想,而不是一個(gè)套路性的代碼組。
我拿一段代碼來(lái)說(shuō)明一下:
public?class?Flyweight
{
????private?readonly?Liststring,?DemoClass>>?_sharedObjects?=?new();
????public?Flyweight()
????{
????????_sharedObjects.Add(new?KeyValuePair<string,?DemoClass>("A",?new?DemoClass()));
????????_sharedObjects.Add(new?KeyValuePair<string,?DemoClass>("B",?new?DemoClass()));
????}
????public?DemoClass?GetObject(string?key)
????{
????????return?_sharedObjects.SingleOrDefault(c?=>?c.Key?==?key).Value;
????}
}
public?interface?IDemoClass
{
????public?void?Operation(string?name);
}
public?class?DemoClass?:?IDemoClass
{
????public?void?Operation(string?name)
????{
????????Console.WriteLine(name);
????}
}
public?static?class?Example
{
????public?static?void?Test()
????{
????????var?flyweight?=?new?Flyweight();
????????flyweight.GetObject("A").Operation("Hello");
????????flyweight.GetObject("B").Operation("I?am?WangPlus");
????????var?heavy?=?new?DemoClass();
????????heavy.Operation("Hello,?I?am?WangPlus");
????}
????//?result:
????//?下面是輕量級(jí)模式
????//?Hello
????//?I?am?WangPlus
????//?下面是普通模式
????//?Hello,?I?am?WangPlus
}
在這段代碼里,真正屬于輕量級(jí)模式模式的其實(shí)只是里面的這一段:
????private?readonly?Liststring,?DemoClass>>?_sharedObjects?=?new();
????public?Flyweight()
????{
????????_sharedObjects.Add(new?KeyValuePair<string,?DemoClass>("A",?new?DemoClass()));
????????_sharedObjects.Add(new?KeyValuePair<string,?DemoClass>("B",?new?DemoClass()));
????}
能理解嗎?這一段主要是構(gòu)造了一個(gè)集合,用來(lái)存放對(duì)象。后面調(diào)用對(duì)象時(shí),是從這個(gè)集合里出來(lái)的。
這樣寫(xiě)的好處,是如果對(duì)象很多,每次 new 會(huì)占用大量?jī)?nèi)存,而先期存儲(chǔ)在一個(gè)集合中,會(huì)讓這個(gè)內(nèi)存占用變得小很多。
好吧,如果不理解,也沒(méi)關(guān)系。在 Dotnet 的整個(gè)源碼中,這樣使用的也并不多。
所以這個(gè)模式屬于一個(gè)可以意會(huì)的模式。而且事實(shí)上,現(xiàn)在的內(nèi)存成本之低,已經(jīng)很少需要這么費(fèi)心了。
七、代理模式
這個(gè)模式也好理解,就是加了一個(gè)代理。通過(guò)中間類(lèi)型來(lái)控制對(duì)于主類(lèi)型的訪(fǎng)問(wèn)。
嗯,別擔(dān)心,這個(gè)是有套路的。
public?abstract?class?MainAbst
{
????public?abstract?void?GetInfo();
}
public?class?MainClass?:?MainAbst
{
????public?override?void?GetInfo()
????{
????????Console.WriteLine("I?am?WangPlus");
????}
}
public?class?Proxy?:?MainAbst
{
????private?MainClass?_main;
????public?Proxy(MainClass?main)
????{
????????_main?=?main;
????}
????public?override?void?GetInfo()
????{
????????_main????=?new?MainClass();
????????_main.GetInfo();
????}
}
public?static?class?ProxyExample
{
????public?static?void?Test()
????{
????????var?proxy?=?new?Proxy(new?MainClass());
????????proxy.GetInfo();
????}
????//?result:
????//?I?am?WangPlus
}
這個(gè)套路也容易懂。MainClass 是我們的主類(lèi),在執(zhí)行一些特定的方法。加出了一個(gè)代理類(lèi) Proxy。外部調(diào)用時(shí),通過(guò) Proxy 來(lái)調(diào)用主類(lèi)的方法,同時(shí),如果有需要對(duì)主類(lèi)的輸入輸出進(jìn)行處理,可以在 Proxy 的方法里直接寫(xiě)。
又是一個(gè)團(tuán)隊(duì)協(xié)作會(huì)用到的模式,嘿嘿。
?
結(jié)構(gòu)模式的套路就是這樣了。
還有一類(lèi)模式,是行為設(shè)計(jì)模式。咱們改天再寫(xiě)。
喜歡就來(lái)個(gè)三連,讓更多人因你而受益
