よしたろうブログ

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

最近の学びを簡単にご紹介~SOLID・カプセル化・宣言型プログラミング・不変・共変・反変・ROA・REST・スキーマ駆動開発など~

初めに

本記事では

『最近の学びを簡単にご紹介』

ということで2年目なりたて Java エンジニアの自分がここ2ヶ月で学んだことを簡単に紹介したいなと思います。

お願い

  1. 2年目成り立てエンジニアが思う事です!
  2. 優しい目で見てね!
  3. あくまで、 個人の意見です!
  4. あえて断定的な物言いにしてるとこもあります!
  5. 議論のネタにでもなったら嬉しいです!

1. SOLID

最近の学びを簡単にご紹介2.007.jpeg


最近の学びを簡単にご紹介2.008.jpeg

有名な設計原則の頭文字をとったものが「SOLID」と呼ばれます。どの様な原則なのか無理筋を承知で一行で表してみたのが以下です。補足します。


最近の学びを簡単にご紹介2.009.jpeg

1-1. 単一責任原則:単位あたりにもたす目的は「一個のみ」

単位あたりといのは、機能・パッケージ・クラス・メソッドなど抽象度が異なるものを指します。どの抽象度でもこの意識が必要であると理解しています。目的が複数あるレイヤーが上位であればあるほど影響範囲が大きくなるので最初の設計が重要でしょう。

1-2. 開放閉鎖原則:仕様変更時に既存のコードを変えるな

どうやってやるんだ?ということで、これはインターフェースを仲介役として放り込み、その実装クラスとして新しいクラスを作成してそこで追加される機能を表現すればいい。

1-3. リスコフの置換原則:親で出来ることは子でも出来なかん

WriteReadClass という親を継承した子は WriteOnlyClass(もしくは、ReadOnlyClass)になってはいけないという事です。機能退化してはいけません。

1-4. インターフェイス分離原則:クライアントには必要な契約のみ

これは単一責任原則の延長の様に思えます。Fat なインターフェイスは slim にしなくてはいけない点と、契約による設計という観点でみると、インターフェイスの抽象メソッドが実装クラスに抽象メソッドの実装を強制させる点にも注目しなくてはいけないかなと。クライアントが必要としない契約の履行を行わせることは適切ではないので、そういった意味でもインターフェイスは必要な責務で切り分ける方が良いと思います。

1-5. 依存性逆転原則:方針を決める上位モジュールに依存する

抽象に依存しましょうという意味です。上位モジュールとは継承関係で例えるとスーパークラスに該当し、下位モジュールとはサブクラスが該当します。

アプリケーションの方針に基づく重要な判断やビジネスモデルを含み、アプリケーションの存在理由を決定づけているのは上位のモジュールです。それにもかかわらず、上位モジュールが下位モジュールに依存すると、下位モジュールの変更が直接上位モジュールに影響を与え、上位モジュールまで変更を強制されることになります。

2. カプセル化と宣言型プログラミング

最近の学びを簡単にご紹介2.010.jpeg


最近の学びを簡単にご紹介2.011.jpeg

OOPの三大要素でカプセル化がよく言われますが、特有でもなんでもなくごく自然な発想だな、というのが一つ。

またカプセル化を実現するためにアクセサメソッドを実装するのもセットで語られてますが間違いです。
getter がロジックや状態を不要に染み出させてしまうし、setter はオブジェクトを可変にしてしまい状態の変化をプログラム内で意識する必要はが出てきてしまいます。あっという間に認知可能な範囲を超えます。
getter に関しては、DB保存の際にオブジェクトに値を getter で取得せざるを得ない場合もあるかもしれないですが。


最近の学びを簡単にご紹介2.012.jpeg

また、命令型・手続き型のプログラミングではカプセル化を破壊するのでは?と思います。

カレーを作りたい、という時に命令型ではカレーの材料よこせ。こっちで作るから。という形です。対して宣言型では、カレーの材料を渡すからカレーを作ってくれないかな?という形です。

