Design Patterns之Adapter Pattern总结
看了一兩節(jié)李建忠老師的WebCast《C#設(shè)計(jì)模式縱橫談》,最大的感覺就是他講得很通俗易懂,但每次看完后過段時間沒接觸又忘了,先前有看過了他講的Abstract Factory模式,雖然現(xiàn)在對其還有印象,但很模糊,這次去圖書館借了本Steven John Metsker的《C#設(shè)計(jì)模式》,打算按它的章節(jié)順序,再結(jié)合李建忠老師的設(shè)計(jì)模式系列課程來學(xué)習(xí)。我想有必要在每次學(xué)習(xí)之后作一個總結(jié),于是,本文就總結(jié)一下今天看的Adapter模式吧。如果文中有什么地方總結(jié)的不好甚至寫錯了,還請各位多多指正。
以下示例代碼均基于李建忠老師設(shè)計(jì)模式課程上的示例程序。
首先,Adapter模式的用途是什么?假設(shè)我們有一些好不容易寫出來的很好很強(qiáng)大的類,而且它又能滿足當(dāng)前項(xiàng)目的某種需求,那當(dāng)然就最好把它應(yīng)用在新項(xiàng)目中,而不用再去重新寫,以達(dá)到復(fù)用的目的,但它的接口卻往往跟當(dāng)前環(huán)境不兼容,此時我們就可以考慮使用Adapter(適配器)模式??梢韵胂笊钪械睦?#xff1a;手機(jī),手機(jī)電池的電量用光了以后,我們就要對它充電,而市電就可以滿足這個需求,但有個問題,我們墻上的插口不是三孔的就是兩孔的,而手機(jī)的充電口只有一孔(我的是一個小圓孔),另外市電是220V的,手機(jī)不能直接接受這么高的電壓,那怎么辦呢?有什么辦法可以既發(fā)揮市電的充電功能,又能解決接口的問題呢?那就是手機(jī)充電器,它就是一個適配器,通過它,就能讓市電給手機(jī)充電了。我們的Adapter(適配器)模式也是這個道理,通過一個適配器(類)讓我們已經(jīng)存在的代碼為我們的新環(huán)境服務(wù)。
接下來看例子:我們要利用.Net Framework中的System.Collections.ArrayList(已經(jīng)存在的代碼)來實(shí)現(xiàn)一個棧(新環(huán)境),堆棧是一個FILO(First In Last Out)的數(shù)據(jù)結(jié)構(gòu),我們來實(shí)現(xiàn)一個簡單的版本,它有三個方法:Push(object o),把o壓入棧頂;Pop(),彈出棧頂元素;Peek(),得到棧頂元素的值,但不彈出,我們期望的棧是這樣子的:
????????/// <summary>
??? /// 新環(huán)境中期望的接口
??? /// </summary>
??? public interface IStack {
?????? void Push(object o);
?????? object Pop();
?????? object Peek();
????????}
而在ArrayList類中是沒有什么Push,Pop方法的,所以我們用一個適配器,讓ArrayList為我們的棧服務(wù):
????????///<summary>
??? /// 類適配器
??? /// </summary>
??? public class ClassAdapter : ArrayList, IStack {
?????? public void Push(object o) {
?????????? this.Add(o);
?????? }
?????? public object Pop() {
?????????? object o = this[this.Count - 1];
?????????? this.RemoveAt(this.Count - 1);
??? ?????? return o;
?????? }
?????? public object Peek() {
?????????? return this[this.Count - 1];
?????? }
????????}
OK,這個ClassAdapter不就已經(jīng)是一個可以用的棧了么,我們可以在main方法中測試:
???????????public static void Main(string[] args) {
?????????? Console.WriteLine("Class Adapter Test...");
?????????? ClassAdapter ca = new ClassAdapter();
?????????? ca.Push("Lin");
?????????? ca.Push("Mou");
?????????? ca.Push("Hong");
?????????? //ca.Remove(“Mou”);
?????????? //Console.WriteLine(ca[0]);
?????????? Console.WriteLine(ca.Peek());
?????????? Console.WriteLine(ca.Pop());
???????????}
棧的作用已經(jīng)達(dá)到了。這個適配器叫類適配器,它是通過繼承已存在的代碼(ArrayList類),并實(shí)現(xiàn)新環(huán)境期望的接口(IStack)來達(dá)到適配器的目的。
但是,這里存在問題:就是上面Main中注釋掉的那兩行,我們要實(shí)現(xiàn)的是一個棧,但是ca.Remove(“Mou”)卻可以把不位于棧頂?shù)脑亟o移掉,ca[0]又可以取到棧底的元素值,這還叫什么棧啊?另外,如果我們要使用的已存在的代碼不只一個,比如有兩個,那我們要讓ClassAdapter去繼承這兩個類嗎?顯然不行,C#是不允許多繼承的。
于是我們來看下面的適配器:對象適配器。
新環(huán)境中期望得到的接口仍然是IStack,但是我們的適配器是用ObjectAdapter:
????????///<summary>
??? /// 對象適配器
??? /// </summary>
??? public class ObjectAdapter : IStack {
?????? private ArrayList adaptee;? //被適配的對象
?????? public ObjectAdapter() {
?????????? adaptee = new ArrayList();
?????? }
?????? public void Push(object o) {
?????????? adaptee.Add(o);
?????? }
?????? public object Pop() {
?????????? object o = adaptee[adaptee.Count - 1];
?????????? adaptee.RemoveAt(adaptee.Count - 1);
?????????? return o;
?????? }
?????? public object Peek() {
?????????? return adaptee[adaptee.Count - 1];
?????? }
??? }
可以看到,對象適配器把被適配的對象(已存在的代碼)作為自己的一個字段來用,然后再實(shí)現(xiàn)新環(huán)境期望的接口IStack,這個對象適配器可以解決前面那個類適配器ClassAdapter留下的兩個問題:
(1)當(dāng)我們實(shí)例化ObjectAdapter后,能調(diào)用的方法僅僅只是Push(object o),Pop(),Peek(),再也沒辦法去調(diào)用什么Remove()之類的方法;
(2)如果我們要使用的已有代碼不只兩個類,比如還有一個ClassTwo類,那我們只需要在ObjectAdapter中加一個ClassTwo類型的字段就可以了。
于是,我們得到了這個結(jié)論:盡量少用類適配器,而用對象適配器。
總 結(jié):
(1) 適配器是為了解決:想重用已有代碼,但這些代碼跟當(dāng)前環(huán)境接口不兼容的情況;
(2) 適配器有類適配器和對象適配器,類適配器是通過繼承要重用的已有類,并實(shí)現(xiàn)新環(huán)境期望的接口來實(shí)現(xiàn)的;而對象適配器是通過組合對象的方式來實(shí)現(xiàn)的,把要重用的類實(shí)例組裝在適配器中作為適配器的字段,然后適配器再去實(shí)現(xiàn)新環(huán)境期望的接口。
(3) 不推薦使用類適配器(但不代表不能用),而推薦使用對象適配器。
轉(zhuǎn)載于:https://www.cnblogs.com/mouhong-lin/archive/2008/03/21/1117099.html
總結(jié)
以上是生活随笔為你收集整理的Design Patterns之Adapter Pattern总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 开机explorer无法启动,无法进入桌
- 下一篇: 微软概述 Windows Server