$30 off During Our Annual Pro Sale. View Details »

オブジェクト指向は必要なのか / Is object-oriented needed?

オブジェクト指向は必要なのか / Is object-oriented needed?

2024/3/24に開催されたObject-Oriented Conferenceでの登壇資料です。
https://ooc.dev/2024/

Naoki Kishida

March 24, 2024
Tweet

More Decks by Naoki Kishida

Other Decks in Programming

Transcript

  1. 2024/03/24 2 自己紹介 • きしだ なおき • LINEヤフー • X(twitter):

    @kis • blog: きしだのHatena • (nowokay.hatenablog.com) • 「プロになるJava」というJavaの本を書いてます • WEB+DB PRESS vol.132でオブジェクト指向特集書いてます
  2. モチベーション • プログラム初心者が「まだオブジェクト指向がわからないので」 のような発言をする • forループが書けるようになるのが先では? • 「オブジェクト指向」がわかっても、やりたいと思ってること できるようにならんで •

    「オブジェクト指向」が初心者の到達するべき目標になっている • 「オブジェクト指向」がわかればプログラムが組める? • プログラムが組めないのは「オブジェクト指向」がわかってない? • こういう不毛な誤解はなくなるべき • 初心者に「オブジェクト指向」は不要。かなりあとまわしでいい。
  3. 簡単な歴史 • 1960年代: Simulaによる原初の実装 • 1970年代: アラン・ケイによる命名とコンセプト化 • 1980年代: C++で一般化

    • カプセル化・継承・ポリモーフィズムという実用的な定義 • 1990年代: GUIの普及で大流行 • オブジェクト指向と付ければ売れるので、何でもオブジェクト指向化 • 2000年代: Web技術の発展により斜陽化 • 関数型の流行、手法からプロセスへ • 2010年以降: そして伝説へ • なんかすごいらしいけど、実際に何を意味するかわからない
  4. アラン・ケイのオブジェクト指向 • プログラミング技術ではないのでは? • Smalltalkは商用化時にSimula形式のオブジェクト機能を導入 • 職業プログラマに使いやすくなった • 「自分の手を離れた」といっている •

    エンドユーザーコンピューティングの研究 • エンドユーザーがコンピュータを使うためのプログラミング • 定義が断片的 • 職業プログラマの開発の指針にするのは難しそう • 実用化には再定義や再構築が必要 • 結局、その人の手法になる • ということでout-of-scope
  5. 代表的なオブジェクト指向手法 • スリーアミーゴズの手法 • OMT(オブジェクトモデル化技法) • ランボー • オブジェクトモデリング •

    OOSE(オブジェクト指向ソフトウェア工学) • ヤコブソン • ユースケースによる分析 • ブーチ法 • ブーチ • 開発プロセス • 他にもコード/ヨードンのOOA/OOD、Shlaer/Mellor法など
  6. なぜ多くの手法が現れたのか • 分析からコードまで同じモデルが使える • 気難しい継承を扱うために手法が必要 • クラスと継承を軸にそれぞれ関心ある開発領域をまとめる • 分析、プロジェクト管理、etc •

    オブジェクト指向と名前をつけたら儲かる • 研究に予算がつく • 本が売れる • ソフトウェアが売れる • ソフトウェア工学が全部オブジェクト指向に • ソフトウェア工学の名前が浸透しなくなった • この会も「ソフトウェア工学カンファレンス」が適切(人がこない)
  7. オブジェクト指向手法の欠点 • ウォーターフォールが前提となっている • 分析→設計→実装 • 分析と実装で同じ記号が使える部分は実装の前倒しでしかない • 図によるコーディングでしかない •

    アーキテクチャが組み込まれていない • ネットワーク普及前なので1台で処理する前提になってしまっている • RDBMSとのインピーダンスミスマッチ • 複雑なサーバー構成に対応できない • 現実にはアーキテクチャが決まり実装モデルが決まる • アプリケーションを一体で構築する
  8. 手法からプロセスへ • 各工程ごとの成果物を定義するとウォーターフォールになる • ウォーターフォールを脱すると各工程での成果物の定義が難しい • サービス成長にあわせてアーキテクチャの模索も必要になる • コードの形だけを事前に決めることが難しい •

    よいソフトウェアはよいプロセスから生まれる • CMMの流行 • ラショナルの統一手法も統一プロセスになった • コンピューティングリソースを開発プロセスにまわせるように なった • 継続的インテグレーション • Gitによる開発プロセス(昔はリポジトリのコピーなんて考えれなかった)
  9. 継承ではない条件を考える • method(obj)をobj.method()と書ければオブジェクト指向 • 大騒ぎするほどの効果があるか疑問 • クラスを使えばオブジェクト指向 • 構造体でよさそう? •

    データと関数を一緒に書ければオブジェクト指向 • 抽象データ型 • オブジェクトを作ればオブジェクト指向 • static避けでオブジェクト指向ロンダリングのためのオブジェクト作る • ソフトウェアをうまく作る工夫はすべてオブジェクト指向 • ソフトウェア工学のブランディングの失敗・・・
  10. オブジェクトと関数の関係 • オブジェクトと関数は可換 • 属性をもたずメソッドが一つのオブジェクトは関数として扱える • Javaのラムダ式 • 名前を与えて要素を返す関数はオブジェクトとして扱える Function<String,

    Object> myObj(int a, int b) { return name -> switch(name) { case "a" -> a; case "b" -> b; case "mul" -> a * b; case "add" -> a + b; default -> throw new RuntimeException(); }; } class MyFunc() { int apply(int a, int b) { return a + b; } }
  11. テンプレートメソッド • ポリモーフィズムの基本 abstract class Base { abstract void before();

    abstract void after(); void proc() { before(); println("はろー"); after(); } } class School extends Base { @Override void before() { println("起立"); } @Override void after() { println("着席"); } }
  12. 高階関数による実装 • 高階関数だとすっきり書ける void proc(Runnable before, Runnable after) { before.run();

    println("はろー"); after.run(); } proc(() -> println("起立"), () -> println("着席")); ※ Runnableは引数戻り値なしの関数を表すのに使われる
  13. ポリモーフィズムは事前評価での条件分岐 • 条件をどのように集約するか • 次のcartの合計処理を考える • PackedとBulkで計算処理が違う sealed interface Type

    { /** 量り売り */ record Bulk(int price, int unit) implements Type {} /** パッケージ */ record Packed(int price) implements Type {} } record Item(Type type, int amount) {} var cart = List.of( new Item(new Packed(300), 3), new Item(new Bulk(250, 100), 230));
  14. switchで条件分岐 • switchで条件分岐 var cart = List.of( new Item(new Packed(300),

    3), new Item(new Bulk(250, 100), 230)); int total = cart.stream() .mapToInt(item -> switch(item) { case Item(Packed(int price), int amount) -> price * amount; case Item(Bulk(int price, int unit), int amount) -> price * amount / unit; }).sum();
  15. 継承で条件分岐 • 継承を使うとスッキリかけるように見える • Typeが増えても計算部分を変更しなくていい sealed interface Type { int

    calc(int amount); /** 量り売り */ record Bulk(int price, int unit) implements Type { int calc(int amount) { return price * amount / unit; } } /** パッケージ */ record Packed(int price) implements Type { int calc(int amount) { return price * amount; } } } var cart = List.of( new Item(new Packed(300), 3), new Item(new Bulk(250, 100), 230)); int total = cart.stream() .mapToInt(item ->  item.type().calc(item.amount())) .sum();
  16. オブジェクト指向は状態管理技術 • 状態管理を共通化する • 例 • I/O • List abstract

    class IO { boolean opened; void open() { if (opened) return; openImpl(); opened = true; } void close() { if (!opened) error; closeImpl(); opened = false; } abstract void openImpl(); abstract void closeImpl(); } class MyIO extends IO { @Override void openImpl() { println("開いた!"); } @Override void closeImpl() { println("閉じた!"); } }
  17. 部分型はオブジェクト指向か? • 確かに部分型は継承の重要な機能 • オブジェクト指向は部分型以外の型機能に触れていない • 関数型 • 総称型(ジェネリクス) •

    部分型にも種類がある • 継承の部分型は明示的に基底型を指定する • 公称型 • 継承=公称型+属性拡張 • 指定した要素を持っていれば部分型とみなすやりかたもある • 構造的部分型 • ダックタイピング
  18. クラスのデータ性とモジュール性 • クラスにはデータ型としての性質とモジュールとしての性質 • データ型としてのクラス • インスタンスが複数つくられる • モジュールとしてのクラス •

    シングルトンであることが多い • サブシステムになる • 処理のあつまり class Name { String first; String last; Name(String first, String last) { this.first = first; this.last = last; } String toString() { return first + " " + last; } } @RestController class Greeting { @GetMapping("/hello") String hello() { return "hello"; } @GetMapping("/myname") Name myName() { return new Name("Naoki", "Kishida"); } }
  19. 純粋データ型を考える • 純粋関数 • 副作用をもたない • 同じ引数を与えると同じ戻り値が返る • 純粋データ型(造語) •

    データ型の外に副作用をもたない • 同じ属性を持つデータのメソッドに同じ引数を与えると同じ戻り値が 返る
  20. まとめ • オブジェクト指向は継承を使うプログラミングであり状態管理 • 継承以外はオブジェクト指向以外の言葉が使える • 用語のブレがないので技術コミュニケーションしやすい • アプリケーションではオブジェクト指向の出番はほとんどない •

    端点で状態管理をすれば中間はステートレス=関数的 • フレームワークやミドルウェアでは必要 • オブジェクト指向を勉強してフレームワークやミドルウェアを作ろう