オブジェクト指向設計実践ガイド読書会 - 第40回 === ###### tags: `オブジェクト指向設計実践ガイド読書会` # 開催概要 * 日程: 2022/11/12 Sat 21:00〜23:00 * 会場: https://discord.com/channels/432531367427964929/898843794101911622 * [README](https://hackmd.io/sSmTRzcQSCuvqiO7uDUkFg) * [タイムキーバー用テンプレート](https://hackmd.io/E0HZBN9OS9GBAtnsYXjDsw) ## 課題図書 『オブジェクト指向設計実践ガイド ~Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方』(Sandi Metz 著, 髙山泰基 訳, 技術評論社) - 技術評論社, pdf版あり - https://gihyo.jp/book/2016/978-4-7741-8361-9 - Amazon - https://www.amazon.co.jp/dp/B01L8SEVYI ## 実施要領 - 事前読書なし。都度その場で読んで、感想を書いて、議論する、を繰り返す。 - 参加スタイルは自由形。チャットでも音声でもご自由に。 - 主催はチャット参加です。 ## タイマーについて 時間管理はタイマーbotで行います。 VCチャンネルに入っている人を対象にメンションが飛ぶので、VCチャンネルへの参加をお願いします。 また、タイマーは終了時に **音声がなる** ので、ご注意ください。 ## 当日までの準備 1. 課題図書を手元に用意する - 買う、借りる、etc(?) 2. 当日までに読んだり読まなかったりする - 当日その場で読むので、事前に読んでおく必要は無し - 気になったら事前に読んでおいても良し --- # 当日の進行 ## 進行フロー https://hackmd.io/sSmTRzcQSCuvqiO7uDUkFg?view#%E9%80%B2%E8%A1%8C%E3%83%95%E3%83%AD%E3%83%BC ## :goti: or :okawari: について - 各パートごとに :goti: or :okawari: を投票する. - :okawari: が1つでも有れば、パートを続行する. ## 開催コール https://1.bp.blogspot.com/-YrhlmWQ4uIQ/UrlmxoUdDeI/AAAAAAAAcLw/M6VFUKHnTos/s400/text_start.png ## タイムキーパー募集 https://hackmd.io/E0HZBN9OS9GBAtnsYXjDsw#%E3%82%BF%E3%82%A4%E3%83%A0%E3%82%AD%E3%83%BC%E3%83%91%E3%83%BC%E5%8B%9F%E9%9B%86 ## 読書パート https://hackmd.io/E0HZBN9OS9GBAtnsYXjDsw#%E8%AA%AD%E6%9B%B8%E3%83%91%E3%83%BC%E3%83%88 ## 投票パート https://hackmd.io/E0HZBN9OS9GBAtnsYXjDsw#%E6%8A%95%E7%A5%A8%E3%83%91%E3%83%BC%E3%83%88 ## トークパート ### 議論(:thinking_face:)パート https://hackmd.io/E0HZBN9OS9GBAtnsYXjDsw#%E8%AD%B0%E8%AB%96%E3%83%91%E3%83%BC%E3%83%88 ### 任意トーク(:+1:)パート https://hackmd.io/E0HZBN9OS9GBAtnsYXjDsw#%E4%BB%BB%E6%84%8F%E3%83%88%E3%83%BC%E3%82%AF%E3%83%91%E3%83%BC%E3%83%88 ### 次回繰越(:eyes:)パート https://hackmd.io/E0HZBN9OS9GBAtnsYXjDsw#%E6%AC%A1%E5%9B%9E%E7%B9%B0%E8%B6%8A%E3%83%91%E3%83%BC%E3%83%88 ## 終了コール http://3.bp.blogspot.com/-zgTNWF_hBmI/UZYlh9RhuDI/AAAAAAAATQ8/dymPl5P4eAs/s500/undoukai_relay_animal.png # 読書メモ ## 8章_コンポジションでオブジェクトを組み合わせる_8.2Partsオブジェクトをコンポーズする_Partsオブジェクトをもっと配列のようにする_p.216_220 ### discord開始地点 https://discord.com/channels/432531367427964929/898843794101911622/1040962057727639665 ### 感想 - forwadable知らなかった :eyes: - https://docs.ruby-lang.org/ja/latest/class/Forwardable.html - 実装見に行ったら想定していたよりまあまあややこしそうだった - https://github.com/ruby/forwardable/blob/master/lib/forwardable.rb - たまには `delegate` のことも思い出してあげてください - 昔、これと似たようなことがやりたくて(でもこれの存在を知らなくて)、自前で実装したおもひで :wheel_of_dharma: - ここで似たような問題、PHPでちょいちょい遭遇して大変かったるいので、これは羨ましい - コレクションオブジェクトをArrayぽく扱えるようにするだけならPHPでも ArrayAccess を実装すれば良いけど、それ以外だと……(そもそもコレクションオブジェクトを作りたいのであって、Arrayっぽくしたいわけでもない) - Ruby、デザインパターン系のライブラリがいくつかあってべんり(Rails だと委譲は ActiveSupport の delegeate 使うけど) :point_left: :+1: - Ruby のデザインパターン系のその他のライブラリ - https://docs.ruby-lang.org/ja/latest/library/observer.html - https://docs.ruby-lang.org/ja/latest/library/singleton.html - まさにActiveSupportの delegate っぽいのをピュアなRubyでも使えるように、みたいな感じで作ろうとしてました - コレクションオブジェクトに限らず、プリミティブな値をラップして便利に使える系のオブジェクト実装で言語問わず(問題、というほどでもないが面倒を軽減したくて)頭を悩ませる問題だが、最終的な解決策がRuby固有でちょっとしょんぼり - "頭を悩ませる問題" - 単にラップしただけだと、「XっぽいけどXのように使えないオブジェクト」になる - 「Xっぽいメソッド」を生やせば「XっぽくてXのように使えるオブジェクト」になるが、いちいちメソッドを以上する実装を手書きするのは面倒くさい - 継承すれば「Xっぽいのではなく特化したXそのもの」になるが、継承したメソッドを使った時に糸しない挙動がおきがち - ラップしたクラスのインスタンスではなく、親クラスのインスタンスが生成されたりする(p.217 `Array#+` の例など) - 再帰的ジェネリクス(みたいなもの) を使うと解決する? - `class Foo<T extends Foo<T>>` - いや落ち着いて考えると、「このforwardableみたいなのを作ればor探すという選択肢」は得られた & I/Fの参考例も手に入ったので、まあまあ収穫だった - 標準クラスを継承する例は gem だと hashie とかがある :eyes: - https://github.com/hashie/hashie/blob/v5.0.0/lib/hashie/hash.rb - https://github.com/hashie/hashie/blob/v5.0.0/lib/hashie/mash.rb - https://github.com/hashie/hashie/blob/v5.0.0/lib/hashie/dash.rb - https://github.com/hashie/hashie/blob/v5.0.0/lib/hashie/rash.rb - https://github.com/hashie/hashie/blob/v5.0.0/lib/hashie/trash.rb - 本文で言及されている問題、特に `Parts < Array` の実装で指摘されている問題をどう解決しているのか気になる - mash.rbななめ読みした限り、結構ぱわーぷれい(全部オーバーライド)に見える(^^; ### 疑問 ## 8.3_Partsを製造する_p.220_224 ### discord開始地点 https://discord.com/channels/432531367427964929/898843794101911622/1040969795815886848 ### 感想 > 変数がつくられたのはかなり前なので、忘れ てしまったかもしれません。 - p.220 - これらの変数がどう生成されているかはp.214を参照 > 次のコードは、単純な2次元配列で組み合わせを記述しています。 - p.220 - 2次元配列が見にくいから json にしたい...とかは、言語によって違う感覚かも。 - p.220のroad_configおよびmountain_configは単なる配列のネストなので、JSONにしてもほとんど変わらない気がする('が"になるぐらい?)。見にくいと感じるなら、主にインデント(対する慣れ)の関係かも。 - 配列0個目がキー、1個目が値というのがはっきりしてないのが不安になるところあります。インデント慣れもありそうですが。 - そこは全くもって同意するところで、ただ、そこはJSONフォーマットかどうかはあまり関係なくて、「順番に意味のあるArray」という構造の問題な気がします。:+1: :+1: - JSONではないプレーンなRubyでも、↓のようにすればここでの「見にくさ」はほとんど解消されるんじゃないかなと :+1::+1: ```ruby road_config = [ { key: 'chain', value: '10-speed' }, { key: 'tire_size', value: '23' }, # 略 ] ``` ### 疑問 > 過去にほかの言語での経験があるプログラマーであれば、この言葉を聞 いて身をすくめたくなるかもしれません。(...)Rubyでのファクトリーは単純なので、このように明確な意図を表す語句を避ける理由はどこにもありません。 - p.221 - どういう背景を想像しているんだろう…… - [トークパート] - RubyじゃなくてもFactoryは単純にできるというか、複雑に作れば複雑なFactoryになるし、単純に作れば単純なFactoryになるだけじゃね、感 :point_left: +1 - ruby固有の「単純に作れる仕組み」というのがあるのでしょうか? (最後は実装方法でいかようにもなりますが) :thinking_face: - また著者の暗黙的かつ一方的な前提ないし偏見が(ひっそりと)炸裂していそうな気配を感じる(^^; > p.220-221のFactoryの例 - road_configとmountain_config、`PartsFactory#build_road` とか `PartsFactory#build_mountain` みたいなメソッドの内部だけで使う値かと思ったら、Factory利用側が組み立てるのか…… - [トークパート](https://discord.com/channels/432531367427964929/898843794101911622/1040979815077003378) - メソッドの内部実装を簡単に済ませるために、内部的かつ局所的なデータはArrayとかHashで済ませて独自データ構造はつくらない、というぐらいならたまにやる - が、利用側に組み立てさせるなら、入力用のデータ構造(xxx_config)はStruct製でも良いから作っておきたい、という印象 - Struct(というか OpenStruct)が次で紹介されているのでそこにつなげるためにあえてHashのままにしている、というのもありそう :thinking_face: - p.223-224で確かにOpenStructは導入されていますが、これはPartを置き換えるために使っているわけで、入力値 = config は引き続きArrayですよね - 注入するものによって、メンバのキーの集合が異なる (Struct/OpenStruct のメンバが異なる) ことがあるので、引数の構造の固定化を避けようとすると ~~Hashになる~~ Struct/OpenStruct などを避けることになるのではないかしら < 「入力用のデータ構造(xxx_config)はStruct製でも良いから作っておきたい」 - > 注入するものによって、メンバのキーの集合が異なる (Struct/OpenStruct のメンバが異なる) - Partによって渡すパラメータが変わっているなら、そういう理由で「引数のリスト」みたいな感じにするのも理解できるのですが、最終形のp.224を見ると、結局生成されるPartは name, description, needs_spare で一定なんですよね…… - needs_spare はオプショナルですけど、単にオプショナルというだけならStructでもHashでもフォローできますし. - あれ、p.220でもp.224でも、road_configやmountain_configを構築するために使われているのは、HashではなくてArrayですよね……?「0番目がパーツの名前、1番目が説明、...」という意味を持った。 - あー引数はHashの配列のままで内部でOpenStructにしているのか :pray: - 「(Factory#buildの)引数はネストしたArrayで、内部でPartの生成をOpenStructにしている」ですね。数字キーで参照している & road_config や mountain_config の作成自体には特に言及無いので、引数も「ネストしたArray」のままでHashにはしていないと思います。 :bow: - 元々の意図を雑にすませずちゃんと書いておくと、↑の感想欄でも言及している「順番に意味のあるArray」をFactory#buildの引数にするのではなくて、せめて「Hashの配列」か「(config定義用に作られた)Structの配列」にしてほしい、という意図です - Factoryの利用側、たしかにロードバイク用のPartsやマウンテンバイク用のPartsを組み立てる方法は知らずに済んでるけど、代わりにロードバイク用のconfigやマウンテンバイク用のconfig(という名のArray)を知らないといけなくなってる - そうした別種の知識が必要になること自体は、レイヤをまたいだりするタイミングでどうしても発生することもあるので直ちに悪いわけではない - が、結局別種の知識は要求されるようになっているわけだから、もうちょいデータの構造が類推しやすいようにしておきたい。このケースなら、ArrayじゃなくてHashにするだけでも良いと思う。 - Arrayの要素順序に依存するI/Fは勘弁(使うなら完全に内部に閉じ込めて局所化してほしい) :sorena::+1: ## {読書範囲} ### discord開始地点 ### 感想 ### 疑問 --- # ふりかえり - 感想/次回の課題それぞれで5分 - 要望に応じて :okawari: ## 感想 https://hackmd.io/E0HZBN9OS9GBAtnsYXjDsw#%E6%84%9F%E6%83%B3 ### 気づいたこと、気になったこと - 前回とかもですが、この辺りは若干Rubyテクニックの話になりがちかも? :point_left: - forwadableは便利そうだが、Ruby固有の解決策なので、まあ参考程度 - 似たような仕組みを作ろうと思ったら、I/Fは参考にしても良いかも - 「生成に関する知識が散らばるので、Factoryにやらせよう」という展開は真っ当なのだけれど、実際に作られたFactoryに対する著者の意図がよくわからない - なぜネストした配列を引数に使ったんだろう…… - 「Rubyでのファクトリーは単純なので」 :thinking_face: - 実際に掲載されているサンプルコードも、特段Ruby固有のものがあるようには思えない - 強いて言えばFactoryが最終的に生成しようとしているオブジェクトのコンストラクタ自体も可変にしている点は、まあRubyだから楽にできる点かもしれない - が、今回のサンプルでそれをする意味があまりよくわからないし、そうしている理由も説明されていないし、動的型付け言語ならだいたい似たようなことできるのでは?感 - というか、全体的に、どこに向かおうとしているのかが示されない(検討されない)まま、手当たり次第に目の前のコードをこねくり回しているだけに見えて、なにを伝えようとしているのかが分かりにくくなってしまっている印象が拭えない。 :point_left: - 後半、力尽きた説... - 冒頭からなんだよなぁ……。(^^; :shushing_face: :zany_face: ### 疑問点 ### 仕事に活用してみたいこと ## 次回の課題 https://hackmd.io/E0HZBN9OS9GBAtnsYXjDsw#%E6%AC%A1%E5%9B%9E%E4%BB%A5%E9%99%8D%E3%81%AE%E8%AA%B2%E9%A1%8C [まとめ用mdに追記](https://hackmd.io/wfDfaf4nRQuPG1BYtcy_jA) # 次回 https://hackmd.io/vHUHir2lS1-BDuwreaAyBw?both