設計原則をデザインパターンで考える

はじめに

こちらは Voicy Advent Calendar 2021 13日目の記事です。

はじめまして、株式会社Voicyでバックエンドエンジニアをしているなだまるです。

みなさん、設計してますか? 「設計」っていうと難しいこと考えがちですが、普遍的に言われていることを組み合わせてみてみると、あ、こんなもんかと思ったりします。

今回はそんなアプリケーションの設計に関するお話をしてみます。

設計の原則

広く知られる設計の原則として、SOLID原則があります。

この原則の目的は主に以下のような性質を持つ中間レベルのソフトウェア構造を作ることです。

  • 変更に強いこと
  • 理解しやすいこと
  • コンポーネントの基盤として、多くのソフトウェア・システムを利用できること

SOLID原則は以下の各原則の頭文字をとってSOLIDです。 今回は各原則について詳しくは記述しませんが、軽く紹介までに。

  • Single Responsibility Principle(単一責任の原則)
  • Open-Closed Principle(オープン・クローズドの原則)
  • Liskov Substitution Principle(リスコフの置換原則)
  • Interface Segregation Principle(インターフェイス分離の原則)
  • Dependency Inversion Principle (依存関係逆転の原則)

これらだけ読んでも「ほうほう」くらいはなるんですが、具体イメージは湧きづらいですよね。

デザインパターンと合わせて考える

ここで言うデザインパターンGoFデザインパターンを指します。 デザインパターンは23種あり、よく出会う問題とそれに対処する良い設計としてまとめらた指針のようなものです。 こちらの各パターンの紹介も今回は割愛しますが、前半で紹介した原則と合わせて理解することで、どういったところに肝があるのかを掴みやすくなります。

本記事はこの部分を扱います。 説明にクラスと言う言葉を使いますが、便宜上そうしているだけでモジュール等の一定の実装のかたまりの意味合いで捉えてください。

単一責任の原則とFacadeパターン

単一責任の原則はざっくり言うと「モジュールを変更する理由はひとつにすべきだ」というヤツで、ユースケースのアクターが異なるのであればコードは分割しましょうねということです。

Facadeパターンはモジュールの窓口を担う、システムの外側に対するシンプルなAPIを提供するパターンで、クラス図にすると以下のようなものです。

f:id:natacon:20211213223702p:plain
Facadeパターン

Facade パターン - Wikipedia

実装コードを扱うクラスがあったとして、それぞれアクターが異なるメソッドが実装されているとします。(例が雑でごめんなさい) implement()は実装者、review()はレビューする人、release()はシステム管理者が行うとします。 このままだとどういうユースケースの変更であったとしてもCodeクラスを変更することになります。

f:id:natacon:20211213223845p:plain

アクターが異なることがわかっているので、アクターごとにクラスを分けてます。

f:id:natacon:20211213223956p:plain

分けたら分けたで、利用者側からは3つのクラスを意識して上手に使ってあげないといけなくなります。 それは使いにくいので窓口としてFacadeクラスを配置します。

f:id:natacon:20211213224014p:plain

こうすることにより、実装者にかかわる変更はImplementerのみを変更すれば良くなり他のアクターのケースは影響を受けません(関数のシグネチャーが変更される場合などはFacadeも変更対象になります)。

このように、Facadeパターンを通じて、ユースケースのアクターごとに変更すべきクラスを分けることができ、かつ理解もしやすくすることができます。

依存関係逆転の原則とAbstract Factoryパターン

依存関係逆転の原則は、ソースコードの依存関係を抽象だけを参照するようにして、その名の通り依存関係を逆転させるヤツです。抽象クラスやインターフェイスに依存しましょうってことですね。

Abstract Factoryパターンは直訳すると「抽象的な工場」で、これだけだと意味が分かりづらいですが、工場で生産する部品の具体には触れず、インターフェイスだけを使って部品を組み立て製品にまとめようぜというヤツです。APIを定義するイメージですね。クラス図で表すと以下のようになります。

f:id:natacon:20211213224045p:plain
Abstract Factoryパターン
Abstract Factory パターン - Wikipedia

上側がAPIになっていて、下側に詳細の実装が配置されています。

ClientはProduct1のインターフェイス経由でConcreteProduct1を使用します。 Product1はAbstractFactoryインターフェイス経由ででProduct1を生成します。 このとき、AbstractFactoryの実装はConcreteFactoryで実装されていて、この実装がConcreteProduct1のインスタンスを戻す形になっている。

真ん中にアーキテクチャの境界を意味するものとして一本線を引いてみると見通しが良くなります。

f:id:natacon:20211213224137p:plain

この線を横切る依存性はすべて具象側から抽象側に向かっています。 この曲線を横切る処理の流れは、依存性とは逆向きになるため「依存関係逆転」と呼ばれてるんですね。

最後に

今回は設計原則をデザイパターンで考えてみました。

概念をざっくり理解したものについて、違う観点・粒度を合わせ絵考えてみると理解が深くなったりします。 こういう点と点を"意識的に"つなげにいく活動を今後も続けたいですね。

さて次はAndroidエンジニアの @horitamon さんです!お楽しみに!