LoginSignup
2
5

More than 5 years have passed since last update.

ソシャゲのサーバーサイドでif文と結合度を減らせるかもしれない設計

Posted at

※本記事はあくまでゲームの内部設計に関する考察です。実際のゲームとは関係ありません。

よくある光景

よくあるゲームのプレゼントボックスを考えます。

アイテム名 取得理由 受け取り期限
薬草 × 10 ダンジョン4のクリア報酬です あと100日
魔石 4日目のログインボーナスです あと100日
10000ゴールド 6日目のログインボーナスです あと99日
薬草 × 5 ダンジョン3のクリア報酬です あと99日
5000ゴールド 3日目のログインボーナスです あと96日
戦士キータ 1日目のログインボーナスです あと93日

このDBのスキーマはこんな感じでしょうか。
スクリーンショット 2016-12-02 18.53.56.png

item_type によってアイテムやお金等の区別をします。
仮にこうなっているものとします。

item_type 内容
1 お金
2 アイテム
3 キャラ
4 魔石

これを受け取る時(プレゼントボックスから手持ちに移す時)のコードはこうなるでしょうか。

def acquire_present(user, present):
  if present.item_type == 1:
    """お金を受け取る処理"""
  elif present.item_type == 2:
   """アイテムを受け取る処理"""
  elif present.item_type == 3:
    """キャラを受け取る処理"""
  ...
  else:
     raise Exception()

if文地獄です。
しかもそれぞれの受け取り処理で色々やる可能性大です(手持ち確認とかログとか)。
そうなると acquire_present関数とそれぞれのデータクラスでがっちり結合度が高まります。

さらに今後のゲームの拡張によってitem_typeが増えればさらに長くなります。
長いif文と密結合はメンテナンス性がよろしくないのでなんとかしたいです。

受け取り処理の分離

そこでプレゼントの受け取り処理を委譲するクラスを考えます。

class AqruireDalegatorBase(object):
  """受け取り処理委譲ベースクラス"""
  @classmethod
  def acquire(user, present):
     raise NotImplementedError()

受け取りの共通インターフェースを定義しました。
継承してそれぞれの受け取り処理委譲クラスを実装します。

class MoneyAqruireDalegator(AqruireDalegatorBase):
 """お金の受け取り処理委譲クラス"""
  @classmethod
  def acquire(user, present):
     user.add_money(present.item_quantity)

class ItemAqruireDalegator(AqruireDalegatorBase):
 """アイテムの受け取り処理委譲クラス"""
  @classmethod
  def acquire(user, present):
     user.add_item(present.item_id, present.item_quantity)

... その他に続く

実際に受け取り関数に組み込んでみます。

def acquire_present(user, present):
  if present.type == 1:
    MoneyAqruireDalegator.acquire(user, present)
  elif present.type == 2:
    ItemAqruireDalegator.acquire(user, present)
  elif ...

それぞれのタイプごとの受け取るロジックを切り出したことで疎結合感が出てきました!

拡張も楽になるようにしてみる

でもまだif文が長くなるしタイプが追加される度にこの関数を修正しないといけないので、

DELEGATOR_MAP = {
  1: MoneyAqruireDalegator,
  2: ItemAqruireDalegator,
  ...
}

とタイプと委譲クラスのマッピングをします。

def acquire_present(user, present):
  delegator_class = DELEGATOR.get(present.item_type)
  delegator_class.acquire_present(user, present)

かなり短くなりました!
そしてこれでタイプが増えた場合でも、

  1. AqruireDalegatorBaseを継承した受け取り委譲クラスを実装する
  2. DELEGATOR_MAPに追加する

で拡張可能です。acquire_present 関数を修正する必要はありません!
テストも追加した委譲クラス分だけすればOKです!

まとめ(?)

長いif文は不具合調査時も拡張時も厄介なので疎結合な感じになっていると現在と未来の人達に優しいですね!

(これはファサードパターンなのだろうか・・・?若干DIっぽい気もする)

2
5
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
2
5