🚀 ニフティ’s Notion

【オブジェクト指向2024 #1】設計の目的・オブジェクト指向とは?

目次

初めに

ソフトウェア開発の究極の目標は開発のコスト・時間を最小にすること

そのためには…

  • 高速に開発ができること
  • 変更がしやすいこと
オブジェクト指向までの道のり
  1. 複雑性の増大
    コンピュータ科学が発展し、ソフトウェアシステムの複雑性が増大するにつれて、従来の手続き型プログラミングでは、大規模なプロジェクトの開発やメンテナンスが困難なった。プログラムの構造化やモジュール化が進めらたが、それでもなお、プログラム全体の把握や変更が容易ではなかった。
  2. 抽象化の需要
    ソフトウェア開発者は、プログラムの複雑さを管理する方法を求めた。このとき、抽象化という概念が重要視された。抽象化は、システムの詳細を隠し、重要な機能やデータに焦点を当てることで、システムを理解しやすくする手法。
  3. オブジェクト指向の台頭
    このような背景の下で、 オブジェクト指向プログラミング (OOP)が台頭した。OOPは、データと操作をオブジェクトとして表現し、オブジェクト間の相互作用を重視する。これにより、プログラムの構造をより自然な形でモデル化し、抽象化と再利用性を高めることができた。
  4. 設計思想の確立
    OOPの普及とともに、いくつかの設計思想が確立された。これには、カプセル化、継承、ポリモーフィズム、抽象化などが含まれる。これらの設計思想は、プログラムの柔軟性、拡張性、保守性を向上させることができる。
  5. プログラミング言語の進化
    OOPが広く受け入れられると、多くのプログラミング言語がOOPの機能をサポートするようになった。C++、Java、Pythonなどの言語は、OOPの概念を取り入れ、多くの開発者によって広く使用されるようになった。

このような経緯を経て、OOPはソフトウェア開発の主要なパラダイムの一つとして定着した。OOPは、ソフトウェアの複雑性を管理し、再利用性を高めるための強力な手段として、現在も広く利用されている。

設計(アーキテクチャ)の目的

ソフトウェアアーキテクチャの目的は、変更容易性を高めるためにある

  • 変更に必要な労力が少なく、システムのライフサイクル全体で低く保たれているなら、その設計は優れている
  • 変更を加えていけばいくほど、コードが汚くなっていき、変更が難しくなっていく
開発・保守の中で起きるソフトウェアの変更を楽にしたいという気持ちによって生まれるのが設計
設計を行わない弊害
  • 仕様変更でコードを変更すると別の場所でバグが発生した
    • 処理と処理が依存し合っているため、変更を加えると別の処理が壊れる
  • 別の場所でバグが発生しないかどうか、変更の影響範囲を調べていたら日が暮れてしまった
    • 処理を追うのにどこのコードを読んでいけばいいかわかりづらい
  • バグを埋め込みやすい
    • 変更による影響範囲が予測しにくい、思わぬところにバグを埋め込んでしまう
  • 簡単な仕様変更・バグ修正に何日もかかった
    • どこの処理を変えればいいのかその調査に時間がかかったりする
何が悪かったのか?
image block
  • 優れた・クリーンな・うまく設計されたコードの重要性を理解していない
    • 〆切まで時間がないので、とりあえず動くものを作ろうという気持ちになる
      • 「とりあえずモノを完成させて、後でコードを綺麗にするか」と言って、結局時間がなくやらない
    • 競合に追いつくためには機能を追加し続けなければならない
      • 今、この間にも競合はどんどん先を行っている
      • 後からコードを綺麗にする時間なんてない
  • 崩壊したコードを書けば短期的には開発速度が上がると信じている
    • 最初はそれでいいかもしれないが…
    • どんどん変更が難しくなり、長期的には開発速度が下がる

ソフトウェアアーキテクチャ

ソフトウェアアーキテクチャのモチベーション
  • 以下が達成されるようなルールを考え、変更しやすいシステムを作る
    • ソフトウェアが持つ複雑性を軽減させる
      • 業務で扱うようなシステムは仕様がややこしい
      • 問題を細かく分割して、人間にとって考えやすくする
    • 分割した要素同士の関係性をコントロールする
      • 要素の関係性を整理して、変更の影響度を小さくする
アーキテクチャの導入
  • コーディングに入る前に、アーキテクチャを考えていく
  • アーキテクチャを1から考える必要はなく、先人たちが考えたパターンを参考にしていく
    • システムやサービスの規模・特性や、エンジニアの成熟度、アーキテクチャ自体の学習難易度、使用言語の特性などを考えて、適切なパターンを選択する
  • 自分達のシステムに合わせてカスタマイズしていく
    • 考えたアーキテクチャは図で可視化して、他人と議論しやすくする
  • 最初から正解に辿り着こうと思わず、開発を進めていく中で理想的なアーキテクチャに近づけていく
    • 後からアーキテクチャを変更できるように作っておくことも重要
      アーキテクチャには正解も間違いもない。ただトレードオフがあるだけだ。(Neal Ford)
  • アーキテクチャの例
    • クリーンアーキテクチャ
    • ヘキサゴナルアーキテクチャ
    • MVCアーキテクチャ
