よしたろうブログ

駆動設計・アーキテクチャ・変更容易性とかの話が好きです。

勉強会でLTしてきました!in 大阪

初めに

九月末に参加した勉強会でLTしたので、LT風景の紹介や使用した資料の簡単な補足説明でもしようかと思います。

https://kokokara.connpass.com/event/262504/?utm_campaign=event_participate_to_follower&utm_source=notifications&utm_medium=twitter

kokokara.connpass.com

1. LTの風景

2. LT内容紹介

LT資料リンク

speakerdeck.com

2-1. お知らせとお願い


このスライドは短い時間で分かり易い様にするのと刺さりやすい様にするために結構尖った書き方というか断定的な物言いをあえてしてましたね。理詰めでしっかり説明しようとすると、長いしうまいことまとめるのもむずいんですよんね.......

2-2. 最近の学び(目次)

2-3. いろんな原則


設計原則で有名な二つの原則集ですが、分析・設計・実装といった各フェーズでも非常に有用な原則だと思います。名著「アジャイルソフトウェア開発の奥義」にて単一責任原則は以下の様に語られています。

クラスを変更する理由は1つ以上存在してはならない。 2つの役割を別々のクラスに分けるのが、なぜそんなに重要だったのだろう? 役割が複数あれば、 その1つ1つが変更理由になってしまうからだ。 仕様要求が変わると、クラスの役割が変化するの で、どのように変化したのかを見れば、変更部分が浮き彫りになる。 しかし、クラスが複数の役割を 背負ってしまうと、クラスを変更する理由も複数になってしまい。 変更部分がぼやけてしまう。 クラスが複数の役割を背負っているような場合、それらの役割は結合してしまう。その結果、ある 役割が変更を受けると、そのクラスが担っている他の役割も影響を受け、不具合が生じる可能性があ もろい設計を生み出してしまう。

単一責任の原則 (SRP) は最もシンプルな原則のひとつであるが、正しく適用することが最も難し 原則の1つでもある。 我々はともすれば複数の役割を結合してしまいがちなのだ。 結合している役 を見つけそれらを分離する作業は、 ソフトウェア設計の本質である。

私は上記の部分を関心ごとの分離として捉え、この原則に忠実であればカプセル化というものは自然な発想だと理解しています。開放閉鎖原則では、機能拡張時などの既存コード変更については、別のクラスに責任を持たせる事で既存コードを拡張から分離している様に見えますし、インターフェイス分離原則は単一責任原則に追随する考え方の様に捉えられます。GRASPの情報エキスパート・高凝集・疎結合もそのまま単一責任原則の考え方ではないでしょうか?

これらから鑑みると、ほとんどの原則の上位概念として単一責任原則が存在している様に感じています。

そして、継承は改めて危ない道具だなと思います。リスクが多いし、リスクヘッジの手段が面倒だなぁと思います。


DRYは自分も勘違いしてましたが、コードの重複に対して盲目的に適用するものではないなと気付きました。

コードとしての粒度で見た時に、二つの処理が例え共通化できそうな処理だったとしても、それが全く別の抽象化された目的のためのロジックを表現するコードならば、共通化してはいけないというのがDRYだと理解しています。コードの重複が主眼なのではなく、情報や目的の重複をしたあかんでということであり、解りやすいのが template methodパターンですね。目的が同じものを役割を抽象可し、不変部分を抽出し、結果として共通化しています。

2-4. 抽象化


抽象可を繰り返す事で、そのインターフェイスの裏側を気にしないで良くなっています。 Javaがメモリの管理を抽象化してくれたのがガベージコレクションで、実際にどう管理しているのか知りませんが、私はその役割を知っています。こうやって叩き続きけているキーボードだって裏側では電子機器の組み合わせと信号伝達とその判断ロジックなどが文字を画面上に表現していますがどうやっているのかなんて全く知りません。ですが、役割をしっているだけで目的は達成できています。

こういった観点で見ればこのスライドに書かれていることもそれ以外のことも抽象化の産物であることが解ります。

マジックナンバーも、プロダクト内でその数字が何の役割をするのか適切な名付けをすることで実際になんの数字か分からなくても役割がわかる様になります。

2-5. インターフェイスについて


ネットで調べると以下の様なことが説明されてるのが多んですよね

  • interface を implements するとクラスにメソッドの実装を強制できるのがメリット
  • メソッドの実装を強制するなどの前提やルール