例には getter がないですがあると想定してください。 右上の例ではカプセル化されていてほしいデータが滲み出ています。「男性」の部分ですね。もしここが、他の管理方法になって「M」になった場合、この「男性」という記述箇所の数だけ修正を行う必要があります。カプセル化も破壊してますし、バグの可能性を仕込んでしまいます。
対して、右下の例は「この参照型変数の user は男性ですか?Ture か Flase を教えてください」と尋ねています。 実際の値は呼び出し側からは隠蔽されていますし、仕様の変更が入っても改修箇所は一箇所ですみます。

3. 不変・共変・反変

最近の学びを簡単にご紹介2.013.jpeg


最近の学びを簡単にご紹介2.014.jpeg

このスライドだけ見てると意味わからんですよね。主に引数の型がもつ継承関係がどの様なものなのかを表現する性質です。これは言語ことに異なり、Java での引数は通常は「不変」です。


最近の学びを簡単にご紹介2.015.jpeg

使い所は下記の例を示します。


最近の学びを簡単にご紹介2.016.jpeg

変数 list に String も int も add されてしまっています。これは実行時例外としてエラーが出ますが、実行しないと分からないでは怖いですよね。


最近の学びを簡単にご紹介2.017.jpeg

Java においてはジェネリクスを用いるとオブジェクトに add できる型を指定できます。ここで問題になるのが型の継承関係です。Java では全てのスーパークラスとして「Object」が存在しており、こちらは暗黙的に継承されています。

String を型して指定したい時に、継承元の Object 型も add 出来てしまうと上述の実行時例外を許容する形になります。これを防ぐために継承関係を無かったことにする「不変」という性質が適用されるのです。 <String>と記述すれば Object 型の代入はコンパイルエラーで弾かれる様になります。

4. ROAとREST

最近の学びを簡単にご紹介2.018.jpeg


最近の学びを簡単にご紹介2.019.jpeg

5. RESTとURI

最近の学びを簡単にご紹介2.020.jpeg


最近の学びを簡単にご紹介2.021.jpeg

REST は アーキテクチャでなく、一連の設計条件であり、「REST アーキテクチャ」というも のは存在しません。REST はきわめて一般的な設計条件であり、 特に Web と結び付いているわけでもないのです。

ROAは、それ自体を参照するに値するものを「リソース」として定義し、リソースを中心に考えるアーキテクチャのことです。リソースという概念、リソースはURIを持つ(URI)、リソースに対する操作(統一インターフェース)、リソースの表現形式(表現)、というようにリソース指向の中心はあくまでリソースになる。

Web API を RESTful にするとは、 ROA に沿って設計することにほかなりません。したがって、この ROAの概念と特徴について正確に理解しておく必要があります。ROA はRESTful APIを実装するうえで問題を解決してくれるアーキテクチャです。言い換えるとRESTful APIROA の実装ということになります(ROA に則り RESTful API は実装されている)。

6. HTTPメソッド

最近の学びを簡単にご紹介2.022.jpeg


最近の学びを簡単にご紹介2.023.jpeg

各HTTPメソッドのイメージです。言葉で語るよりも分かりやすいですね。


最近の学びを簡単にご紹介2.024.jpeg

RESTでは、POST は主に従属リソースを作成するために使用されます。従属リソースとは、ほかの「親」リソースに関連して存在するリソースです。たとえば、ブログは、各ブログをリソース(qiita.com/yoshitaro-yoyo) として提供し、 個々のブログエントリを従属リソース (qiita.com/yoshitaro-yoyo/items/bb8cc631276380b68c13) として提供します。

PUT でも従属リソースを作成することは可能です。Wikiはてなブログなどはその方法でリソースを作成できます。私ははてなブログにて記事を追加する際に自身で URI を指定してから投稿します。


最近の学びを簡単にご紹介2.025.jpeg

クライアントが自分の意図をサーバーにどのように伝えるのでしょうか?

