--- title: C語言 物件導向程式設計篇 心得 tags: C 語言筆記 --- ## C語言 物件導向程式設計篇 心得 ### Data Encapsulation * 一種將抽象性函式介面的實作細節部份包裝、隱藏起來的方法。同時,它也是一種防止外界呼叫端,去存取物件內部實作細節的手段 * 在 C 裡可以利用 forward declaration 與 function pointer 來實作(4到11行) ```c= #include <stdio.h> #include <stdlib.h> /* forward declaration */ typedef struct object Object; typedef int (*func_t)(Object *); struct object { int a, b; func_t add, sub; }; static int add_impl(Object *self) { // method return self->a + self->b; } static int sub_impl(Object *self) { // method return self->a - self->b; } // & : address of // * : value of // indirect access int init_object(Object **self) { // call-by-value if (NULL == (*self = malloc(sizeof(Object)))) return -1; (*self)->a = 0; (*self)->b = 0; (*self)->add = add_impl; (*self)->sub = sub_impl; return 0; } int main(int argc, char *argv[]) { Object *o = NULL; init_object(&o); o->a = 9922; o->b = 5566; printf("add = %d, sub = %d\n", o->add(o), o->sub(o)); return 0; } ``` * 因為 Object *o = NULL,傳給 init(o) 後做到 malloc 時會發現只是在 NULL = malloc(...),所以等於沒做,所以才要用 o 的 address 來做 * 所以如果把 malloc 放到 main 做,就可以不用 pointer to pointer ### Inheritance and Polymorphism in C * [Inheritance-and-Polymorphism-in-C 連結](https://www.codeproject.com/Articles/108830/Inheritance-and-Polymorphism-in-C) * The Person class representation ![](https://i.imgur.com/05o1QEC.png) * Struture of the Employee object (Inheritance-and-Polymorphism) ![](https://i.imgur.com/wVXPNEd.png) ### Design Patterns in C * 情境 ![](https://i.imgur.com/QDMpU3w.png) * [程式碼](https://github.com/QMonkey/OOC-Design-Pattern/tree/master/Observer) * [ include/iobserver.h ]: 檔名開頭的 i 字母表示 interface ```c= typedef struct _IObserved IObserved; typedef struct _IObserver IObserver; struct _IObserved { /* 商品 */ void (*registerObserver)(IObserved *, IObserver *); void (*notifyObservers)(IObserved *); void (*removeObserver)(IObserved *, IObserver *); }; struct _IObserver { /* 顧客 */ void (*handle)(IObserver *); }; ``` * 接下來 include 裡的 observed.h 和 observer.h 分別實作介面 * [ include/observed.h ] ```c= typedef struct _Observed Observed; struct _Observed { IObserver **observers; size_t count; size_t size; union { IObserved; IObserved iobserved; }; }; extern Observed *Observed_construct(void *); extern void Observed_destruct(Observed *); ``` * [ include/observer.h ] ```c= #include "iobserver.h" typedef struct _Observer Observer; struct _Observer { union { IObserver; IObserver iobserver; }; }; extern Observer *Observer_construct(void *); extern void Observer_destruct(Observer *); ``` **問題: 為何 struct 的 anonymous union 要這樣寫?** * 之後才是在 src/observed.c 和 src/observer.c 實作函式 * 最後可以看到 main 裡的使用 * new、delete 是放在 base.h 裡的巨集 ```c= const int OBSERVER_SIZE = 10; int main() { Observed *observed = new (Observed); IObserved *iobserved = &observed->iobserved; Observer *observers[OBSERVER_SIZE]; for (int i = 0; i < OBSERVER_SIZE; ++i) { observers[i] = new (Observer); observed->registerObserver(iobserved, &observers[i]->iobserver); printf("handle: %p\n", &observers[i]->iobserver); } printf("\n"); iobserved->notifyObservers(iobserved); for (int i = 0; i < OBSERVER_SIZE; ++i) delete(Observer, observers[i]); delete(Observed, observed); return 0; } ``` ### Strategy Pattern * 為了達到相同的目的,物件可以因地制宜,讓行為擁有多種不同的實作方法。例如,一個壓縮檔案物件,可以採用 zip、arj、rar、tar、7z 等不同的演算法來執行壓縮工作。讓物件可以自由切換演算法或行為實作。 * 用 switch case 會讓開發、修改變難 * 可以為演算法定義一個 Strategy 介面,針對每一種演算法,新增一個實作 Strategy 介面的 ConcreteStrategy 物件。在執行期間藉由改變 Strategy 成員變數所指向的 ConcreteStrategy 物件,來切換不同的演算法演算法。 * 以下範例是定義一個叫 itravel_strategy.h 的 Strategy 介面 ```c= typedef struct _ITravelStrategy ITravelStrategy; struct _ITravelStrategy { void (*travel)(ITravelStrategy *); }; ``` * 之後可以在 main 中這樣使用,定義了搭飛機跟坐火車兩種旅行方法,person 要用哪種的 itravelStrategy 不需要變換 ```c= #include "base.h" #include "itravel_strategy.h" #include "airplane_strategy.h" #include "train_strategy.h" #include "person.h" int main() { TrainStrategy *trainStrategy = new (TrainStrategy); ITravelStrategy *itravelStrategy = &trainStrategy->itravelStrategy; Person* person = new (Person, itravelStrategy); person->travel(person); AirplaneStrategy *airplaneStrategy = new (AirplaneStrategy); itravelStrategy = &airplaneStrategy->itravelStrategy; person->setTravelStrategy(person, itravelStrategy); person->travel(person); delete (Person, person); delete (AirplaneStrategy, airplaneStrategy); delete (TrainStrategy, trainStrategy); return 0; } ```