天天看點

農民伯伯 談 接口 [interface]

前言

     相信大家對接口是不陌生的,但是你真的了解什麼是接口嗎?真的能用好嗎?我們口口聲聲說按接口程式設計,到底如何接口程式設計呢?接口程式設計的意義在哪呢?...對于接口的迷茫,經常在三層結構裡面看到的,千篇一律的把每一個dao都寫一個接口,每個service再寫一個接口,因為他們看的例子就是這樣的,網上很多例子都是這樣的,這就叫按接口程式設計了?!心裡沒底,到下次自己寫項目自己設計的時候再加上趕進度怕是沒這麼勤奮的複制粘貼了,原因還是沒有明白接口到底有什麼用!甚着感覺接口這玩意就像脫褲子放屁——多此一舉!真的是這樣麼?那麼,接下來我和大家一起來探讨關于接口的種種...

正文

     一、什麼是接口、接口有什麼用

          我們先看看别人是怎麼說接口的,我收集總結了一下,僅列出以下五種說法:

          1.     接口的意義在于頂替多重繼承。

          2.     接口的作用,一言以蔽之,就是标志類的類别(type of class)。把不同類型的類歸于不同的接口,可以更好的管理他們。

          3.     接口簡單了解就是一種約定,使得實作接口的類或結構在形式上保持一緻,使用接口可以使程式更加清晰和條理化。

          4.     接口就是定義了一個合同,實作這個接口的類都保證自己符合這個合同要求。

          5.     接口從更深層次的了解,應是定義(規範,限制)與實作(名實分離的原則)的分離。

          這裡我就不評價這幾種說法了,下面說說我了解的接口是什麼樣子的。這裡拿電腦裡的主機闆來講,主機闆上有usb總線接口、基本外設接口(用來連接配接鍵盤、滑鼠、列印機等傳統外設)、驅動器接口(用來連接配接硬碟驅動器、CD光牒驅動器和軟碟驅動器等)...很眼熟吧!!這些東西都是帶接口兩字的,我們稱之為硬體接口或接口類型,在翻閱關于這些硬體借口資料的時候你經常會發現這些接口都是由許多有名的公司如microsoft、ibm、intel、apple等公司共同約定、開發的一種标準!!例如:usb是compaq、dec、ibm、intel、microsoft、nec(日本)、nothern telecom(加拿大)等7家公司與1994年11月聯合開發的計算機串行接口總線标準;ieee 1394是1986年由apple公司和ti(德克薩克儀器)公司開發的高速串行接口标準,命名為“火線”(fire wire)等。那麼為什麼要制定這些标準呢?包括現在都在争的3g标準,更有專門的标準組織和标準委員會。全世界硬體廠商多不勝數,随便列幾個:

          cpu:     intel、amd

          記憶體:     金士頓、黑金剛、宇瞻

          硬碟:     日立、希捷

          顯示器:     飛利浦、三星、lg、明基、優派等,這麼多廠商,這麼多品牌,我們沒有因為把飛利浦的顯示器換成三星的電腦就不能用了,任意換硬碟、換記憶體,加顯示卡,接不同牌子的滑鼠,用不同牌子的鍵盤,為什麼沒有問題?關鍵就在這裡了——他們都遵循了标準,這些硬體都是按标準生産出來的!!是以我們用盜版的硬體(如 滑鼠)也可以很爽,因為盜版他也遵循了标準!!可以說沒有這些硬體标準——個人電腦也不能像今天如此普及!!現在我們再回過頭來看接口,請告訴我你有什麼感覺?我的感覺就是接口就是标準,或者稱之為标準接口!!在硬體裡面是,軟體裡面也是同樣如此。好處是顯而易見的,下面我們将上面的硬體接口“轉換”成下面的軟體接口的代碼:

