設計模式--補充面向對象基礎

                                                    

 

這一篇給大家補充一些學習設計模式的基礎知識,比如設計模式的哲學思想,c語言如何實現面向對象特性等等。是進一步學習各種設計模式招式的內功。

設計模式的感悟

中國數千年的歷史文明,留給後人數之不盡的智慧。自從事編程工作以來,已經三年多有餘,慢慢代碼敲多了,竟然在代碼中發現了一些先哲們偉大思想的蛛絲馬跡。

道德經中有這麼一段:“有物混成,先天地生。寂兮寥兮,獨立而不改,周行而不殆,可以爲天地母。吾不知其名,字之曰道,強爲之名曰大。大曰逝,逝曰遠,遠曰反。”

每個人對道有不同的理解,在我看來,如老子說的一般,道可道,非常道。說不明白,解釋不盡的纔是道。有那麼一點只可身教、不可言傳的味道。它是各種自然界天然法則的本質抽象,至少在某一方面跟我們揭示了各種事物如何組織在一起以及進行溝通互動的原始規律。

設計模式就是這麼一種道。它指導着開發者在編碼過程中,各個對象怎麼組織聯繫成爲一個優雅健壯的整體,如何使對象相互間即獨立又相互協作。一方面,保證了良好的可維護項和可拓展性,另一方面,共同穩定可靠地完成某種功能。

上面說了,設計模式是面向對象的道,那我們必需先了解對象是什麼?它有什麼特性?c語言如何實現面向對象?

對象的特性

幾乎所有的書本資料都會告訴你,c語言是面向過程的語言,C++是一門面向對象的語言。而面向對象有三大特性:封裝、繼承、多態

封裝

圖片中的ATM相信小百姓都不陌生,那大家有沒有想過爲什麼ATM要把money都存放到內部的保險櫃中,只留一個出鈔口可以獲取到money?哈哈,這個問題有點弱智,其實就是爲了保護money不被人非法獲取,只有符合身份信息驗證的人才能從裏面提取money。

其實這個就是封裝,封裝隱藏了裏面的所有money,你沒有辦法直接地知道里面money的細節,比如這些money是第幾版的紙幣?它的新舊程度怎麼樣?要獲取這些細節就只能通過唯一的出鈔口來獲取。

繼承

恩?這是...國民老公?對,雖然現在他名下資產被法院凍結,但遙想思聰當年,娛樂圈紀委、百分百勝率ADC、喫着熱狗帶領IG捧回當年S賽冠軍,是何等風光。思聰C道出位離不開對其父親--首富王健林的幫助。這其實就是一種"繼承".

換在程序開發的話,你可以試想,如果你花了大力氣開發了一套國家圖書館管理軟件,那麼在以後可能遇到其他省市級圖書館管理軟件需要開發的時候,是不是希望把自己以前開發國家圖書館管理軟件的代碼也派上用場呢?

多態

我們可以看到圖片中各種各樣的球類,雖然它們都是圓滾滾的,也都富有彈性,但是它們的用途各不一樣。這說明對象都擁有自己的一些行爲特點,我們把對象的這些行爲特點稱爲"多態"。

總結一下面向對象的特性

  • 封裝,隱藏內部細節

  • 繼承,複用現有代碼

  • 多態,改寫對象行爲

c語言面向對象的藝術

先來看封裝。

所謂封裝,通俗地說,就是一個姑娘化了妝,只給你看她想讓你看的那一面,至於裏面是否颳了骨、墊了東西,不給你看。說到封裝就得說隱藏,這是對兄弟概念;其實我理解隱藏是更深的封裝,完全不給你看見,而封裝可能是猶抱琵琶半遮面。封裝在 C++ 語言中有 protected 、 private 關鍵字在語言層面上支持,而 C 語言中沒有這些。 C 有結構體( struct ),其實可以實現封裝和隱藏。

struct A_private;  
struct A {  
    int a;  
    ...  
    void (*func)(struct A*);  

    struct A_private * priv;  
};

上面的代碼,我們只前向聲明結構體 struct A_private ,沒人知道它裏面具體是什麼東西。假如 struct A 對應的頭文件是 A.h ,那麼把 A_private 的聲明放在 A_p.h 中,A.c 文件包含 A_p.h ,那麼在實現 struct A 的函數指針 func 時如何使用 struct A_private ,客戶程序員根本無須知道。

這樣做的好處是顯而易見的,除了預定義好的接口,客戶程序員完全不需要知道實現細節,即便實現經過重構完全重來,客戶程序員也不需要關注,甚至相應的模塊連重新編譯都不要——因爲 A.h 自始至終都沒變過。

另外對於某些方法,僅僅是在對象內部使用,它們將採用static修辭把作用範圍侷限在一個文件的內部:

到現在爲止,封裝和隱藏就實現了,而且很徹底。

再看繼承。

首先C語言中的void指針是非常強大的,不僅可以指向變量而且還可以泛指向任何數據,例如不同類型的變量,函數等。不同變量間的指針只需要強制轉換類型就可實現不同類型數據的訪問。例如如下代碼:

struct parent class
{
int a, b;
char *str;
};
struct child class
{
struct parent class p;
int a, b;
};
void func()
{
struct child class obj, *obj ptr;
struct parent class *parent ptr;
obj ptr = &obj;
/* 獲得父指針 */
parent ptr = (struct parent*) &obj;
parent ptr->a = 1;
parent ptr->b = 5;
obj ptr->a = 10;
obj ptr->b = 100;
}

在上面代碼中,注意subobject結構中第一個成員p,這種聲明方式代表subobject類型的數據中開始的位置包含一個parent類型的變量。在函數func中obj是一個subobject對象,正向這個結構類型指示的,它前面的數據應該包含一個parent類型的數據。在第行的強制類型賦值中parent ptr指向了obj變量的首地址,實際上也就是obj變量中的p對象。好了,現在parent ptr指向的是一個真真實實的parent類型的結構,那麼可以按照parent的方式訪問其中的成員,當然也包括可以使用和parent結構相關的函數來處理內部數據,因爲一個正常的,正確的代碼,它是不會越界訪問parent結構體以外的數據的。經過這基本的結構體層層相套包含,對象簡單的繼存關係就體現出來了:父對象放於數據塊的最前方,代碼中可以通過強制類型轉換獲的父對象指針。

最後來看多態。

這裏只講解多態最基本的用法,就是實現改寫對象的行爲。虛函數與重載涉及比較複雜的底層機制,筆者暫時沒有研究清楚。

多態,就是說用同一的接口代碼處理不同的數據。比如說,下面的base_class結構就是一個通用的數據結構,我們也不清楚a是什麼數據,vfunc是什麼處理函數?但是,我們處理的時候只要調用base_class->vfunc(int a)就可以了。剩下來的事情我們不需要管,因爲不同的接口會有不同的函數去處理,我們只要學會調用就可以了。

struct base_class
{
int a;
void (*vfunc)(int a);
}
void base_class_vfunc(struct base_class *self, int a)
{
assert(self != NULL);
assert(slef->vfunc != NULL);
self->vfunc(a);
}
struct child_class
{
struct base_class parent;
int b;
};
void child_class_init(struct child_class* self)
{
struct base_class* parent;
parent = (struct_base_class*) self;
assert(parent != NULL);
parent->vfunc = child_class_vfunc;
}
static void child_class_vfunc(struct child_class*self, int a)
{
self->b = a + 10;
}

學完這一節打好設計模式的基礎了,歡迎繼續查看其它c語言設計模式系列文章。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章