オブジェクト指向できていますか?
Upcoming SlideShare
Loading in...5
×
 

Like this? Share it with your network

Share

オブジェクト指向できていますか?

on

  • 181,457 views

 

Statistics

Views

Total Views
181,457
Views on SlideShare
142,511
Embed Views
38,946

Actions

Likes
610
Downloads
1,203
Comments
8

107 Embeds 38,946

http://yamitzky.hatenablog.com 14064
http://hiroki.jp 8468
http://naosim.blog16.fc2.com 2280
https://twitter.com 2056
http://hpbs002 1702
http://d.hatena.ne.jp 1348
http://www.techgig.com 1079
http://tech.shuntak.net 1078
http://obc-fight.blogspot.jp 941
http://cointoss.hatenablog.com 756
http://hrnabi.com 645
http://bikkuri.me 416
http://bobuhiro11.net 375
http://naosim.syoyu.net 353
https://si0.twimg.com 351
http://localhost 228
http://baba-s.hatenablog.com 223
http://assaulter.hatenablog.com 222
http://takubon.hatenablog.com 169
http://notes.shuntak.net 163
https://twimg0-a.akamaihd.net 155
http://172.18.12.46 149
http://www.pochi.cc 143
http://otto.sytes.net 142
http://naganegi0505.hatenablog.com 141
http://s.deeeki.com 121
http://labs.asunochikara.com 101
http://otrsrv-mzp 80
http://admin.blog.fc2.com 70
http://10.150.200.57 61
http://control.blog.fc2.com 53
http://twitter.com 52
http://obc-fight.blogspot.com 49
http://tomoyamkung.net 47
http://memerelics.tumblr.com 47
http://10.8.0.201 47
http://flavors.me 38
http://omega014.tumblr.com 36
https://cybozulive.com 35
http://49.212.192.99 35
http://blog.bobuhiro11.net 28
http://pute293.tumblr.com 26
http://2764315556584061984_73861a7364f94f99e40694ede4186de175c91c27.blogspot.com 25
http://shake-freek.com 23
http://tweetedtimes.com 22
http://webmemo.uzuralife.com 21
http://cptl.corp.yahoo.co.jp 20
http://feedly.com 18
http://b.hatena.ne.jp 16
http://133.242.3.32 14
More...

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel

15 of 8 Post a comment

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
  • なんかいいですね。
    とてもいい刺激になりました。
    ありがとうございます。
    Are you sure you want to
    Your message goes here
    Processing…
  • とりあえず長い関数を分割することからはじめてみよう
    Are you sure you want to
    Your message goes here
    Processing…
  • takano32様、ご指摘ありがとうございます。P43修正いたしました。P46はmyFriendをリファクタリングして、getYourFriendを呼ばないようにするという意図です。
    Are you sure you want to
    Your message goes here
    Processing…
  • P43 typo
    LimetTime

    P46 me.getYourFriend() ?
    --
    yourFriend = me.getYourFriend();
    yourFriend.method();
    --

    more better, i think so.
    Are you sure you want to
    Your message goes here
    Processing…
  • なるほどね。
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