農民伯伯 談 接口 [interface]

    #region cpu接口

    public interface cpu接口 { }

    public interface 針腳式 : cpu接口 { }

    public interface 卡式 : cpu接口 { }

    public interface 觸點式 : cpu接口 { }

    public interface socket478 : 針腳式 { }

    public interface socket754 : 針腳式 { }

    public interface socket940 : 針腳式 { }

    #endregion

    #region 記憶體接口

    public interface 記憶體接口 { }

    public interface i144pin : 記憶體接口 { }

    public interface i168pin : 記憶體接口 { }

    public interface i240pin : 記憶體接口 { }

    #region 硬碟接口

    public interface 硬碟接口 { }

    public interface ide : 硬碟接口 { }

    public interface scsi : 硬碟接口 { }

    public interface sata : 硬碟接口 { }

    public class 精英a780gm

    {

        /// <summary>

        /// 構造一塊主機闆

        /// </summary>

        /// <param name="cpu">socket am2/am2+</param>

        /// <param name="hd">sata接口</param>

        /// <param name="ddr">ddr2</param>

        public 精英a780gm(socket940 cpu, sata hd, i240pin ddr)

        {

        }

    }

農民伯伯 談 接口 [interface]

     這款精英a78gm主機闆是我随便從中關村線上裡面找的一塊闆子,而上面的接口就是許多廠商坐在一起約定出來的标準接口,當然這裡隻是例舉了主機闆的部分元件,但是可以看到,主機闆廠商都是按标準來進行制造的,他們生産不擔心你插什麼樣的牌子cpu、硬碟、記憶體到主機闆上,隻要你符合這個标準接口就行!!需要說明的是,上面五種對于接口的說法都是有一定道理的,而這裡,我認為接口可以是标準,接口的意義更大展現在制定标準上面!!

二、如何使用标準

     1.     标準接口

          在上面的例子中,制定标準展現出良好的相容性,有效降低了組合成本,更促進廠商按照标準專注本身等,下面我們再從軟體程式設計中找更加貼切的例子來說明這一點。在跨資料庫或資料庫切換的時候我們可以用标準的接口來限制和規範資料庫操作,以達到無縫切換(實際中可能有部分需要特殊處理)和跨資料庫應用。下面給出一段無縫切換資料庫的例子:

農民伯伯 談 接口 [interface]

    public interface idal

        /// 根據主鍵删除資料

        /// <param name="pk">主鍵</param>

        void delete(string pk);

    public class sqldal : idal

        #region idal 成員

        public void delete(string pk)

            sqlhelper.executenonquery(string.concat("delete table [test] where id = ", pk));

        #endregion

    public class oracledal : idal

            oraclehelper.executenonquery(string.concat("delete table [test] where id = ", pk));

    public class mysqldal : idal

            mysqlhelper.executenonquery(string.concat("delete table [test] where id = ", pk));

    public class business

        #region 變量

        private static type daltype;

        private idal dal;

        #region 構造函數

        public business()

            dal = activator.createinstance(daltype) as idal;

        static business()

            //web.config:     <add key="databasetype" value="sqlserver" />

            string databasetype = configurationmanager.appsettings["databasetype"];

            if (!string.isnullorempty(databasetype))

            {

                switch (databasetype.tolower())

                {

                    case "sqlserver":

                        daltype = typeof(sqldal);

                        break;

                    case "mysql":

                        daltype = typeof(mysqldal);

                    case "oracle":

                        daltype = typeof(oracledal);

                    default:

                }

            }

            else

                daltype = typeof(sqldal);

        /// 删除一筆資料

        /// <param name="id"></param>

        public void remove(string id)

            dal.delete(id);

