天天看點

溫故而知新:設計模式之擴充卡模式(Adapter)

借用terrylee的原話:

Adapter模式主要應用于“希望複用一些現存的類,但是接口又與複用環境要求不一緻的情況”,在遺留代碼複用、類庫遷移等方面非常有用。

擴充卡模式再次展現了“面向接口程式設計,而非面向實作程式設計”這一精神。

場景:

有一個基于資料庫的系統,裡面的資料庫操作就拿最常用的查詢來說,主要是用SqlHelper類裡的QueryData(string sql)這個方法來處理的,後來意外發現該方法實作上性能并不是最好(或者不能滿足新的需要),而這時正好有一個第三方的DbHelper程式集,寫得很成熟性能也不錯,但唯一不足的是裡面的查詢方法簽名是SelectData(string sql),怎麼辦?所有引用SqlHelper的地方全部修改,重頭編譯麼?No,沒人會想這樣

先看下原來的代碼:

using System;

using System.Data;

namespace Adapter

{

    class Program

    {

        static void Main(string[] args)

        {

            IDBHelper dbhelper = new SqlHelper();

            dbhelper.QueryData("Select * from TableA");

            Console.ReadKey();

        }

    }

    public interface IDBHelper 

        DataSet QueryData(string sql);

    public class SqlHelper : IDBHelper 

        public DataSet QueryData(string sql) 

            Console.WriteLine("QueryData is Called,the sql is :\"{0}\"",sql);

            return new DataSet();//這裡示範起見,就直接傳回一個DataSet執行個體完事 :)

}

如何在盡量不影響原有用戶端代碼的情況下,用新的DbHelper來取代舊的SqlHelper呢?

假如第三方的DBHelper結構如下:

/// <summary>

    /// 第三方的新dbHelper,實際場景中,這個類通常都是封裝在程式集中以dll提供,用戶端程式無法修改

    /// </summary>

    public class DbHelper 

        public DataSet SelectData(string sql)

            Console.WriteLine("SelectData is Called,the sql is :\"{0}\"", sql);

可以新增一個擴充卡:

    /// <summary>

    /// 新增的擴充卡

    public class DBHelperAdapter : IDBHelper 

        private DbHelper _dbHelper;

        public DBHelperAdapter(DbHelper dbHelper) 

            this._dbHelper = dbHelper;

        public DataSet QueryData(string sql)         

            return _dbHelper.SelectData(sql);

這樣原有的用戶端程式,隻需要把

IDBHelper dbhelper = new SqlHelper();

改成:

IDBHelper dbhelper = new DBHelperAdapter(new DbHelper()); 就萬事大吉了,當然你可以用配置檔案+反射,完全解耦,此處略過

反思:

本例中之是以能輕易将新的類替換舊的類,主要得益于舊的代碼僅依賴于抽象(即接口IDBHelper),而非具體的實作(即類SqlHelper),否則也不可能達到最終效果。

OO原則中的"面對接口編碼","依賴倒置"的妙處也就在于此。

最後給出類圖:

溫故而知新:設計模式之擴充卡模式(Adapter)