サーバーは、特定のリクエストが何らかのデータを取得するためのリクエストであって、そのデータを削除したり別のデータで上書きしたりするためのリクエストではないことをどのように知るのでしょうか。

データの操作に関する情報をメソッド情報と呼びます。Webサービスでメソッド情報を伝える方法 の1つは、それをHTTPメソッドに含めることです。 HTTPメソッド名の大きな利点は、標準化されているため世界中で使用できることです。

7. 安全性と冪等性

最近の学びを簡単にご紹介2.026.jpeg


最近の学びを簡単にご紹介2.027.jpeg

安全性

GET または HEAD リクエストは、何らかのデータを読み取るためのリクエストであり、サーバーの状態を変更するためのリクエストではありません。クライアントは GET または HEAD リクエスト を10回繰り返すことができるが、そのリクエストを1回だけ実行するのも、1回も実行しないのも同じことです。GET リクエストを送信しても単にその表現を取得するだけです。クライアントは、 未知の URI に GET リクエストや HEAD リクエストを送信しても害がないことに安心できるでしょう。

冪等性

これは数学に由来する概念です。数学におけるべき等演算とは、1回適用しても複数回適用しても結果が同じもののことです。数字に0を掛けることは冪等であり、4×0x0 × 0は4×0と同じです。つまり、1つのリクエストの実行が同じリクエストを繰り返し実行することと同じである場合、リソースでの操作は冪等であると言えます。

リソースの状態を相対的に変更する表現を PUT することをクライアントに許可してはいけません。リソースがそのリソース状態の一部として数値を維持する場合、クライアントは PUT を使用して、その値を4、0、または-50に設定することは許可しても問題ありませんが、その値を1ずつインクリメントすることはで許可すべきではありません。 初期値が0の場合「値を4に設定する」という PUT リクエストを2回送信しても値は4のままですが、 初期値が0の場合「値を1ずつインクリメントする」とい うPUTリクエストを2回送信すると、値は1ではなく2になります。これは冪等ではありません。


最近の学びを簡単にご紹介2.028.jpeg

・GETと HEAD は読み取り専用で、あるリソースを何回GETしても同じ結果は同じです。またリソースの状態を変化させないので「安全」であり「冪等」です

・POSTはリソースの状態を変化させ、同じリクエストでもリソースの状態変化を予測できません。POST した数だけ新しいリソース作成されるため「安全」でも「冪等」でも有りません。

・PUT と DELETE はリソースの状態を変化させますので「安全」では有りません。それぞれリソースの更新と削除を行います。ですが、たとえば一度削除したリソースに再度削除のリクエストを送ってもリソースが削除されている状態は変わりません。同じリクエストを複数回送信しても結果は変わらないので「冪等」です。


最近の学びを簡単にご紹介2.029.jpeg

Web API の利用側からするとこの様に誤用された HTTPメソッドは想定外の結果を生み出しかねません。

設計を見直したり、エンドポイントを設定する際は「安全性」と「冪等性」の点を考慮に入れるべきでしょう。

8. スキーマ駆動開発とAPI設計

最近の学びを簡単にご紹介2.030.jpeg


最近の学びを簡単にご紹介2.031.jpeg

スキーマ駆動開発とは、「APIスキーマをまず初めに定義し、その定義をもとにサーバー側(API)とクライアント側(画面)の開発を並行して進めること」を指します。

サーバ・クライアントのそれぞれの開発は定義したスキーマをベースに進めていきます。APIスキーマを最初に定義することによって、両者の間でAPIの仕様のズレを防ぐことができます。また、仕様に変更がある場合はスキーマの定義を修正し、それぞれの開発に反映させていきます。


最近の学びを簡単にご紹介2.032.jpeg


最近の学びを簡単にご紹介2.033.jpeg

8-1. スキーマ活用シーン

スキーマは Web API開発のあらゆるシーンで活用されます。

❐ サーバの実装 当然ですが、Web APIサーバはスキーマに沿って開発されます。明文化されたスキーマが存在するので、サーバの実装を迷いなく進めることができます。