image block
引用:
graph LR
  Model --> Controller
	Controller --> Model
  View --> Controller
	Controller --> View
MVCアーキテクチャ

アーキテクチャの共通思想
  • どのパターンも共通しているのは、(1)責務を考え分離する、(2)依存関係を考えるということになる
責務を考える
  • 対象物の果たすべき役割がなんなのかを考え、余計な処理を入れないようにしたり、ロジックがバラバラにならないようにする
  • 責務を考える対象
    • 関数・クラス・変数・構造体
    • 機能ごとに関連する関数・クラス・変数・構造体などをまとめたグループ
      • 例えばレイヤードアーキテクチャでは以下のようなグループに分けるとされている
        UI層 表示に関する要素を入れる HTTP
        Application層 Domain層やInfrastructure層と協働して、ユースケースを実現する要素を入れる ユーザー登録、認証など
        Domain層 システムの根幹となる概念をモデリングした要素を入れる ユーザー、IDに関する情報など
        Infrastructure層 技術詳細に関する要素を入れる RDBやAPIへのアクセスなど

        image block

  • 各要素・グループは自分の責務だけを全うすることに集中し、余計なことはしない
    • 例えば、画面に文字列を表示する関数にファイルを削除する副作用を入れる
    • 思わぬバグにつながる
  • 責務を分離するメリット
    • 改修をする際に、どこを変更すればいいか見通しが立てやすい
    • エンジニア同士で、どこに手を入れればいいか共通認識を生みやすくなり、開発が効率化できる
    • ソフトウェアの複雑度を減らすことができる
依存関係を考える
  • 依存関係を考えるというのは、変更の影響範囲が小さくなるように、グループ同士の関係性を検討しようということ
    • 変更を楽にするためには依存関係をコントロールする必要がある
    • ソフトウェアは一つのもので完結することは少ない
      • 必ず依存したり、依存されたりする
    • 依存関係は矢印で表現する
      • AはBに依存している(AにとってはBが必要)
        • AやBは関数、変数、クラスなどのコード上に出てくる要素だと思ってください
        graph LR 
          A --> B
    • 依存の矢印の逆は変更の影響範囲を示している
      • Bが変更されるとAを変更する必要が出てくることを示している
      • コードを修正すると別の部分が壊れることがあるが、このせい
      graph LR 
        A -- 依存 --> B
        B -. 変更の影響 .-> A
      graph LR
        A -- 依存 --> B
        B -- 依存 --> C
        C -- 依存 --> D
        D -. 変更の影響 .-> C
        C -. 変更の影響 .-> B
        B -. 変更の影響 .-> A
      

  • 依存関係が複雑にならないように考えていく
    • 依存関係によって、要素が破壊・変更された際の影響範囲が変わってくる
    • 人間にとって依存関係を追いやすい構造にする
  • 依存関係を考えるメリット
    • 変更した際の影響範囲を小さくなるようにすると、安心して変更できるようになる
👍
アーキテクチャのパターンはさまざまありますが、責務と依存関係についてどう取り扱っていくかが変わっていきます
アーキテクチャ設計の正解はなく、最善だと思われる物を選択していきましょう

オブジェクト指向

システム全体を、モノに見立てた「オブジェクト」と呼ばれる構成単位の組み合わせとして捉え、システムの振る舞いをオブジェクト間の相互作用としてとらえる考え方。

複雑なシステム記述、巨大なライブラリ(特に部品間で緊密で複雑な相互関係を持つもの)の記述においては、オブジェクト指向の考え方は必須である。

オブジェクト指向のメリット
  • 複数人で並行作業するチーム開発や業務で取り扱う大規模システムにメリットが現れやすい
👥
分業しやすい
  • オブジェクト単位で作業ができるため複数人での並行作業がしやすい。
  • システムの規模が大きくなる程、プログラムの実装は膨大になるため、複数人で開発作業を並行して行う必要がある。
🌀
拡張性が高い
  • ビジネスの加速に併せて、すぐに拡張・新機能が追加できる。
再利用性が高い
  • オブジェクトを使いまわせる。
  • 同じ処理を何度も書く必要がなくて楽
    • ただし、本質的には別のものを共通化する危険性があるのでやりすぎは良くない
👀
可読性が高い
  • 業務では自分が書いてないコードを読んで改修していく
🧑‍🏭
保守性が高い
  • 不具合が発生した場合の問題箇所の特定もしやすい。
主要な概念(以降解説)
カプセル化
👨‍👨‍👦
継承
😸
ポリモーフィズム

まとめ

設計(アーキテクチャ)の目的は変更容易なシステムを作ることにある
オブジェクト指向とは、システムをオブジェクトと呼ばれる構成単位を組み合わせとして捉え、オブジェクト間が相互作用することによって振る舞いが生まれるとする考え方
分業化しやすい、拡張性が高いなどのメリットによってチーム開発で採用されやすい。
ポリモーフィズム、カプセル化、継承などが主要な概念。


次へ: 📄 【オブジェクト指向2024 #2】クラスとインスタンス