Decorator パターンでキメラを作る

公開:2021/01/23
更新:2021/01/23
5 min読了の目安(約4500字TECH技術記事

はじめに

初めまして、久しぶりに記事を書きます(╹◡╹)
お気に入りの喫茶店で Decorator パターンを知ったので、その備忘録的な感じで作ります。
私はプライベートでゲームを作っているのですが、このデザインパターンを駆使すれば、組み合わせ無限大な敵を作れるか???となったのでちょっと書いてみたいと思いました。

なので、カジュアルな感じなのですがご了承ください🙇‍♂️

Decorator パターンとは

超簡単に表現するとある Component に対して Decoration(装飾) するという感じです。

イメージ

  1. スポンジケーキがある
  2. そこに生クリームとイチゴをトッピングするとショートケーキになるし、チョコソースでコーディングするとチョコレートケーキになる

以上!!!!🤣

キメラを作ろう

というわけで早速キメラ Enemy みたいなのを作ろうかなと思います(╹◡╹)

ベースクラスを作成

Enemy Class を作ります。めちゃシンプルです!

class Enemy {
public:
    Enemy(const int &life, const int &power);
    virtual ~Enemy() {}
    
    virtual void move() = 0;
    
    int getLife() const;
    int getPower() const;    
protected:
    int mLife;
    int mPower;
private:
    Enemy();
    Enemy(const Enemy& rhs);
    Enemy& operator=(const Enemy& rhs);
};
#include "Enemy.hpp"

Enemy::Enemy(const int &life, const int &power) : mLife(life), mPower(power) {
    
}

int Enemy::getLife() const {
    return mLife;
}

int Enemy::getPower() const {
    return mPower;
}

こちらが Decorator パターンの Component になります。
Enemy はインターフェースとして定義されており、 Decorator パターンでは全てこの型で扱います。

Decorator を作成

次に Enemy を強化する Decorator を作っていきたいと思います(╹◡╹)

Base の Decorator

Base になる Decorator を作ります。

class EnemyDecorator : public Enemy {
public:
    EnemyDecorator(Enemy* component);
    virtual ~EnemyDecorator() {}
protected:
    Enemy* mComponent;
private:
    EnemyDecorator();
    EnemyDecorator(const EnemyDecorator& rhs);
    EnemyDecorator& operator=(const EnemyDecorator& rhs);
};
#include "EnemyDecorator.hpp"

EnemyDecorator::EnemyDecorator(Enemy* component) : Enemy(
    component->getLife(), 
    component->getPower()
), mComponent(component) {
    
}

Decorator のベースになります。
メンバに Enemy の component を用意しておきコンストラクタでは component から lifepower を与えています。

具象の Decorator を実装

EnemyDecorator を使って具体的な Decorator を作っていきましょう。
今回は 2 つ用意します。

  • FlyEnemyDecorator
#include "EnemyDecorator.hpp"

class FlyEnemyDecorator : public EnemyDecorator {
public:
    FlyEnemyDecorator(Enemy* component);
    virtual void move() override;
private:
    FlyEnemyDecorator();
    FlyEnemyDecorator(const FlyEnemyDecorator& rhs);
    FlyEnemyDecorator& operator=(const FlyEnemyDecorator& rhs);
};
#include "FlyEnemyDecorator.hpp"

FlyEnemyDecorator::FlyEnemyDecorator(Enemy* component) : EnemyDecorator(component) {
    
}

void FlyEnemyDecorator::move() {
    mComponent->move();
    LOG("Fly Move.\n");
}
  • SwimEnemyDecorator
#include "EnemyDecorator.hpp"

class SwimEnemyDecorator : public EnemyDecorator {
public:
    SwimEnemyDecorator(Enemy* component);
    virtual void move() override;
private:
    SwimEnemyDecorator();
    SwimEnemyDecorator(const SwimEnemyDecorator& rhs);
    SwimEnemyDecorator& operator=(const SwimEnemyDecorator& rhs);
};
#include "SwimEnemyDecorator.hpp"

SwimEnemyDecorator::SwimEnemyDecorator(Enemy* component) : EnemyDecorator(component) {
    
}

void SwimEnemyDecorator::move() {
    mComponent->move();
    LOG("Swim Move.\n");
}

飛んだり泳いだりさせましょう!

具象の Enemy Class

具象の Enemy Class である NormalEnemy をテキトーに用意する。

#include "Enemy.hpp"

class NormalEnemy : public Enemy {
public:
    NormalEnemy(const int &life, const int &power);
    virtual void move() override;
private:
    NormalEnemy();
    NormalEnemy(const NormalEnemy& rhs);
    NormalEnemy& operator=(const NormalEnemy& rhs);
};
#include "NormalEnemy.hpp"

NormalEnemy::NormalEnemy(const int &life, const int &power) : Enemy(life, power) {
    
}

void NormalEnemy::move() {
    LOG("Normal Move.\n");
}

こいつに羽を生やしたり、ヒレを付けたりするわけですね。

キメラ爆誕

いよいよキメラを誕生させます(╹◡╹)

Enemy* enemy = new NormalEnemy(10, 1);
Enemy* flyEnemyDecorator = new FlyEnemyDecorator(enemy);
Enemy* flyAndSwimEnemy = new SwimEnemyDecorator(flyEnemyDecorator);
    
flyAndSwimEnemy->move();

ログは下記のような感じになります。

Normal Move.
Fly Move.
Swim Move.

おめでとうございます。合成獣の完成です(╹◡╹)
空飛んだり泳いだりしてますねw
最強!!!?

Decorator パターンのメリット

こんな感じで Decorator パターンを使うと Enemy の強化を柔軟に作ることができます。
普通は継承を駆使して Enemy を作っていくとは思いますが、 Decorator パターンだとちょっとした組み合わせで作ることができるかもしれません!

メリットは以下のような感じかなと思います。

  • 拡張がやりやすい。拡張用のクラスを作る手間が減る
  • 基底クラスにそのまま拡張機能を持たせると、基底クラスが変更されると全体に影響が出てしまうが、Decorator パターンは影響が少なくて済む

※設計は良く考えないといけないのは変わりません!

Decorator パターンだけで作るというよりは継承も組み合わせていいとこ取りしていきたいなと個人的には考えていたりします。

おしまい

今回は Decorator パターンを紹介しました。勿論、ゲーム以外にも活躍しそうなデザインパターンです!
まあ、このデザインパターンを知らなくても自然に作られることは結構ありそうではあるのですが・・・😅

今回はこんな感じですね。
皆さんの開発の助力になれば幸いです(╹◡╹)