『C/C++』一個用C/C++分別實現介面與實現相分離的設計原則的例子

來源:互聯網
上載者:User

原文連結:一個用C/C++分別實現介面與實現相分離的設計原則的例子

良好的設計應該只暴露介面給使用者,所有的實現細節對使用者來說應該是隱藏的,也就是說使用者只要給介面傳遞相應的參數就行了,不需要管內部是如何?的,比如我們使用fopen,fseek,CreateWindow等函數會發現很好用,而不需要管fopen,fseek,CreateWindow函數內部代碼是如何?的,資料結構是如何組織的,也就是說絕對不能暴露任何的細節給使用者,包括資料群組織在內。

我現在用C和C++舉一個例子,來說說C/C++分別是如何?的,然後來看看哪種實現更好。

先來看C++用類實現的封裝:

--------------------------- interface1.h ---------------------------

#ifndef INTERFACE1_H#define INTERFACE1_Hclass DATA{private:    int _i;    short _j;public:    DATA();    ~DATA();    void set(int i, short j);    void get(int* i, short* j);    };#endif

--------------------------- interface1.cpp ---------------------------

#include "interface1.h"DATA::DATA(){    _i = _j = 0;}DATA::~DATA(){    _i = _j = 0;}void DATA::set(int i, short j){    _i = i;    _j = j;}void DATA::get(int* i, short* j){    *i = _i;    *j = _j;}

--------------------------- test.cpp ---------------------------

#include <stdio.h>#include "interface1.h"int main(){    DATA data;    int i;    short j;    data.set(2, 3);    data.get(&i, &j);                printf("i = %d, j = %d\n", i, j);        return 0;}

再來看 C 如何巧妙的封裝以及隱藏實現細節:

---------------------------  interface.h  ---------------------------

#ifndef INTERFACE_H#define INTERFACE_Hvoid* data_create();void data_set(void* dummy, int i, short j);void data_get(void* dummy, int* i, short * j);void data_destroy(void* dummy);#endif

---------------------------  interface.c  ---------------------------

#include <stdlib.h>struct DATA{    int i;    short j;};void* data_create(){    return malloc(sizeof(struct DATA));}void data_set(void* dummy, int i, short j){    struct DATA* data = dummy;    data->i = i;    data->j = j;}void data_get(void* dummy, int* i, short * j){    struct DATA* data = dummy;    *i = data->i;    *j = data->j;}void data_destroy(void* dummy){       free(dummy);}

---------------------------  test.c  ---------------------------

#include <stdio.h>#include "interface.h"int main(){    int i;    short j;    void* data = data_create();    data_set(data, 2, 3);    data_get(data, &i, &j);        printf("i = %d, j = %d\n", i, j);    data_destroy(data);    return 0;}

可以看的出來,C的實現只暴露了介面給使用者,內部的實現細節都隱藏了起來,可是C++用類實現反而在標頭檔暴露了實現細節。
當然用C++也可以做到只暴露介面給使用者,不過實現起來會比較複雜,而且需要消耗更多的記憶體(使用了虛函數)。

-------------------------------------- parent.h --------------------------------------

#ifndef PARENT_H#define PARENT_Hclass PARENT{public:    virtual void set(int i, short j) = 0;     virtual void get(int* i, short* j) = 0;};PARENT* get_child();#endif

-------------------------------------- parent.cpp --------------------------------------

#include "parent.h"#include "child.h"PARENT* get_child(){    return new CHILD;}

-------------------------------------- child.h --------------------------------------

#ifndef CHILD_H#define CHILD_H#include "parent.h"class CHILD : public PARENT{private:    int _i;    short _j;public:    CHILD();    ~CHILD();    void set(int i, short j);    void get(int* i, short* j);    };#endif

-------------------------------------- child.cpp --------------------------------------

#include "child.h"CHILD::CHILD(){    _i = _j = 0;}CHILD::~CHILD(){    _i = _j = 0;}void CHILD::set(int i, short j){    _i = i;    _j = j;}void CHILD::get(int* i, short* j){    *i = _i;    *j = _j;}

-------------------------------------- test.cpp --------------------------------------

#include <stdio.h>#include "parent.h"int main(){        int i;    short j;    PARENT* parent = get_child();    parent->set(2, 3);    parent->get(&i, &j);                printf("i = %d, j = %d\n", i, j);        return 0;} 

另外:

關注這個文章:http://bbs.csdn.net/topics/310212385,摘錄幾句,當思考下:


我們知道,類有介面(成員函數),有資料、狀態變數等,這些都有實現代碼。由於介面是要向外公開的,而實現是需要隱藏的(使用者不需要知道),這樣才能應對變化。比如介面的實現有變化,或者一個介面有多種可能的實現,我們就可以隨意修改這些實現,而不影響使用者的使用,因為使用者看到的只是對外公開的介面,介面並沒有變。

   將介面與實現分離的技術:

   (1)Interface class:將介面部分實現為abstract base class,在C++中,這個抽象類別中含一個virtual解構函式和一組pure virtual函數,實現部分則由派生出來的各個子類來完成。C++/Java/C#中都有現成的abstract類機制,例子就不需要舉了。

   (2)Handle class:也稱為pimpl技術,就是把隸屬對象的資料(即實現)從原對象中抽離出來,封裝成一個獨立的impl對象,在原對象中用一個指標成員指向它,一般使用智能指標。舉一個例子,按鈕組件通常都有一個背景映像作為資料,通常這個映像作為組件類的一個成員,為了分離實現,我們把這些資料抽離出來進行獨立的封裝:

struct PMImpl{ //封裝資料對象的    std::tr1::shared_ptr<Image> bgImage; //指向背景映像的智能指標    int imageChanges;  //背景映像更改次數}; class Button{ //按鈕類private:    Mutex mutex; //互斥鎖    std::tr::shared_ptr<PMImpl> pImpl; //指向資料對象的智能指標public:    void changeBackground(std::istream &imgSrc);    //...}; void Button::changeBackground(std::istream &imgSrc){    using std::swap;  //使用這個函數進行異常安全編程    Lock m1(&mutex); //加鎖,下面成為臨界區    std::tr1::shared_ptr<PMImpl> pNew(new PMImpl(*pImpl)); //建立臨時的pNew,                                              //並指向了原有的資料對象(含有背景映像)    pNew->gbImage.reset(new Image(imgSrc)); //根據傳進來的映像,更改背景映像    ++pNew->imageChanges;  //更改次數加1    swap(pImpl,pNew); //把更改後的資料交換到pImpl中,並且會釋放互斥鎖mutex,                      //從而完成了Button背景映像的更改}


介面主要是用於模組間的互動,實現就是具體的處理邏輯;如果介面過多的牽扯處理邏輯,代碼的重用性會比較差;如果介面和實現分開,新增功能只需要關注具體的實現,不需要改動介面。

公有介面是類的抽象組件,類函數定義是實現細節,把兩者分離,具體做法就是類成員函數定義與公有介面分屬不同的代碼檔案。

介面與實現的分離其實你一直在使用,最直接的例子就是標準庫,平時你使用標準庫的標頭檔,這個就是個介面,而標準庫被封裝在不同的地方,例如靜態庫,這個就是實現。兩者是分離的。


介面和實現分離,模組間的依賴會自然轉化為對介面資料類型的依賴

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.