原カバンは鞄のお店ではありません。

Unityを使ったゲーム制作のあれこれを綴っていきます。

【Unity】Unity公式のサンプルプロジェクトから学ぶ設計思想:SOLID原則

Pixel Watch

勢いに乗って購入したものの使い道がいまいち思いつかない皆さんこんにちは。あまり使いこなせていないのですが、とりあえずオプションで購入したオレンジバンドが上下で色が微妙に違うのが気になります。

 

デザインパターン

システム開発の現場でオブジェクト指向のプログラミング言語が広く普及したことにより、それを利用した設計方針(デザインパターン)が提案されるようになりました。

デザインパターンとは一言で言うと、設計のノウハウ集で過去のエンジニアが使用してきた設計方法を分類別に纏めたものです。
最も有名なのはGoF(Gnag of Four)のデザインパターンで「生成」「構造」「振る舞い」の分類別に23のパターンが紹介されています。

一昔前まではこのデザインパターンはオブジェクト指向による設計の基礎知識となっていたので職場の研修や学校の授業等で履修した方も多いと思います。
しかし、最近の開発現場ではフレームワークでの開発が主流となっており、デザインパターンの知識はそこまで必須というわけではないようです。

Unityではオブジェクト指向言語のC#を使用するので、一般のシステム開発と同様にこのデザインパターンを利用してゲーム開発を行うことが出来ます。
まぁ、こちらも最近ではCorgi EngineやAdventure Creator、宴のようなフレームワーク系のツールを使って開発をする事ができる為、わざわざデザインパターンを考慮してゲーム内の処理を設計する必要はありませんが、基礎知識として、または品質や開発効率を高める為にデザインパターンを学んでおいても損はありません。

 

先日、Kan Kikuchiさんのブログで取り上げられていたのですが、このデザインパターンを学習するためにUnity公式でサンプルプロジェクトが公開されているようです。

kan-kikuchi.hatenablog.com

 

GitHubで公開されているので下のリンクからDownload ZIPからzipファイルをダウンロードして、UnityHubで解凍したプロジェクトを開いてください。

github.com

 

公開されているもの

このプロジェクトは1~12にナンバリングされたディレクトリと、プロジェクト内で汎用的に使用されるオブジェクト、フォント等が格納されたディレクトリ(General)とテンプレートスクリプトが格納されたディレクトリ(ScriptTempletes)で構成されています。

「1 SingleResponsibility」から「5 DependencyInversion」まではデザインパターンを学習する前に必要となる「SOLID原則」について、それぞれの原則に沿ってコーディングされたサンプルソースが格納されています。

 

一般的なデザインパターンとしては「6 Factory」から「12 MVP」のディレクトに各デザインパターンの実用例としてのデモシーンが格納されています。

■Factory

 

■Object Pool

 

■Singleton

 

■Command

 

■State

 

■Observer

 

■MVP

 

SOLID原則

先程も紹介したように、このプロジェクトの「1 SingleResponsibility」から「5 DependencyInversion」までのディレクトリには「SOLID原則」の各原則に沿ったサンプルソースがそれぞれ格納されています。

SOLID原則とは、オブジェクト指向プログラミング言語を使ってソフトウェアを構築する際に従うべき以下の5つの原則(ガイドライン)のことを指します
この原則を守ることで、ソフトウェアの拡張性や保守性を高めることを目的とします。

  • Single Responsibility Principle:単一責任の原則 
  • Open/closed principle:開放閉鎖の原則
  • Liskov substitution principle:リスコフの置換原則 
  • Interface segregation principle:インターフェース分離の原則 
  • Dependency inversion principle:依存性逆転の原則

上にあげた5つの原則のそれぞれの頭文字をとって「SOLID原則」と呼びます。サンプルプロジェクトではこの5原則のそれぞれの名前でディレクトリが作られています。

これらディレクトリ配下にはサンプルソースが格納されていますが、ソース名だけ見てもどれが何を示しているのか分からないと思うので、各原則と各サンプルソースについて軽く説明したいと思います。

 

1 SingleResponsibility:単一責任の原則 

1つのクラスが受け持つ責任は1つだけ」という原則です。
このディレクトリを開くと以下の5つのソースが格納されていることがわかります。

ここでは「Player」というクラスに「入力を受け取る」「移動する」「音声を鳴らす」という機能を持たせるケースが実装されており、
それぞれの機能が

  • 入力を受け取る → PlayerInputクラス
  • 移動する → PlayerMovementクラス
  • 音声を鳴らす → PlayerAudioクラス

それぞれのクラスに分けて分担するように実装されています。
「UnrefactoredPlayer」というクラスは前述の三機能をすべて受け持ったクラスで、単一責任の原則に反する例として作成されています。

 

2 OpenClosed:開放閉鎖の原則

拡張にはオープンで、変更にはクローズドであるべき」という原則です。

ここでは「AreaCalculator」クラスで円(Circle)と四角(Rectangle)それぞれの面積を計算する機能を実現しようとするケースについての実装例となっています。

AreaCalculatorクラス内で円の面積計算の関数と四角の面積計算の関数をそれぞれ実装するのは開放閉鎖の原則に反する実装、として紹介されており、開放閉鎖の原則に即した実装として以下のクラスが作成されています。

  • 面積を計算する関数(CalculateArea)を持った抽象クラス:Shape
  • Shapeを継承した円の面積計算クラス:Circle
  • Shapeを継承した四角の面積計算クラス:Rectangle