農民伯伯 談 接口 [interface]

          注意:這段代碼不考慮sql語句安全、效率等問題,關鍵是展現接口的作用。

          說明:好處是顯而易見的,可維護性高,這段業務代碼在切換資料庫時是不需要更改任何代碼的,隻需輕松的把web.config的databasetype指定為其他的資料庫類型就行了。這得益于sqldal、mysqldal、oracledal都是按标準的方式來實作的資料庫操作的。便于分工,我們可以把這三個類分别交給三個人精通各自資料庫的人來編寫,這樣同時也将業務層和資料層解耦了,隻要标準一出,資料層和業務層的員工就可以同時開始編寫代碼,業務層員工隻管按标準調用,而資料層員工隻管按标準來編寫。

          有些朋友可能說,既然接口主要用于标準,我小型項目就沒必要弄這麼複雜了,但是我的朋友,你應該知道複用這回事吧,也就是說我做完這個項目我還得做下一個項目。如果我能夠把這個項目的資料層直接移植到下個項目該多好,即使資料庫改變也沒有關系,假如你花心思設計好了我相信這不會很困難:)

     2.     參數傳遞

          對于接口的用途我最早是在java裡面用于參數傳遞的,java和c#都是強類型語言,也就是你傳一個參數過來的時候需要明确指定一個類型。但是有一個類型非常特别,那就是如果我将參數的類型指定為object的時候,你不管傳什麼參數都可以,因為所有類型都繼承自object!而将接口用于參數傳遞實作方式同object是一樣的,隻要你繼承了你就可以被傳輸,是以大家經常能看到空的接口。接下來也會貼java下使用hibernate的一個例子 ,也是我第一次認識到接口作用的例子:

               daobase.java

農民伯伯 談 接口 [interface]

public class daobase extends hibernatedaosupport {