❐ サーバ実装のテスト スキーマを読み込んで自動化テストに組み込むことで実装が定義されたスキーマを満たしているか 確認できます。自動化テストを定常的に実行する CI (Continuous Integration、継続的インテグレーション) 環境を整備すれば、もしコードに変更を加えたときにスキーマから外れる挙動になってしまってもすぐに気付くことができます。

❐ ドキュメントの生成 スキーマはプログラムから処理しやすい形式で書 かれるのでそれ自体は必ずしも人間にとって読みや すいものではありませんが、そこから人間向けのド キュメントを自動生成することでクライアントの実装に役立てることができます。

❐ スタブサーバの活用 ドキュメントがあっても、実際にHTTPレスポンスを返してくれる Web API サーバがなければクライアントの実装は進めにくいものです。スキーマを読み込んで動作するスタプサーバがあれば、スキーマを用意できたと同時にクライアントの実装に活用できます。

9. プロダクトや組織の文脈を把握する

最近の学びを簡単にご紹介2.034.jpeg


最近の学びを簡単にご紹介2.035.jpeg

11月でやっとエンジニア一年目を卒業しました。にも関わらず参画したプロダクトは5つつです。   激烈炎上してる案件のテスターとして参加したのが最初です。

この中で重要だと思ったのは、以下に現場に馴染むのかでした。   設計が終わりコーディングをすることも勿論重要ですが、前提としてプロダクトの誕生背景や誰が必要としているのか?なんのために作っているのか?何故、何を作るのか?課題に対するソリューションとしてプロダクトが存在しているわけですからこの文脈を理解せずにコーディング作業を行なっても大体手戻りが発生しています。また、多機能との連携や異常系などにも関心を持つためにもあってはいけないことや無くてはならいことを事前に理解しておくべきです。

テキストコミニケーションが主流になっているなかで、間を読む・空気を読む・行間を読むとった暗黙の了解は通じません。PMやPdMは限られた時間でタスクを回し、大きな責任を持っています。こちらか認識の擦り合わせのためのアクションを行わないとをそのずれは埋まりません。最悪リリース前に発覚とかもあるあるじゃないでしょうか?

管理者が周知しないから悪い、と安全な場所で愚痴るのは簡単で卑怯です。上司も人間で、完璧じゃありません。こちらが上司をマネジメントするという考えも必要です。問題があると思うのであれば、こちらか適切なタイミングで、適切な内容で、適切な伝え方で、認識してもらう様に動かなくてはいけません。でなければ、早期のパフォーマンス発揮はできません。

自分はあまりの見込みがいい方ではないので、まずは現場にハマりにいくことを重要視してます。チームの中での当たり前の単語一つとっても相手と自分の間で大きなギャップが存在していますのでそのままにして、チームが想定している結果を出せるとは思えないからです。

10. まじアウトプットは大事!!

最近の学びを簡単にご紹介2.036.jpeg


最近の学びを簡単にご紹介2.037.jpeg

私は現在、週に一回アウトプット用の記事を書くことを縛りにしています。8月から続けていて、ここは縛ってる訳ではないのですが結果的に大体2万字くらいのボリュームで技術記事を投稿しています。めっちゃ大変です。


最近の学びを簡単にご紹介2.038.jpeg

いい事たくさんあります。上記で書いている様に本当に勉強になるし、評価してもらえます。   自分で主催している勉強会で約30人くらいの方に手をあげてもらって確認しましたが、実際にアウトプットしている方は自分を含めて3人程度しかいませんでした。ハードルが高い分、差別化も計れるので転職活動にも非常に有益です。詳細は書きませんが、めちゃくちゃ実感しています。ただ、知識偏向型で実務経験が釣り合わない状態で過大評価されそうでビクビクしています(笑)

最後に

といった感じで、9月から10月の二ヶ月間で学んだことを駆け足で紹介してきました。
如何でしょうか?何かツッコミどころなどあればコメントいただけると泣いて喜びます。