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

オレがオブジェクト指向をやめるために必要な3つのPHP RFC/PHP RFC for quit Object Oriented Programming

オレがオブジェクト指向をやめるために必要な3つのPHP RFC/PHP RFC for quit Object Oriented Programming

2023年6月22日のPHPカンファレンス福岡全然野菜での登壇資料です。
https://pepabo.connpass.com/event/280682/

Naoki Kishida

June 22, 2023
Tweet

More Decks by Naoki Kishida

Other Decks in Programming

Transcript

  1. オレがオブジェクト指向をやめるために必要な
    3つのPHP RFC
    2023/6/22 PHPカンファレンス福岡 全然野菜
    LINE Fukuoka きしだ なおき

    View Slide

  2. 2023/06/22 2
    自己紹介

    きしだ なおき

    LINE Fukuoka

    twitter: @kis

    「プロになるJava」という
    Java入門書を書いてます

    View Slide

  3. 2023/06/22 3
    オブジェクト指向とは

    オブジェクト指向は状態管理技術

    状態遷移の共通化が行いやすい

    状態遷移を管理しない場合
    他の技法でも十分可能

    View Slide

  4. 状態管理はどこにあらわれるか

    モジュール境界では状態管理が必要
    GUI ロジック データ
    ストア
    ユーザー
    アプリケーション

    View Slide

  5. 現代的アプリケーションはモジュール化済

    Webクライアント+マイクロサービスでモジュール化

    DOM管理やDB接続は世界に数個あればいい

    アプリケーションプログラマが状態管理を共通化する必要がない
    GUI ロジック データ
    ストア
    ユーザー
    アプリケーション
    DOM HTTP DB接続

    View Slide

  6. オブジェクト指向からデータ指向へ

    データ指向プログラミング

    データをデータとして扱う

    抽象データ型

    代数的データ型

    パターンマッチング

    View Slide

  7. 抽象データ型と代数的データ型

    抽象データ型

    型の定義の指針

    代数的データ型

    型の組み合わせの指針

    View Slide

  8. 抽象データ型

    データの操作だけ公開することで変更に強く柔軟な型を定義

    データ特化の「カプセル化」

    操作としてデータ操作だけを含む
    class PersonName {
    function __construct(
    string $surname, string $givenName);
    function getFullName();
    function getSurname();
    function getGivenName();
    }
    class PersonName {
    private string $surname;
    private string $givenName;
    function __construct (
    string $surname, string $givenName) {
    $this->surname = $surname;
    $this->givenName = $givenName;
    }
    function getFullName() {
    return "%s %s".formatted(surname, givenName);
    }
    ...
    class PersonName {
    private string $fullName;
    function __construct (
    string $surname, string $givenName) {
    $this→fullName = $surname.” “.$givenName;
    }
    function getFullName() {
    return $fullName;
    }
    String getSurname() {
    return explode(“ “, $fullName)[0];
    }
    ...
    どちらでもOK

    View Slide

  9. データ型のルール

    純粋関数をデータ型に拡張する

    純粋データ型?

    参照等価

    戻り値が引数だけで決まる

    副作用なし

    状態の変更や外部への入出力なし

    参照等価

    戻り値が引数とフィールドだけで決まる

    副作用なし

    フィールド変更以外の状態の変更や
    外部への入出力なし

    View Slide

  10. 代数的データ型

    直積型

    クラス

    配列

    直和型

    継承

    union

    View Slide

  11. 直積型

    値を組み合わせる型

    値のとりうるパターンがそれぞれの値のパターン数の積になる

    class A{
    function __construct(bool $a1, byte $a2) {}
    }

    2 x 256で512とおり

    View Slide

  12. 直和型

    いずれかの型になる

    int | float

    継承

    View Slide

  13. Tagged Unions

    継承を使う場合

    可能な型を限定できない

    enumで限定

    https://wiki.php.net/rfc/tagged_unions
    enum Distance {
    case Kilometers(int $km);
    case Miles(int $miles);
    }

    View Slide

  14. パターンマッチング

    isによるパターンマッチング

    https://wiki.php.net/rfc/pattern-matching
    class Point {
    public function __construct(public int $x, public int $y, public int $z) {}
    }
    $result = match ($p) is {
    // These will match only some Point objects, depending on their
    property values.
    Point{x: 3, y: 9, %$z} => "x is 3, y is 9, z is $z",
    Point{%$z, %$x, y: 4} => "x is $x, y is 4, z is $z",
    Point{x: 5, %$y} => "x is 5, y is $y, and z doesn't matter",
    // This will match any Point object.
    Point{%$x, %$y, %$z} => "x is $x, y is $y, z is $z",
    };

    View Slide

  15. データの処理を考える

    こんな値を合計する
    record Product(String name, Type type) {}
    record Item(Product product, int amount) {}
    var cart = List.of(
    new Item(new Product("餃子", new Packed(300)), 3),
    new Item(new Product("牛肉", new Bulk(250, 100)), 230));

    View Slide

  16. 継承で実装してみる

    継承での実装
    sealed interface Type {
    /** 量り売り */
    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;
    }
    }
    int calc(int amount);
    }
    int total = cart.stream()
    .mapToInt(item ->
    item.product().type().calc(item.amount()))
    .sum();

    View Slide

  17. 継承のほうがいい?

    すっきりして見える
    int total = cart.stream()
    .mapToInt(item ->
    item.product().type().calc(item.amount()))
    .sum();

    View Slide

  18. 継承で実現したときの欠点

    追うときにいろいろなクラスを見る必要がある

    他のデータが関係あるときに対応できない

    1パック170円、3パック500円とか

    場合わけの型が2つあるときれいにかけない

    どちらかの型に場合わけが必要

    業務処理では一箇所にすべての型の処理をまとめるほうがいい

    システム境界では他の型が関係することがすくないので継承が有効

    OracleConnectionがMySQLConnectionを気にしたりとかはない

    View Slide

  19. 継承で実現したときの欠点

    追うときにいろいろなクラスを見る必要がある

    他のデータが関係あるときに対応できない

    1パック170円、3パック500円とか

    場合わけの型が2つあるときれいにかけない

    どちらかの型に場合わけが必要

    業務処理では一箇所にすべての型の処理をまとめるほうがいい

    システム境界では他の型が関係することがすくないので継承が有効

    OracleConnectionがMySQLConnectionを気にしたりとかはない

    View Slide

  20. 詳しくはWEB+DB PRESS vol.134

    Javaだけど。

    在庫なしっぽいので、電子版を

    もしくは
    「データ指向プログラミング」

    View Slide