0
2

More than 3 years have passed since last update.

【入門】iOS アプリ開発 #4【アーキテクチャの設計】

Last updated at Posted at 2020-08-13

アーキテクチャの設計

今回はパックマンのゲームを構築するにあたり、全体のアーキテクチャを設計する。

仕様書から画面モードは4つあり、それぞれのモード内にはキャラクタがいて移動処理などがある。これらを1つ1つのオブジェクトとして管理し扱っていく。

Image2.png

設計方針

各画面モードをオブジェクトとして扱い、簡単に切り替え操作したい。

attractMode.startSequence()
// ・・・アトラクトモード実行中・・・

// アトラクトモードからクレジットモードへ切り替える
attractMode.stopSequence()
creditMode.startSequence()
// ・・・クレジットモード実行中・・・

こんな感じ。

またイベント・メッセージを画面モード内のオブジェクトにも簡単に通知したい。

attractMode.sendEvent(message: .Update, parameter: [16])
attractMode.sendEvent(message: .Touch, parameter: [x,y])

イベント・メッセージは表示更新タイミング(Update)やタッチイベントなどで、画面モードが無効のものには通知しないようにする。

まとめると、以下のようなオブジェクトの階層構造が作れるようにする。

Image4.png

これらの基本コンポーネントのクラスを作成する。

CbObject class(基底クラス)

/// Based object class
class CbObject {

    /// Kind of Message ID to handled events in object.
    enum EnMessage: Int {
        case None
        case Update
        case Timer
        case Touch
        case Swipe
        case Accel
    }

    /// When it is true, object can handle events.
    var enabled: Bool = true

    /// For accessing parent objects.
    private var parent: CbObject?

    /// Initialize self without parent object
    init() {
        parent = nil
    }

    /// Initialize self with parent object
    /// - Parameter object: Parent object
    init(binding object: CbObject) {
        parent = object
        parent?.bind(self)
    }

    /// Bind self to a specified object
    /// - Parameter object: Object to bind self.
    func bind( _ object: CbObject) {
        // TO DO: override
        // (This is pure virtual method.)
    }

    // Send event messages
    /// - Parameters:
    ///   - id: Message ID
    ///   - values: Parameters of message.
    func sendEvent(message id: EnMessage, parameter values: [Int]) {
        receiveEvent(sender: self, message: id, parameter: values)
    }

    /// Handler called by sendEvent method to receive events
    /// - Parameters:
    ///   - sender: Message sender
    ///   - id: Message ID
    ///   - values: Parameters of message
    func receiveEvent(sender: CbObject, message: EnMessage, parameter values: [Int]) {
        guard enabled else { return }
        if message == .Update {
            update(interval: values[0])
        } else {
            handleEvent(sender: sender, message: message, parameter: values)
        }
    }

    /// Event handler
    /// - Parameters:
    ///   - sender: Message sender
    ///   - id: Message ID
    ///   - values: Parameters of message
    func handleEvent(sender: CbObject, message: EnMessage, parameter values: [Int]) {
        // TO DO: override
        // (This is pure virtual method.)
    }

    /// Update handler
    /// - Parameter interval: Interval time(ms) to update.
    func update(interval: Int) {
        // TO DO: override
        // (This is pure virtual method.)
    }

}

基底クラスの CbObject は、init で上位のオブジェクトとの関連付けを行える。sendEventメソッドでメッセージを送信でき、メッセージが、.Update なら、updateハンドラが呼ばれ、それ以外は、handleEvent が呼ばれる。ハンドラは派生クラスでオーバーライドして扱う。プロパティの enabled を false にすると、ハンドラは呼ばれなくなる。

CbContainer class(派生コンテナ・クラス)

/// Container class that bind objects.
class CbContainer : CbObject {

    private var objects: [CbObject] = []

    /// Bind self to a specified object
    /// - Parameter object: Object to bind self.
    override func bind( _ object: CbObject) {
        objects.append(object)
    }

    /// Handler called by sendEvent method to receive events
    /// It sends messages to all contained object.
    /// - Parameters:
    ///   - sender: Message sender
    ///   - id: Message ID
    ///   - values: Parameters of message
    override func receiveEvent(sender: CbObject, message: EnMessage, parameter values: [Int]) {
        guard enabled else { return }

        super.receiveEvent(sender: sender, message: message, parameter: values)

        for t in objects {
            t.receiveEvent(sender: self, message: message, parameter: values)
        }
    }
}

派生コンテナ・クラスの CbContainer は、sendEventメソッドで受け取ったメッセージを、関連付けしている全てのオブジェクトに送信する。同様にプロパティの enabled を false にすると、イベント・ハンドラは呼ばれなくなる。

使用方法

class MyObject: CbObject {
    override func update(interval: Int) {
        print("Update!")
    }
}

func test() {
    root  = CbContainer()
    node1 = CbContainer(binding: root)
    node2 = MyObject(binding: node1)

    root.sendEvent(message: .Update, parameter: [16])
}

root に送ったメッセージが末端の node2 のオブジェクトに届くようになる。

まとめ

全体のオブジェクトの管理、イベント・メッセージの仕組みを設計した。

高々100行程度のコードだが、最初にこれらを決めておかないと、後々、苦労しそうな気がする。

次は、CbContainerクラスを継承して画面モード・クラスを作成していく。

次の記事

【入門】iOS アプリ開発 #5【シーケンスの設計】

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2