検索ガイド -Search Guide-

単語と単語を空白で区切ることで AND 検索になります。
例: python デコレータ ('python' と 'デコレータ' 両方を含む記事を検索します)
単語の前に '-' を付けることで NOT 検索になります。
例: python -デコレータ ('python' は含むが 'デコレータ' は含まない記事を検索します)
" (ダブルクオート) で語句を囲むことで 完全一致検索になります。
例: "python data" 実装 ('python data' と '実装' 両方を含む記事を検索します。'python data 実装' の検索とは異なります。)
当サイトのドメイン名は " getwebtips.net " です。
トップレベルドメインは .net であり、他の .com / .shop といったトップレベルドメインのサイトとは一切関係ありません。
practical_python_design_patterns

Practical Python Design Patterns - Python で学ぶデザインパターン: The Prototype Pattern - Part. 4「総決算: いよいよプロトタイプパターン実装の実践」の巻 投稿一覧へ戻る

Published 2022年5月22日13:45 by T.Tsuyoshi

SUPPORT UKRAINE

- Your indifference to the act of cruelty can thrive rogue nations like Russia -

Practical Python Design Patterns - The Prototype Pattern 編

Using What We Have Learned in Our Project
(総決算: いよいよプロトタイプパターン実装の実践)

プロトタイプパターンの本質を一言で表せば、オブジェクトの完全なコピーを返す clone() メソッドである、ということになります。
プロトタイプパターン実装時における骨組みとなるのは、clone() 抽象メソッドを定義する抽象ベースクラスを宣言することです。そして、渡された引数の値によって作成すべきオブジェクトの種類を変化させるクラス (polymorphic constructor capability を備えたクラス: 我々の RTS の例では各ユニットクラスがこれに相当します - つまり、指定されたレベルによって属性値の異なるユニットオブジェクトを作成します) はこの抽象ベースクラスから自らを派生させ clone() メソッドを実装する必要があります。そして、ユニット作成を依頼するクライアントコードでは、希望するユニットのクラス名をハードコードしてその __new__() を呼び出すことで1つ1つユニットを生産するのではなく、プロトタイプオブジェクトの clone() メソッドを呼び出すことで生産を行います。
まず骨組みとなる抽象ベースクラスを定義し、サブクラスで実装すべき clone() を宣言します:
prototype_1.py
from abc import ABCMeta, abstractmethod

class Prototype(metaclass=ABCMeta):
@abstractmethod
def clone(self):
pass
抽象ベースクラスから派生させたユニットクラスを定義します。このクラスでは渡された引数 (level) によって作成すべきユニットの属性値を変化させる (異なるファイルを読み込む) ことで多様性 (polymorphic) を実現しています。また、抽象ベースクラスで定義されている clone() 抽象メソッドを実装しなければいけません:
rts_prototype.py
from copy import deepcopy
from collections import namedtuple
from prototype_1 import Prototype

UnitParam = namedtuple('UnitParam', ['life',
'speed',
'attack_power',
'attack_range',
'weapon'])

def unit_description(type: str, params: UnitParam) -> str:
return (f'Type: {type}\n'
f'Life: {params.life}\n'
f'Speed: {params.speed}\n'
f'Attack Power: {params.attack_power}\n'
f'Attack Range: {params.attack_range}\n'
f'Weapon : {params.weapon}')


class Knight(Prototype):
def __init__(self, level):
self.unit_type = 'Knight'

filename = f'{self.unit_type.lower()}_{level}.dat'
with open(filename, 'r', encoding='utf-8') as param_file:
self.unit_params = UnitParam._make([p.strip() for p in param_file])

def __str__(self):
return unit_description(self.unit_type, self.unit_params)

def clone(self):
return deepcopy(self)


class Archer(Prototype):
def __init__(self, level):
self.unit_type = 'Archer'

filename = f'{self.unit_type.lower()}_{level}.dat'
with open(filename, 'r', encoding='utf-8') as param_file:
self.unit_params = UnitParam._make([p.strip() for p in param_file])

def __str__(self):
return unit_description(self.unit_type, self.unit_params)

def clone(self):
return deepcopy(self)


class Barracks:
def __init__(self):
self.units = {
'knight': {
1: Knight(1),
2: Knight(2)
},
'archer': {
1: Archer(1),
2: Archer(2)
},
}

def build_unit(self, unit_type, level):
return self.units[unit_type][level].clone()

if __name__ == '__main__':
barracks = Barracks()
knight1 = barracks.build_unit('knight', 1)
archer1 = barracks.build_unit('archer', 2)
print(f'[Knight: level 1]\n{knight1}')
print(f'[Archer: level 2]\n{archer1}')
Barracks クラスでは、自分のところで生成可能なユニットのオブジェクトをあらかじめ units 辞書として保持しておきます。これによって、ユニットの生産が依頼された場合、1回1回該当ユニットの対象レベルのファイルから属性値を読み込む必要がなくなり、保持している該当オブジェクトの clone() メソッドを呼び出すだけになります。
[Knight: level 1]
Type: Knight
Life: 400
Speed: 5
Attack Power: 3
Attack Range: 1
Weapon : short sword
[Archer: level 2]
Type: Archer
Life: 200
Speed: 7
Attack Power: 3
Attack Range: 10
Weapon : long bow
素晴らしい!!自分自身の RTS ゲーム作成の第1段階を終了したばかりか、非常に有用なデザインパターンをしっかりと理解しましたね!!

この投稿をメールでシェアする

0 comments

コメントはまだありません。

コメントを追加する(不適切と思われるコメントは削除する場合があります)