2018年1月9日 星期二

什麼是物件導向,的差別在哪裡

什麼是物件導向,差別在哪裡

用一句話來形容就是
資料與方法的群組化再群組化
如果說編程是解放 繁瑣與重複的工作,那麼物件導向就是解放編程的繁瑣與重複的工作
下面讓我用簡單的群組概念來解釋如何解放繁瑣的工作,當然~實際上物件導向不只如此,舉例只是這個海灘的中的其中一粒沙子,但是這已經有足夠強大的功能與便利性了。
讓我們看看一個單向鍊結的實例,下面是一個用C語言實做的例子
函式
// 資料結構
typedef struct node Node;
struct node {
    int data;
    Node* next;
};
// 初始化節點
Node* link_init(int num) {
    Node* n = (Node*)malloc(sizeof(Node));;
    n->data = num;
    n->next = NULL;
    return n;
}
// 尾端新增節點
Node* link_append(Node* node, int num) {
    node->next = link_init(num);
    return node->next;
}
// 插入節點
void link_insert(Node* node, size_t idx, int data){
    // 旗標(插入點的前一個)
    for(unsigned i = 0; i < idx; ++i) {
        node = node->next;
    }
    Node* temp = node->next;
    node->next = link_init(data);
    node->next->next = temp;
}
操作範例
    // 新增鏈結
    Node* head = link_init(-1);
    Node* tail = head;

    // 新增節點
    tail = link_append(tail, 1);
    tail = link_append(tail, 2);
    tail = link_append(tail, 3);
    // 插入節點
    link_insert(head, 1, 0);
    // 查看節點
    for(Node* n = head->next; n; n=n->next) {
        printf("%d, ", n->data);
    } printf("\n");

    // 刪除鏈結
    link_delete(head);
    head=NULL;
    return 0;
在這個例子裏面可以看到有三件繁瑣的事情
  1. 每一個函式呼叫都需要引入 Node*
  2. 為了方便識別函式是對屬於誰的,名稱帶前缀名
  3. 結束之後自己要記得 delete
由於這是一個簡單的範例所有只有幾個函式看起並不多餘,如果這個鏈結陣列裝的東西不只是int可能會需要很多個函式來操作處理,這時候每一個函式參數都要帶一個 Node* 就顯得有些多餘。
那物件導向是怎麼優化這件事情的呢,讓我們來看一下物件導向的寫法。
函式
struct Node_class{
    // 資料結構
    struct Node {
        int data;
        Node* next;
    } *node, *tail;

    // 初始化
    Node_class(){
        node = init(-1);
        tail = node;
    }
    // 自動刪除鏈結
    ~Node_class(){
        del();
    }

    // 初始化節點
    static Node* init(int num) {
        Node* node = (Node*)malloc(sizeof(Node));;
        node->data = num;
        node->next = NULL;
        return node;
    }
    // 尾端新增節點
    void append(int num) {
        tail->next = init(num);
        tail = tail->next;
    }
    // 插入節點
    void insert(size_t idx, int data){
        // 旗標(插入點的前一個)
        Node* node = this->node;
        for(unsigned i = 0; i < idx; ++i) {
            node = node->next;
        }
        Node* temp = node->next;
        node->next = init(data);
        node->next->next = temp;
    }
    // 刪除鏈結
    void del(){
        for(Node* temp=node; temp; temp=node) {
            node = temp->next;
            free(temp);
        }
    }
    // 印出節點
    void print(){
        for(Node* n = node->next; n; n=n->next) {
            printf("%d, ", n->data);
        } printf("\n");
    }
};
操作方法
    // 新增鏈結
    Node_class list;

    // 新增節點
    list.append(1);
    list.append(2);
    list.append(3);
    // 插入節點
    list.insert(1, 0);
    // 查看節點
    list.print();

    // 刪除鏈結
    // list.~Node_class();
再來回頭看那三點
  1. 每一個函式呼叫都需要引入 Node*
    由於方法與資料的群組結構,每一個物件都帶有各自的資料,而這個物件可以呼叫這些方法,用的就是內帶的資料,每個物件帶有不同的資料。
  2. 為了方便識別函式是對屬於誰的,名稱帶前缀名
    物件的名字取代了函式的前缀名,這個物件是誰建立的就傭有哪些方法,如果不是則無法使用,我們再也不需要為了同名而困擾了。
  3. 結束之後自己要記得 delete
    之所以將她註解掉是因為並沒有影響,每一個物件再生命週期結束的時候都會自動呼叫一次解構子,也就是帶有~符號的類別名稱方法。

沒有留言:

張貼留言