Upgrade to Pro — share decks privately, control downloads, hide ads and more …

美しい設計の色香に惑わされないために - 手段としてのオブジェクト "C" 向

美しい設計の色香に惑わされないために - 手段としてのオブジェクト "C" 向

Shota Takizawa

February 16, 2020
Tweet

Other Decks in Programming

Transcript

  1. 27 テストのために DB 操作をまとめたい 課題 ◼ 責務の適切な分担 ◼ DB アクセスの抽象化

    とかとか必要ですね ゼロから実現方法を 模索するのは大変
  2. 28 テストのために DB 操作をまとめたい 課題 ◼ 責務の適切な分担 ◼ DB アクセスの抽象化

    とかとか必要ですね ゼロから実現方法を 模索するのは大変 オブジェクト指向を導入 実装できるルールに落とし込む
  3. カプセル化 中身はソースに書く #include "Request.h" Request *requestNew(int id) { Request *r

    = (Request*)malloc(sizeof(Request)); r->id = id; return r; } int requestGetId(Request *r) { return r->id; } Request.c 79
  4. カプセル化 使うとき #include <stdio.h> #include "Request.h" int main(void) { Request

    *r = requestNew(1); printf("%d¥n", requestGetId(r)); } main.c 80
  5. カプセル化 工夫すると部分的にprivateにできます struct RequestImpl; typedef struct Request { int id;

    struct RequestImpl *pImpl; } Request; Request.h 86 public private RequestImplの中身を.cに書く
  6. カプセル化 Pimpl : Pointer to implementation struct RequestImpl; typedef struct

    Request { int id; struct RequestImpl *pImpl; } Request; Request.h 87 public private
  7. struct Request { int id; int src; int dst; char

    payload[256]; … }; 継承 91 これを継承して RequestEx をつくる
  8. 継承 struct Request { int id; int src; int dst;

    char payload[256]; … }; 0x00f5 b2da fdc0 プログラムからみた アドレス ︙ 94
  9. 継承 struct Request { int id; int src; int dst;

    char payload[256]; … }; int id int src int dst char payload[256] 0x00f5 b2da fdc0 プログラムからみた アドレス ︙ 上から順に並ぶ 95
  10. 継承 struct Request { int id; int src; int dst;

    char payload[256]; … }; struct RequestEx { Request request; int type; } extends 親クラスをメンバの先頭に配置 97
  11. struct RequestEx { Request request; int type; } 継承 Request

    request int type 0x00f5 b2da fdc0 プログラムからみた アドレス ︙ 98
  12. struct RequestEx { Request request; int type; } 継承 Request

    request int type 0x00f5 b2da fdc0 プログラムからみた アドレス ︙ 99 ここだけ見れば親
  13. 継承 int id int src int dst char payload[256] int

    type 101 私は Request のポインタです 中身は int, int, int, char[256]
  14. 継承 子クラスのコンストラクタ #include "Request.h" RequestEx *requestExNew(int id, int type) {

    RequestEx *rex = (RequestEx *) malloc(sizeof(RequestEx)); rex->request.id = id; rex->type = type; return rex; } 104
  15. 継承 親クラスのコンストラクタを使いたい #include "Request.h" RequestEx *requestExNew(int id, int type) {

    RequestEx *rex = (RequestEx *) malloc(sizeof(RequestEx)); rex->request.id = id; rex->type = type; return rex; } 105
  16. 継承 メモリ確保を外に持っていく void requestNew(Request *r, int id) { … }

    void requestExNew(RequestEx *rex, int id, int type) { requestNew(&(rex->request), id); rex->type = type; } RequestEx *r = (RequestEx *)malloc(sizeof(RequestEx)); requestExNew(r, 1, 2); 107
  17. void requestNew(Request *r, int id) { … } void requestExNew(RequestEx

    *rex, int id, int type) { requestNew(&rex->request, id); rex->type = type; } RequestEx *r = (RequestEx *)malloc(sizeof(RequestEx)); requestExNew(r, 1, 2); 継承 メモリ確保を外に持っていく 108 併用不可
  18. void requestNew(Request *r, int id) { … } void requestExNew(RequestEx

    *rex, int id, int type) { requestNew(&rex->request, id); rex->type = type; } RequestEx *r = (RequestEx *)malloc(sizeof(RequestEx)); requestExNew(r, 1, 2); 継承 メモリ確保を外に持っていく 109 Pimpl を使う必要がある
  19. ポリモーフィズム 関数ポインタを持つ親を継承 struct Request { int id; … void (*setHeader)(char

    *); int (*send)(void); }; struct RequestEx { Request request; … }; 112 子のコンストラクタで関数ポインタを上書き
  20. ポリモーフィズム 関数ポインタを持つ親を継承パターン void requestPush(Request *r) { r->send(r); } /* RequestEx

    *rex を用意 */ /* r->send は Request と RequestEx で別の関数 */ requestPush((Request *)rex); 113
  21. 134 int register_filesystem(struct file_system_type * fs) { … } fs/filesystems.c

    ファイルシステムを使えるようにする ここに必要な情報を書いて渡す
  22. 135 struct file_system_type { const char *name; int fs_flags; …

    int (*init_fs_context)(struct fs_context *); … include/linux/fs.h ファイルシステムを使えるようにする 名前やら関数ポインタやら
  23. 136 ext4 の場合 static struct file_system_type ext4_fs_type = { .owner

    = THIS_MODULE, .name = "ext4", .mount = ext4_mount, .kill_sb = kill_block_super, .fs_flags = FS_REQUIRES_DEV, }; /* 初期化関数内 */ err = register_filesystem(&ext4_fs_type); fs/ext4/super.c 普通のインスタンスでした
  24. 138 ファイルを操作する部分 const struct file_operations ext4_file_operations = { … .open

    = ext4_file_open, .release = ext4_release_file, .fsync = ext4_sync_file, … fs/ext4/file.c 構造体に関数ポインタを設定
  25. 139 どこから呼ばれるのか struct inode { … const struct file_operations *i_fop;

    … include/linux/fs.h inode 構造体のメンバにいた! これもインスタンスへのポインタ
  26. 141 外から見ると多態 struct inode *p_inode; … /* 内部で p_inode->i_fop を参照

    */ any_function(p_inode); i_fop で指定した関数で動作が変わる
  27. 142 実質的にインタフェース 言語の機能としては構造体のインスタンス 意図は明らかにインタフェース struct inode { … const struct

    file_operations *i_fop; … class Ext4 implements VFS {…} class Inode { VFS vfs; } ※inode は ext4 用のものというわけではない
  28. 149 class Ext4 implements VFS { … } class Btrfs

    implements VFS { … } ファイルシステムは VFS インタフェースを使うのね
  29. 150 interface VFS { public void file_open() { ... }

    public void file_read() { ... } public void file_write() { ... } … } ふむふむ こんなメソッドがあるのね
  30. 157 const struct file_operations ext4_file_operations = { … .open =

    ext4_file_open, .release = ext4_release_file, .fsync = ext4_sync_file, … おっ。それっぽいな。
  31. 158 const struct file_operations ext4_file_operations = { … .open =

    ext4_file_open, .release = ext4_release_file, .fsync = ext4_sync_file, … おっ。それっぽいな。 他のファイルシステムも同じだ。 これがインタフェースの役割か。