使い方にすぎない説明が多いんですよね。本質的な説明がなく非常にやきもきしました。 ネット検索して上位に出てくるページは参考になりません。Javaの構文の話がメインですし、使いどころが書いていません。 ルールを覚えるのは重要ですが、もっとも重要なのは抽象化の考え方です。

契約の考え方からすると実装する責任があるから当然の前提ですが、それらで関係が作られたクラス間で、どのような抽象的効果を発揮しているかが、実際は重要な点でした。

インターフェイスの使い所は以下ではないでしょうか?

  • ①クラス階層・クラス間の関係性を無視して、振る舞い・性質をオブジェクトに付与・定義したい
  • ②ひとつのオブジェクトに複数の振る舞い・性質を付与するために、多重継承(型)を行いたい
  • ③呼び出し側から具象クラスをカプセル化(データ抽象化し実装隠蔽)したい場合(多態性の前提)
  • 開放閉鎖原則や抽象依存逆転原則を実現したい時
  • ⑤抽象化によって二者間の結合を疎にする(疎結合
  • カプセル化されたモジュール(情報隠蔽)やパッケージとクライアント間での接続口(仲介役)
  • ポリモーフィズムとかラムダ式とか

過去記事にこの辺りは色々と書き散らかしました。

yoshitaro-yoyo.hatenablog.com

2-6. デザインパターン


以下の過去記事で解説しています。

  1. デザインパターンとは何か?
    1-1. 引用:「Java言語で学ぶデザインパターン入門」
    1-2. GoF本内にあるデザインパターン23種類のカテゴライズ
    1-3. ソフトウェアパターンの中の一つのカテゴリがデザインパターン
    1-4. ソフトウェアパターンには他に何があるのか?
    1-5. デザインパターンの最小構成
    1-6. GoFの歴史
  2. GoFデザインパターンの教え
    2-1. 継承ではなく委譲
    2-2. 具象ではなくインターフェイス(抽象)に対してプログラミング
    2-3. オブジェクト指向プログラミングでの本質的な再利用性の提示
    2-4. 「変更されない部分」と「変更される部分」の分離
  3. 1995年登場時のGoFデザインパターンの立ち位置【リファクタリングと自動テストがなかった時代】
  4. GoFデザインパターンの立ち位置【リファクタリングと自動テストが登場してから】
  5. 15年後の2009年に行われた GoFによるGoFデザインパターンの再定義(重要)
    5-1. 新規追加パターンについて
    5-1-1 これまでの分類表
    5-1-2. 2009年以降の分類表
    5-2. 現在においてもデザインパターンは有用か?
  6. UMLについて
    6-1. クラス図
    6-2. クラスの関連
    6-3. シーケンス図
    6-3-1. クラス、インスタンス、オブジェクトの違い
  7. プログラムを完成品としてみない

2-7. クラス指向なオブジェクト指向


クラス指向という概念がなかったですね。え!そうか!クラスがないって考えもあるよな!ってなりました。 そこから、クラスとはそもそも何の課題に対するソリューションなのか?みたいなところから考え始めました。

カプセル化とかオブジェクト指向特有のものかとてっきり思い込んでましたが、全くそんなことはなかったですね。

手続き型プログラミングって非常に認知的負荷の少ない記述方法の様に感じます。自然なアプローチではあるのですが、やはり限界がありますと。

そのことは下記に記事に詳しく書いてあります。

small-is-beautiful.net

またこここから、クラスがないというGo言語にも興味を持つ様になりました。

2-8. 関数型プログラミング(FP)


副作用はあかんあかんいうけど、副作用おこさないでどうやって状態を表現すんねん?っておもってましたけど、やっぱり副作用こそ本質やんな、という考えに落ち着きました。

副作用とは

①同じ様に呼び出しても同じ結果が返ってくるとは限らない処理のこと ②ある処理において、状態を参照あるいは操作することで、次回以降の結果にまで影響を与える効果のことを副作用と呼ぶ

画面入力やファイル入力などプログラミングには状態を持たせることが不可欠なので、以下の思想が重要になる。

「副作用を持たない部分」(純粋関数)と「副作用を持つ部分」を分離することが重要。

  • 純粋関数
    • 外部の状態に依存することがなく、関数の結果は入力のみに依存する
    • 副作用を引き起こさない
    • 関数自身のスコープの外にある値に影響を及ぼさない
  • 不純な関数
    • 外部の状態に依存し、関数の結果に影響を与える
    • 副作用を引き起こす
  • 副作用が引き起こすこと
    • 認知的負荷の上昇・バグの原因になりやすい
    • デバッグしないと実際の処理がわかりにくい
    • 処理が独立していないのでテストが大変

「状態=値が変わりうる変数」を暗黙的に参照・操作することになるので、影響範囲の特定や絞り込みに時間がかかりますね。また、その不純関数の動作は外部に依存するので、その関数の結果を知りたい・回収したいの場合はその外部を把握・理解する必要があります。

2-9. Go言語


Goは言語デザインとして正しく使うのが難しい、乱用するとプログラムを無意味に複雑にしてしまう機能が排除されています(高度な型システム・継承・Generics・例外・イベントモデルによる並行処理など)。 一方でプログラミング言語を習得することのゴールは、それを使って何か大規模なソフトウェアをチームで作ることであるのだと思います。 チームで大規模なソフトウェアを開発することはそれ自体が非常に難しいことです。そのゴールのためにプログラミング言語自体はシンプルに保ち、それ以外の部分により多くの労力をさけるようにするというのは僕は正しい判断だと思います。

引用元:なぜGo言語 (golang) はよい言語なのか・Goでプログラムを書くべき理由

www.yunabe.jp

参考:Frequently Asked Questions (FAQ)

go.dev

2-10. 契約・防御的プログラミング


以下の過去記事にて解説しています。

  • トランザクションスクリプト方式が「防御的プログラミング」
  • ドメインモデル方式が「契約プログラミング

    増田さんのお話ではクラス設計において変更容易性を実現するには「ドメインモデル方式」選択すべきというお話でした。

    本記事では、実装フェーズにおいて、各クラスがどのレイヤー以降なのか?によって、防御的・契約どちらのプログラミングを行うべきか異なる。という話をしていきます。

    どのレイヤー以降なのか?これは外部と内部で区切ります。外部とは内部から呼び出されたWeb API やユーザのフォーム画面に面しているレイヤーであり、外部に面しているレイヤーは防御的なレイヤーです。内部とは防御的レイヤーより内側のレイヤーとしてます。

    ❐結論
    外部に面してるレイヤーは防御的プログラムで有るべき
    それ以降のレイヤーでは契約プログラミングでもいい(設計やドメインによるがDDDでは契約プログラミング
    防御的プログラミングと契約プログラミングの根本的な違いとしては それぞれ「猜疑心」と「相互信頼」に基づく思想だという点かと思います。

yoshitaro-yoyo.hatenablog.com

2-11. テスト発想


理想論ですけど、実装始める前に洗い出しておくべきではないかと思っています。

上流工程で不具合見つけた修正コスト1だとすると、システムテストで40かかる リリース100かかっちゃう つまり後工程なればなるほど多大なコストを要する。設計書の段階で潰すってすごい大事。

設計段階で先行投資という形や投資をしておくと後で手戻りがと抑えられる。

仕様書に書かれているものをそのまま実装・テストすると、非機能要件・異常系が書かれてない実装されてない テストが抜けてしまう。仕様書に書いてあろうとなかろうとユーザーのしてほしいほしくないをちゃんと洗い出しておく必要がある。これがテストの発想かなと思います

  • 欠陥が少ない
  • 予算内
  • 要求条件を満たす
  • 使いやすい

これらが顧客にとって品質が良いということ

たとえ網羅できてなかったとしても、基準だけでも設けておけば、何に留意すべきかという点でも役立つでしょうし、手戻りも少なくすることに貢献できると思います。

以下はこちらについてLTした際のスライド資料です。

www.slideshare.net

2-12. ウォーターフォールについて


ウォーターフォールは変わらないものに向いてるんだろうなと思います。例えば、決済機能とかですよね。逆にもれなく実装できるのではないかと思います。ただ、ユーザーの要望・市場の変化が多様化しサイクルが短縮化していく現状でウォーターフォールの様な手戻りが想定されない開発手法では、対応できないのは自明の理かと思います。フェーズが進む過程・周囲の環境変化などで浮き彫りになる課題や要件というものに対して、対応が必要なプロダクトであればアジャイルが向いているのではないかと思っています。ビジネス環境の変化する速度に、システム開発の速度がついていけなくてはならないからです。


また、ウォーターフォールの原型となった論文においては、フェーズが進むごとに前段階に戻れなくなるという現在のウォータフォールの形を批判していました。逆に、図の様な形でフェーズ間を行き来する開発手法を理想としています。また、現実はうまくいかないといった話もあります。

ウォーターフォール、初出とされている論文はRoyceによる「Managing the Development of Large Software Systems」です。画像はこちらの論文からの引用です。

http://www-scf.usc.edu/~csci201/lectures/Lecture11/royce1970.pdf

2-13. 何事もトレードオフ


至極当然な話ですが、しばしば道具・手段を目的としてまう現象が見受けられます。これは、業界職種関係なく見ますね。企業の営利活動や労働の究極の目的は「顧客に対する価値提供」だと思います。顧客の中にしか答えはないし、顧客自身もその答えを知らない。その答えを作り出し提供することで報酬が発生するのです。技術はそのための道具でしかなく、トンカチだけで家は立てれないのです。

そして、道具の選定とは、目的を達成するために、最善もしくは現状の中でベターなものであり、その組み合わせの中で出来ること・出来ないこと、安全なこと・危険なことを考慮すそれらが引き起こす問題の対応策まで用意する必要がある。私はそんな設計者・実装者になりたいです。加えて、ユーザー・所属組織・自身への利益の最大化を実現できる人材になりたいですね。茨の道かもだけど、目標は高くあるべきでしょう。

2-14. ご清聴ありがとうございました

3. LTに対する感想

  • ありがとうございます!
    とても勉強になりました
    「私ももっと勉強しないと、」と刺激になりました!

  • よしたろうさんらしさが出ていて、かつすごく濃密な内容でした!

  • 最高に熱いLTありがとうございました!!!
    すっごく良い話、チョー刺さりました
    また聞きたいですー!
    是非是非お願いします

  • これだけinputして消化できるのすごいです〜
    アーキテクチャ大事ですね!

  • エンジニア歴1年未満でここまで考えながら開発をされているのは本当にすごいなと思いました。
    また僕にもいろいろ教えてください笑

  • これまでに勉強された内容が広く含まれていて話について行くことが大変でしたがすごい刺激になりました!
    ありがとうございました!

  • 言語化と自分の思想をしっかり持てているところがすごいなと思いました。ありがとうございました。

  • 大変勉強になり刺激をいただきました、ありがとうございます!!アウトプットがやっぱ最強なんですね、実際それを継続されてらっしゃるというのがすごいです:

  • LTありがとうございました!最高でした!
    エンジニア1年目でこんなに色々学んでいるの
    めっちゃくちゃすごいと思います。
    僕も勉強頑張ります!

  • ウォーターフォールがしんどいという気持ち分かります。。私もアジャイル派です笑 またいろいろ教えていただきたいです!

  • めちゃくちゃ勉強になりました!
    自分ももっと学ぶべきことが多いなと感じました!!刺激になります!

  • よしたろうさんつよすぎて病みました。

  • 知識量すごすぎる!
    アーキテクト初学者なので、めちゃくちゃ面白かったです!
    基礎を圧倒的に詰め込む方がコスパ良いという意見めちゃくちゃ共感しました

  • ありがとうございました。
    自分ももっと知識を深めたいと思いました
    ウォーターフォールの話は私も共感できました!!

  • とても勉強になりました。
    改めて基礎的な知識を深めていきたいと思いました。

  • お話がとても聞き取りやすくてよかったです

  • インプットもアウトプットもすごいです。
    とても刺激になりました〜

3-1. 結論

めちゃくちゃ嬉しい

その他、LT後に色々とご質問頂けて応答させて頂けました。自身のアウトプットに感想やご質問を頂けるのは嬉しいものです。何かしらの刺激や価値を生み出せたのかな、と。今後もそういったLTができる様にしていきたいと思います。

今まで十数回LTしてきましたが、自分の話したいことを話すより、聞いている人の気持ちを考慮した構成の方が受けがいいです。当たり前ですけど、できるかどうかはまた別なので今後も頑張ろう。

LTさせてくださった運営の皆様と聞いてくださった皆様、ありがとうございました。

参加イベントのコンパスページリンクです。

kokokara.connpass.com