    public boolean add(imodel model) throws myexception {

        try {

            this.gethibernatetemplate().save(model);

            return true;

        } catch (exception e) {

            throw new myexception(e);

    public boolean modified(imodel model)throws myexception {

            this.gethibernatetemplate().update(model);

}

農民伯伯 談 接口 [interface]

               imodel.java

public interface imodel extends java.io.serializable {

               account.java

農民伯伯 談 接口 [interface]

public class account implements imodel {

    // fields    

     private integer id;

     private string password;

    // constructors

    /** default constructor */

    public account() {

    // property accessors

    public integer getid() {

        return this.id;

    public void setid(integer id) {

        this.id = id;

    public string getpassword() {

        return this.password;

    public void setpassword(string password) {

        this.password = password;

農民伯伯 談 接口 [interface]

          說明:daobase中hibernatedaosupport在這裡就不介紹了,主要是this.gethibernatetemplate()的兩個方法save和update,這兩個方法的所需參數均是object,以前的做法就是每一個表寫一個dao,每個dao裡面寫一個add方法,然後參數為特定model或者說是vo,極其繁瑣,經過這樣改裝後就可以有一個通用的dao了,也減少了許多代碼量,而且比起直接用object參數更加安全,因為它幫助save和update明确指定了隻有繼承了這個接口的vo才能傳遞進來!

     3.     其他用法

          在繼承ihttphandler實作自己handler的時候,如果我們需要用到session就需要繼承接口irequiressessionstate或ireadonlysessionstate,需要注意的是這兩個都是空接口,不知道大家有沒有問個為什麼!!接下來我和大家一起分析這一用法,在asp.net中使用ajaxpro時,有一步就是将方法标記ajaxmethod,如果需要在方法中使用session需要如下标記:

          [ajaxpro.ajaxmethod(ajaxpro.httpsessionstaterequirement.read)]

          [ajaxpro.ajaxmethod(ajaxpro.httpsessionstaterequirement.readwrite)]

          首先從web.config入手,在http節點下可以看到這段代碼:

<add verb="post,get" path="ajaxpro/*.ashx" type="ajaxpro.ajaxhandlerfactory, ajaxpro.2"/>

          ok!從ajaxhandlerfactory入手,拿出reflector,可以得到以下關鍵代碼:

農民伯伯 談 接口 [interface]

ajaxmethodattribute[] customattributes = (ajaxmethodattribute[]) processorarray[i].method.getcustomattributes(typeof(ajaxmethodattribute), true);

if (customattributes.length > 0)

{

    if (customattributes[0].requiresessionstate == httpsessionstaterequirement.read)

          if (!customattributes[0].useasyncprocessing)

           {

                 return new ajaxsynchttphandlersessionreadonly(processorarray[i]);

           }

           return new ajaxasynchttphandlersessionreadonly(processorarray[i]);

     }

     if (customattributes[0].requiresessionstate == httpsessionstaterequirement.readwrite)

     {

          {

                return new ajaxsynchttphandlersession(processorarray[i]);

          }

          return new ajaxasynchttphandlersession(processorarray[i]);

      }

 }

農民伯伯 談 接口 [interface]

           注意紅色代碼部分,然後關注類ajaxsynchttphandlersessionreadonly和ajaxsynchttphandlersession,我們看這兩個類是幹嘛的:

農民伯伯 談 接口 [interface]

    public class ajaxsynchttphandlersessionreadonly : ajaxsynchttphandler, ireadonlysessionstate, irequiressessionstate

        public ajaxsynchttphandlersessionreadonly(iajaxprocessor p) : base(p)

    public class ajaxsynchttphandlersession : ajaxsynchttphandler, irequiressessionstate

        public ajaxsynchttphandlersession(iajaxprocessor p) : base(p)

農民伯伯 談 接口 [interface]

      看到沒有?!他分别繼承了這兩個接口,而普通的隻是實作了ihttphander接口,也就是能不能使用session關鍵就在到底有沒有繼承這兩個接口之一。但是我們使用session仍然是從httpcontext.current.session取得,沒有因為繼沒繼承那兩個接口之一而改變,是以問題應該在httpcontext裡,我們把httpcontext也reflectors出來,直接搜尋這兩個接口,果然大有斬獲,能看到如下代碼:

農民伯伯 談 接口 [interface]

        public ihttphandler handler

            get

                return this._handler;

            set

                this._handler = value;

                this.requiressessionstate = false;

                this.readonlysessionstate = false;

                this.inaspcompatmode = false;

                if (this._handler != null)

                    if (this._handler is irequiressessionstate)

                    {

                        this.requiressessionstate = true;

                    }

                    if (this._handler is ireadonlysessionstate)

                        this.readonlysessionstate = true;

                    page page = this._handler as page;

                    if ((page != null) && page.isinaspcompatmode)

                        this.inaspcompatmode = true;

農民伯伯 談 接口 [interface]

          看到這裡雖然還沒有完全水落石出,但是基本原理應該是明白了的,有需要深入的朋友可以看看httpapplication和sessionstatemodule等相關類。

          講到這裡基本上告一段了,看到評論裡面仍然有人用肯定的詞語“是”、“就是”來評論接口,我覺得是不恰當的,我用的是“可以”二字,因為我覺得接口可能還有其他作用,不僅僅隻是限制和規範或者說是标準。當接口不為空的時候,我覺得接口可以說是标準或限制,因為你繼承了就必須實作接口裡的東西,如方法;但是接口為空的時候請問你,你限制什麼?第二種用法可以說得過去,限制了參數,但是第三種呢?有限制嗎?我覺得就是純粹的身份、标示或者了解為類似于aop的功能,這對于我們不直接用new來擷取對象執行個體的時候,比如用工廠來生成對象、通過其他對象生成,簡稱間接生成的時候使用是大有益處的,接口這個時候也作為一種手段來達到我的目的,而且很好用!!(最後修改 2009-2-6)

     後期維護

           2012-1-6  今天看practical java,發現java裡有一種叫标示接口(marker interface):即不含任何函數的一種接口,用來表示實作此接口的任何類一定具有某種屬性(性質,propterty)。

結束

     寫的時候苦于找不到合适的例子來說服自己,一直努力的闡述關于接口的所見所聞和所想,希望能帶給你多一份關于接口的收獲,熱烈歡迎交流心得!!

轉載:http://www.cnblogs.com/over140/archive/2009/02/05/1383493.html