オブジェクト指向できていますか? Presentation Transcript

  • 1. オブジェクト指向できていますか? 真のオブジェクト指向が身に付くコーディング規約 日本工学院八王子専門学校 大圖 衛玄
  • 2. CEDEC 2012講演時のスライドです。http://cedec.cesa.or.jp/2012/program/PG/C12_P0084.html
  • 3. 自己紹介 1992年~1997年 某ゲーム会社 プログラマ SFC,GB,PS1,N64のゲーム開発経験 1998年~現在 日本工学院八王子専門学校 @mozmoz1972 専任講師 プログラミング教育を中心に担当twitterもfacebookも実名です。よかったらフォローしてください。
  • 4. オブジェクト指向できていますか?
  • 5. なぜオブジェクト指向できないのか?
  • 6. 手続き型の言語から学習した人がOOPするには大きな発想の転換が必要です。
  • 7. そのクラスは巨大な1枚岩のコードで作成され・・・(次に続く)
  • 8. 全ての機能が実装されていた・・・(次に続く)
  • 9. 人々はそのクラスを「神クラス」と呼んだ。非OOPなコードの特徴です。
  • 10. Stage1 Stage2 Stage3 Stage4 Stage5実際にあった伝説のコード。シンプルな2Dゲームなのに1万行ありました。調べてみると、ステージごとに、まるごとコピペで作成した神クラスが5つ。1ゼウス2千行、5ゼウスで、合計1万行となっていました。
  • 11. 理想的なオブジェクト指向の 世界とは?
  • 12. 小さな大量のオブジェクトが・・・(次に続く)
  • 13. お互いメッセージを送りながら協調し複雑なシステムを構築する。(次に続く)
  • 14. 各クラスは、1つの機能に集中し、最小限のインターフェースで構成されています。
  • 15. コーディング規約で 理想的な オブジェクト指向の 世界を目指す本セッションのゴールです。
  • 16. 命名規則やスペースの数、{}位置などのコーディング規約のお話ではありません。
  • 17. オブジェクト指向 エクササイズ Jeff Bayソフトウェア設計を改善する 9つのステップ
  • 18. 第5章に書かれているJeff Bay氏によるエッセイになります。
  • 19. 読後の衝撃は忘れられません。理想的なオブジェクト指向の姿が理解できました。
  • 20. Jeff Bayが冒頭で紹介している、7つのコード品質特性です。
  • 21. Extreme必然的にオブジェクト指向になってしまう、Extremeなコーディング規約です!
  • 22. メソッドにつきインデントは1段階 1つのメソッドごとに制御文を1つに制限 if、for、whileごとにメソッド化する 制御文の内側をメソッド化する 早期リターンを活用しインデントを浅くする制御文のネストをなくすルールです。小さく、単純なメソッドを作成するのが目的です。
  • 23. void Class::method() { void Class::method() { for (外側ループ) { for (外側ループ) { for (内側ループ) { for (内側ループ) { if (条件) { method1(…); 処理; } } } } } } } void Class::method1(…) { if (!条件) return; 処理; } Before After制御分の内側から順番にメソッド化していきます。
  • 24. void Class::method() { void Class::method() { for (外側ループ) { for (外側ループ) for (内側ループ) { method2(…); method1(); } } } void Class::method2(…) {} for (内側ループ) method1(…); } Before After
  • 25. void Class::method() { void Class::method() { for (外側ループ) { for (外側ループ) for (内側ループ) { method2(…); if (条件) { } 処理; void Class::method2(…) { } for (内側ループ) } method1(…); } } } void Class::method1(…) { if (!条件) return; 処理; } Before After制御文ごとにメソッド化され、ネストが1段階となりました。
  • 26. for (int i = 0; i < 5; ++i) { if (a[i] == num) { return true; } } return false; Before return find(&a[0], &a[5], num) != &a[5]; After検索などのループは、標準の関数を利用しましょう。STLのfindに変更しました。
  • 27. totalAge = 0; totalSalary = 0; for (int i = 0; i < 5; ++i) { totalAge += ages[i]; totalSalary += salarys[i]; } Before totalAge = 0; for (int i = 0; i < 5; ++i) totalAge += ages[i]; totalSalary = 0 for (int i = 0; i < 5; ++i) totalSalary += salarys[i]; Afterループの内側で2つのことを扱うと、メソッド化が困難です。ループを分割します。
  • 28. totalAge = 0; for (int i = 0; i < 5; ++i) totalAge += ages[i]; totalSalary = 0; for (int i = 0; i < 5; ++i) totalSalary += salarys[i]; Before totalAge = accumulate(&ages[0], &ages[5], 0); totalSalary = accumulate(&salarys[0], &salarys[5], 0); After配列の合計はSTLのaccumulate関数で求められます。
  • 29. totalAge = accumulate(&ages[0], &ages[5], 0); totalSalary = accumulate(&salarys[0], &salarys[5], 0); Before int Class::totalAge() { return accumulate(&ages[0], &ages[5], 0); } int Class::totalSalary() { return accumulate(&salarys[0], &salarys[5], 0); } Afterさらにメソッド化をしてみました。
  • 30. totalAge = 0; totalSalary = 0; for (int i = 0; i < 5; ++i) { totalAge += ages[i]; totalSalary += salarys[i]; } Before int Class::totalAge() { return accumulate(&ages[0], &ages[5], 0); } int Class::totalSalary() { return accumulate(&salarys[0], &salarys[5], 0); } AfterMartin Fowlerの「Split Loop」というリファクタング例になります。
  • 31. else句を使用しないこと 早期リターンを活用する else if、switchの条件分岐を避ける Strategy、Stateなどのデザパタを活用 三項演算子(?)を利用する条件分岐を単純化するのが目的です。
  • 32. int method() { int method() { int result; if (!条件1) if (条件1) { return 10; if (条件2) { if (条件2) result = 20; return 20; } else { return 30; result = 30; } } } else { result = 10; } return result; } Before After左と右のコードは同等の処理を行います。早期リターンを使うと単純になります。
  • 33. switch (actorType) { class Actor { case PLAYER: public: virtual void update() = 0; updatePlayer(); }; break; case ENEMY: updateEnemy(); actor->update(); break; case BULLET: updateBullet(); break; } Before After「State/Strategyによるタイプコードの置き換え」というリファクタリングです。
  • 34. int method() { int method() { int result; return 条件 ? 20: 30; if (条件) } result = 20; else result = 30; return result; } Before After三項演算子(?)は使いすぎると、わかりにくくなりますが、単純なケースでは有用です。
  • 35. if (x < 0) { x = 0; } else if (x > 640) { x = 640; } Before x = max(0, min(640, x)); After上記のようなif文は、よく見かけますが、STLのmin,max関数で書き直せます。
  • 36. if (x < 0) { x = 0; } else if (x > 640) { x = 640; } Before x = clamp(x, 0, 640); float clamp(float x, float bottom, float top) { return max(bottom, min(top, x)); } Afterさらにclampという関数を作ってみます。C++であれば、関数テンプレート化しましょう。
  • 37. すべてのプリミティブ型をラップ int、floatなどの基本型をラップする stringなども基本型の扱いとする 得点、時間などの変数をクラス化する すべての状態変数をカプセル化するすべてをオブジェクト化するのが目的です。intやfloatはオブジェクトではありません。
  • 38. class Game { class Game { private: private: int score; Score score; int limitTime; LimitTime time; }; }; class Score { private: int score; }; class LimitTime { private: int time; }; Before After得点や制限時間をクラス化します。「すべてがオブジェクト」になっていきます。
  • 39. 1行につきドットは1つまで 直接の友人にだけ話かける 友達の友達とは関連を持たない デメテルの法則に従う余計なクラスとの結合を避けるのが目的となります。
  • 40. myFriend.getYourFriend().method(); Before myFriend.method(); After「保持しているもの、引数で渡されたもの、自ら作成したもの」だけを扱います。
  • 41. 寿限無、寿限無 五劫の擦り切れ 海砂利水魚の 水行末 雲来末 風来末 食う寝る処に住む処 やぶら小路の藪柑子 パイポパイポ パイポのシューリンガン シューリンガンのグーリンダイ グーリンダイの ポンポコピーのポンポコナーの 長久命の長助長ったらしい名前を付けなさいという意味ではありません。その逆です!
  • 42. 名前を省略しない 省略したいほど長い名前がつく原因を考える 命名困難なクラスやメソッドは要注意 複数の責務を持っていると命名困難になる 名前は1つか2つの単語だけ使う名前が長くなるのは、設計に問題があるのではないか? という意図です。
  • 43. void Game::updateAndDrawPlayer(Graphics& g) { playerPosition += Vector2(5.0f, 0.0f); g.drawImage(playerImage, playerPosition); } Before void Game::updatePlayer() { playerPosition += Vector2(5.0f, 0.0f); } void Game::drawPlayer(Graphics& g) { g.drawImage(playerImage, playerPosition); } After2つのことを同時にやってしまうと、名前を付けるのが難しくなります。
  • 44. void Game::updatePlayer() { playerPosition += Vector2(5.0f, 0.0f); } void Game::drawPlayer(Graphics& g) { g.drawImage(playerImage, playerPosition); } Before void Game::update() { player.update(); } void Game::draw(Graphics& graphics) { player.draw(graphics); } Afterメソッドを分割し、さらにクラスの抽出を行いました。名前が単純になりました。
  • 45. すべてのエンティティを小さくする 50行を超えるクラスは作らない 10ファイルを超えるパッケージは作らない 単一の責務を持つ凝集度の高い設計とする凝集度を高めれば、クラスやパッケージは小さくできる。という意図になります。
  • 46. インスタンス変数は2つまで クラスは1つの状態変数に責任を持つ 2つの変数を扱う調整役のクラスもある 状態変数が増えるたびに凝集度が低下1つの状態を管理するクラスと、2つの状態を調整する2種類のクラスが存在します。
  • 47. class Player { class Player { private: private: float x; Vector2 position; float y; Angle angle; float angle; Life life; int life; }; }; class Angle { private: float angle; }; class Life { private: int life; }; Before After状態変数が2つになるまで、段階的に変更していきます。
  • 48. class Player { class Player { private: private: Vector2 position; Transform pose; Angle angle; Life life; Life life; }; }; class Transform { private: Vector2 position; Angle angle; }; Before Afterpositionとangleは座標変換を扱うクラスとして、まとめられそうです。
  • 49. class Player { class Player { private: private: float x; Transform pose; float y; Life life; float angle; }; int life; }; Before After状態変数が2つになりました。
  • 50. class Player { class Player { public: public: Player(float x, Player(Transform& pose, float y, Life& life); float angle, void update(Time& time); int life); private: void update( Transform pose; float time); Life life; private: }; float x; float y; float angle; int life; }; Before After右側のクラスは、すべてがオブジェクトと関連しています。抽象度が高くなるわけです。
  • 51. ファーストクラスコレクション vector、listなども基本型としてラップする クラスにはコレクションを1つだけ持たせる もちろん配列も対象になる汎用のコレクションクラスもプリミティブ型と同様に扱うという意図です。
  • 52. class Game { private: std::list<Actor*> actors; std::list<Particles*> particles; }; Before class ActorManager { std::list<Actor*> actors; }; class ParticleManager { std::list<Particle*> particles; }; After2つのコレクションを1つのクラスで扱えば、おそらく複雑なクラスになるでしょう。
  • 53. class Game { private: std::list<Actor*> actors; std::list<Particles*> particles; }; Before class Game { private: ActorManager actors; ParticleMananger particles; }; After専用のクラスを作って、そちらに委譲してしまいます。
  • 54. Getter Setterを使用しない Getter Setterなどのアクセッサを禁止 極度のカプセル化を行う クラス外に振る舞いが漏れ出すのを防止 「求めるな、命じよ」に従うGetter/Setterを付けてしまうと、カプセル化が崩壊してしまうという意図です。
  • 55. 構造体すべてのメンバ変数にGetter/Setterを持たせると、単なる構造体になってしまいます。
  • 56. void Game::draw(Graphics& g) { Vector2 position = player.getPosition(); Image& image = player.getImage(); g.drawImage(image, position); } Before void Game::draw(Graphics& g) { player.draw(g); } void Player::draw(Graphics& g) { g.drawImage(image, position); } AfterGetterで「求める」のではなく、オブジェクトに「命じよ」。自分のことは自分でやらせる。
  • 57. void Game::update() { Vector2 position = player.getPosition(); position += Vetcor2(5.0f, 2.0f); player.setPosition(position); } Before void Game::update() { player.move(Vetcor2(5.0f, 2.0f)); } After振る舞いが外に出てしまっています。オブジェクト自身に行動させます。
  • 58. 振る舞いが外に出てしまうと、コードの重複を生み出す原因になります。
  • 59. 状態は、すべてカプセル化され、メッセージのやりとりだけで、協調して動作する。
  • 60. #1 1つのメソッドにつきインデントは1段階まで#2 else句を使用しないこと#3 すべてのプリミティブ型をラップする#4 1行につきドットは1つまで#5 名前を省略しない#6 すべてのエンティティを小さくする#7 1つのクラスにつきインスタンス変数は2つまで#8 ファーストクラスコレクションを使用する#9 Getter Setterを使用しない
  • 61. 実際にやってみたエクササイズに挑戦した、ソースコードの解説をしました。(1000行程度のものです)
  • 62. Jeff Bay挑戦した人だけが、たどり着ける高みがあるのです。
  • 63. オブジェクト指向が、いったい何であるかを理解した瞬間をカメラに収めました。
  • 64. エクササイズまとめ 騙されたと思って小さなプログラムで試す 今までとは異なるアプローチが必要になる すべてのルールが普遍的に適用できない ルールを緩めてガイドラインとして利用するルールには、例外も出てきます。ルールの「目的や意図」を理解することが重要です。
  • 65. Jeff BayJeff Bayのエッセイの結びに書いてあった内容です。(抜粋してあります)
  • 66. 9つのルールに関連する、オブジェクト指向設計の原則を紹介しました。
  • 67. オブジェクト指向 設計の原則 単一責任の原則 オープン・クローズドの原則 リスコフの置換原則 依存関係逆転の原則 インターフェース分離の原則「単一責務の原則」と「依存関係逆転の原則」の2つだけ解説します。
  • 68. 単一責任の原則 Single Responsibility Principles クラスを変更する理由は 1つ以上 存在してはならないひとつのクラスには1つの責任だけを持たせるという意味です。
  • 69. 凝集度凝集度とは、1つのクラスがどれだけ、1つのことに集中しているか?ということです。
  • 70. f1 f1 f1 f3 f4 f1 f3 f4 f3 f4 f2 f3 f4 f2 f2状態が1つの場合、凝集度は最高に。2つでも全メソッドで使用されれば問題なし。
  • 71. f1 f2 f1 f1 f1 f1 f2 f2 f2 f2凝集度が低い状態です。明らかに、2つの責務を扱っています。
  • 72. f1 f2 f1 f2 f1 f2 f1 f2 f2 f1メソッドが大きいと、凝集度が低いのか、高いのか判断できません。(大抵は低いはず)
  • 73. f1 f2 f1 f2 f1 f1 f1 f2 f1 f1 f2 f1 f1 f2 f2 f2 f2 f2 f1 f2状態変数の振る舞いを観察しながら、細かくメソッド化をしていきます。
  • 74. f1 f1 f2 f1 f1 f1 f1 f1 f1 f1 f2 f2 f2 f2 f2 f2 f2 f2メソッドをグループ化して、クラスを抽出します。
  • 75. f1 f1 f1 f1 o1 o2 o1 o2 f2 f2 f2 f2抽出後の状態です。凝集度は最高の状態になっています。
  • 76. 大きな関数を多くの小さな関数へ分割する ことが、クラスを、より小さなクラスへと 分割することにつながるのです。「Clean Code 」からの引用です。メソッド化がクラス化への第1歩になります。
  • 77. 平均的なクラス 理想的なクラス私見ですが、世の中の平均的なクラスは、大きすぎると思います。
  • 78. 小さな部品を組み合わせながら、複雑なシステムを構築していきます。
  • 79. 再利用可能なクラスとは、ネジのように単純なことを行う小さなクラスなのです。
  • 80. 依存関係逆転の原則 The Dependency Inversion Principles 上位のクラスは下位のクラスに 依存してはならない どちらも「抽象」に依存する
  • 81. 疎結合オブジェクトは、協調しつつも疎結合であることが望まれます。
  • 82. class Sphere { class Sphere { public: public: void draw( void draw( ID3D11Device& g); ISphereRenderer& g); private: }; Vector3 center; float radius; class ISphereRenderer { }; public: virtual void draw( Vector3 center, float radius ) = 0; }; Before After直接、実装のクラスではなく、抽象インターフェースに依存させます。
  • 83. void Sphere::draw( void Sphere::draw( ID3D11Device& g) { ISphereRenderer& g) { // 頂点バッファ作成 g.draw(center, radius); // インデックスバッファ作成 } // 球体の頂点データを計算 // シェーダー作成 // 頂点レイアウト作成 // 描画コンテスト作成 ・ ・ ・ // なんとか描画する } Before After抽象インターフェースに依存させれば、実装の詳細に依存しなくなるわけです。
  • 84. Sphere.cpp Sphere.cpp ISphereRenderer.h SphereRenderer.cpp DirectX DirectX Before After右図の青い「上向き」の矢印に注目してください。依存関係が逆転しています。
  • 85. 問題領域 問題領域のクラス 抽象インターフェース 実装領域 実装クラス API Windows API, DirectX, OpenGL問題領域と実装を分離すれば、それぞれのクラスの凝集度を高めることができます。
  • 86. class ISphereRenderer { class Sphere { public: public: virtual void draw( class Renderer { Vector3 center, public: float radius) = 0; virtual void draw( }; Vector3 center, float radius) = 0; class Sphere { }; public: void draw( void draw( Renderer& g); ISphereRenderer& g); }; }; Before After抽象インターフェースをインナークラス化し完全に自己完結させた例です。(極端か?)
  • 87. あなたの あなたの コード コード 抽象 実装 レガシーコード レガシーコードレガシーコードの分離にも役立ちます。不要な複雑さを持ち込まないようしましょう。
  • 88. インターフェースと実装を分離する 実装そのものではなく インターフェースに対してプログラミングせよ抽象インターフェースは、多態性だけではなく、依存関係の分離の役割も果たします。
  • 89. Jeff Bayの9つのルールを参考に作成した、コーディング規約の事例紹介です。
  • 90. 7つのコーディング規約 お尻は掻いても クソース書くな 2012年改訂版学生対象のゲーム制作プロジェクトで実際に採用した規約です。
  • 91. 複雑度 10 まで複雑度とは、McCabeの循環的複雑度を意味します。(ほぼ制御文+1になります)
  • 92. 1メソッド 20 ステートメントまでステートメントとは実質的なプログラムの行数です。(コメントなどを除きます)
  • 93. 1クラス 80ステートメントまで
  • 94. ネスト 2段階まで
  • 95. setter 使用禁止Getterは最小限に、Setterのみ禁止にしました。
  • 96. フィールド 4つ まで特に根拠がある数ではありません。フィールド数は、できるかぎり少なくしましょう。
  • 97. tweet! ソースコードを 愛 せよネタです。
  • 98. ≒Jeff Bayのルールに比べれば、「ゆとり」仕様になっていませんか?
  • 99. コーディング規約の調整 理想 標準 妥協 複雑度 5 10 15 ネスト 1 2 4 メソッド長 10 20 40 クラス長 50 80 160厳しすぎてもユルすぎてもダメです。まずは、無理なく守れる程度がよいと思います。
  • 100. http://www.slideshare.net/MoriharuOhzu/ss-9224836昨年のスライドで、コーディング規約の確認の方法や順守させる工夫を紹介しました。
  • 101. 抽象度クラスやメソッドの行数を制限することにより、全体の抽象度を高める効果があります。
  • 102. 3つの極度 極度の抽象化 極度の分離 極度の読みやすさ極度の抽象化の考え方は、上記の本でも紹介されています。
  • 103. まとめ コーディング規約の本質を知ることが重要 例外もあるので、無理やり適用はしない ガイドラインとして活用するところから始める トレーニングとして研修に取り入れてみる繰り返しになりますが、ルールの「目的や意図」を理解することが重要です。
  • 104. クラス設計のポイントです。
  • 105. 小さい 単純 重複なしクラス、メソッドのすべてに、このルールがあてはまります。
  • 106. http://www.bennadel.com/resources/uploads/2012/ObjectCalisthenics.pdf英文になりますが、Jeff Bayのオブジェクト指向エクササイズの全文が入手可能です。
  • 107. http://www.slideshare.net/yojik/ss-1033616SlideShareで紹介されている、オージス総研の方のスライドです。
  • 108. http://www.slideshare.net/rdohms/your-code-sucks-lets-fix-itこちらもSlideShareのスライドです。言語はPHPですが、参考になると思います。
  • 109. 最近では、オブジェクト指向と関数型のハイブリッド言語が注目を浴びています。
  • 110. タイプの異なる3つの言語を3人の女性に例えました。ちょっと考えてみてください。
  • 111. オブジェクト指向がすべてではありません。さまざまな言語から異なる発想を学びましょう。
  • 112. 普通のやつらの上を行け Paul GrahamPaul Grahamのエッセイのタイトルから引用しました。
  • 113. コードと共に生き続ける Moriharu Ohzu