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

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

D068c367c4ebaaa982d583cc51dbd45a?s=128

Shota Takizawa

February 16, 2020
Tweet

Transcript

  1. 28.

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

    とかとか必要ですね ゼロから実現方法を 模索するのは大変
  2. 29.

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

    とかとか必要ですね ゼロから実現方法を 模索するのは大変 オブジェクト指向を導入 実装できるルールに落とし込む
  3. 80.

    カプセル化 中身はソースに書く #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. 81.

    カプセル化 使うとき #include <stdio.h> #include "Request.h" int main(void) { Request

    *r = requestNew(1); printf("%d¥n", requestGetId(r)); } main.c 80
  5. 87.

    カプセル化 工夫すると部分的にprivateにできます struct RequestImpl; typedef struct Request { int id;

    struct RequestImpl *pImpl; } Request; Request.h 86 public private RequestImplの中身を.cに書く
  6. 88.

    カプセル化 Pimpl : Pointer to implementation struct RequestImpl; typedef struct

    Request { int id; struct RequestImpl *pImpl; } Request; Request.h 87 public private
  7. 91.
  8. 92.

    struct Request { int id; int src; int dst; char

    payload[256]; … }; 継承 91 これを継承して RequestEx をつくる
  9. 95.

    継承 struct Request { int id; int src; int dst;

    char payload[256]; … }; 0x00f5 b2da fdc0 プログラムからみた アドレス ︙ 94
  10. 96.

    継承 struct Request { int id; int src; int dst;

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

    継承 struct Request { int id; int src; int dst;

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

    struct RequestEx { Request request; int type; } 継承 Request

    request int type 0x00f5 b2da fdc0 プログラムからみた アドレス ︙ 98
  13. 100.

    struct RequestEx { Request request; int type; } 継承 Request

    request int type 0x00f5 b2da fdc0 プログラムからみた アドレス ︙ 99 ここだけ見れば親
  14. 102.

    継承 int id int src int dst char payload[256] int

    type 101 私は Request のポインタです 中身は int, int, int, char[256]
  15. 105.

    継承 子クラスのコンストラクタ #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
  16. 106.

    継承 親クラスのコンストラクタを使いたい #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
  17. 108.

    継承 メモリ確保を外に持っていく 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
  18. 109.

    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 併用不可
  19. 110.

    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 を使う必要がある
  20. 112.
  21. 113.

    ポリモーフィズム 関数ポインタを持つ親を継承 struct Request { int id; … void (*setHeader)(char

    *); int (*send)(void); }; struct RequestEx { Request request; … }; 112 子のコンストラクタで関数ポインタを上書き
  22. 114.

    ポリモーフィズム 関数ポインタを持つ親を継承パターン void requestPush(Request *r) { r->send(r); } /* RequestEx

    *rex を用意 */ /* r->send は Request と RequestEx で別の関数 */ requestPush((Request *)rex); 113
  23. 127.
  24. 134.
  25. 135.

    134 int register_filesystem(struct file_system_type * fs) { … } fs/filesystems.c

    ファイルシステムを使えるようにする ここに必要な情報を書いて渡す
  26. 136.

    135 struct file_system_type { const char *name; int fs_flags; …

    int (*init_fs_context)(struct fs_context *); … include/linux/fs.h ファイルシステムを使えるようにする 名前やら関数ポインタやら
  27. 137.

    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 普通のインスタンスでした
  28. 139.

    138 ファイルを操作する部分 const struct file_operations ext4_file_operations = { … .open

    = ext4_file_open, .release = ext4_release_file, .fsync = ext4_sync_file, … fs/ext4/file.c 構造体に関数ポインタを設定
  29. 140.

    139 どこから呼ばれるのか struct inode { … const struct file_operations *i_fop;

    … include/linux/fs.h inode 構造体のメンバにいた! これもインスタンスへのポインタ
  30. 142.

    141 外から見ると多態 struct inode *p_inode; … /* 内部で p_inode->i_fop を参照

    */ any_function(p_inode); i_fop で指定した関数で動作が変わる
  31. 143.

    142 実質的にインタフェース 言語の機能としては構造体のインスタンス 意図は明らかにインタフェース struct inode { … const struct

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

    149 class Ext4 implements VFS { … } class Btrfs

    implements VFS { … } ファイルシステムは VFS インタフェースを使うのね
  33. 151.

    150 interface VFS { public void file_open() { ... }

    public void file_read() { ... } public void file_write() { ... } … } ふむふむ こんなメソッドがあるのね
  34. 158.

    157 const struct file_operations ext4_file_operations = { … .open =

    ext4_file_open, .release = ext4_release_file, .fsync = ext4_sync_file, … おっ。それっぽいな。
  35. 159.

    158 const struct file_operations ext4_file_operations = { … .open =

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