これよりAreaCalculatorの面積計算関数(GetArea)では抽象クラス(Shape)の面積計算関数(CalculateArea)をコールするだけとなります。
以後、例えば三角の面積計算の機能を追加したい場合は、Shapeを継承した三角の面積計算クラスを追加するだけで機能を拡張することができます。

 

3  LiskovSubstitution:リスコフの置換原則 

派生型(サブクラス)は、その基底型(スーパークラス)と置換可能でなければならない」という原則
この原則は、サブクラスとスーパークラスがエラーなしで同じ方法で使用できるように、一貫性を保つことを目的としています。

ここでは以下の二つのスーパークラスがあるケースについて

◇RoadVehicle:前進後退の機能インターフェース(IMovable)と左右旋回機能のインターフェース(ITurnable)を持つスーパークラス

◇RailVehicle:前進後退の機能インターフェース(IMovable)だけを持つスーパークラス

RailVehicleを継承したサブクラスのTrainは前進後退の機能のみを作り込みべきで、左右旋回を機能を持つとスーパークラスRailVehicleの提供機能を超える(=置き換え不可)ので原則に反します。

 

4 InterfaceSegregation:インターフェース分離の原則

「クライアントが使用しないメソッドへの依存を強制すべきではない」という原則
簡単に言うと無駄に不要なインターフェース作るな、という原則で、汎用的なインターフェースを作っていろんなクラスで使用するよりも、各クラスの要件に応じたインターフェースをそれぞれに特化して作るほうがよい、という考えです。

このディレクトリでは敵ユニットクラス(EnemyUnit)と爆発物クラス(ExplodingBarrel)について、EnemyUnitには、ダメージ処理用(IDamageable)、移動処理用(IMovable)、ステータス処理用(IUnitStats)のインターフェースを継承させいます。
一方、ExplodingBarrelにはダメージ処理用(IDamageable)と爆発処理用(IExplodable)のインターフェースを継承させて、それぞの要件に応じたインターフェースを使っています。

 

5 DependencyInversion:依存性逆転の原則

上位のモジュールは下位のモジュールに依存せず、どちらのモジュールも「抽象」に依存すべき」という原則
importやuseしてモジュールを使う側が、依存する側です。
依存する側がモジュールを直接呼び出すのではなく、抽象クラスやインターフェースを使い「抽象」に依存しましょうというのが、この原則です。

このディレクトリではドアクラス(Door)とトラップクラス(Trap)はどちらもISwitchableインターフェースを継承しており、Switchクラスではアクティブ化(ドアオープン/トラップ作動)と非アクティブ化(ドアクローズ/トラップ作動)の処理をISwitchableインターフェースを通して行います。
インターフェイスを導入することにより、上位モジュール(Switch)は下位モジュール(Door/Trap)を直接呼び出さず(依存せず)処理を実装することができます。

 

おわりに

ここまで5つの原則(SOLID原則)とそれを実装したサンプルソースについて概要を説明してきました。

SOLID原則はデザインパターンを学ぶための基礎となります。
この原則を頭の片隅に置いて、「6 Factory」以後のデモシーンを触ると理解度も変わってくるのではないでしょうか?

今回の記事ではSOLID原則までの紹介でしたが、次回以降は各デザインパターンとデモシーンでどのように実装されているのかについて説明していきたいと思います。

 

◇プライバシーポリシー

●個人情報の利用目的

当ブログでは、メールでのお問い合わせ、メールマガジンへの登録などの際に、名前(ハンドルネーム)、メールアドレス等の個人情報をご登録いただく場合がございます。

これらの個人情報は質問に対する回答や必要な情報を電子メールなどをでご連絡する場合に利用させていただくものであり、個人情報をご提供いただく際の目的以外では利用いたしません。

●個人情報の第三者への開示

当サイトでは、個人情報は適切に管理し、以下に該当する場合を除いて第三者に開示することはありません。

・本人のご了解がある場合
・法令等への協力のため、開示が必要となる場合

個人情報の開示、訂正、追加、削除、利用停止
ご本人からの個人データの開示、訂正、追加、削除、利用停止のご希望の場合には、ご本人であることを確認させていただいた上、速やかに対応させていただきます。

アクセス解析ツールについて

当サイトでは、Googleによるアクセス解析ツール「Googleアナリティクス」を利用しています。

このGoogleアナリティクスはトラフィックデータの収集のためにCookieを使用しています。このトラフィックデータは匿名で収集されており、個人を特定するものではありません。
この機能はCookieを無効にすることで収集を拒否することが出来ますので、お使いのブラウザの設定をご確認ください。

●免責事項

当サイトからリンクやバナーなどによって他のサイトに移動された場合、移動先サイトで提供される情報、サービス等について一切の責任を負いません。

当サイトのコンテンツ・情報につきまして、可能な限り正確な情報を掲載するよう努めておりますが、誤情報が入り込んだり、情報が古くなっていることもございます。

当サイトに掲載された内容によって生じた損害等の一切の責任を負いかねますのでご了承ください。

●プライバシーポリシーの変更について

当サイトは、個人情報に関して適用される日本の法令を遵守するとともに、本ポリシーの内容を適宜見直しその改善に努めます。

修正された最新のプライバシーポリシーは常に本ページにて開示されます。