オブジェクト指向の方法で在庫を処理するにはどうすればよいですか?


7

私はオブジェクト指向のアプローチに従ってプレーヤーのインベントリを処理するための最良の方法を考えています。

たとえば、剣と斧は2つの異なるクラスで、どちらも武器から継承しています。武器とポーションもアイテムから継承する異なるクラスです。

理想的には、在庫はアイテムのコンテナーとして実装されますが、それはいくつかの理由で実行できません。したがって、アイテム(スマート)ポインターのコンテナーである必要があります。newとdeleteの呼び出しでコードを制限したくないので、ctor / dtorでニュース/削除するItemWrapperクラスにラップします。

したがって、コードは次のようになります

class Sword;
class ItemWrapper;

//Prepare the long sword.
const Sword longSword(...params...);

//Declare the inv
vector<ItemWrapper> inv;

 //Add it to inventory
inv.push_back(longSword);  //compiles if ItemWraper(const Sword&) is defined.

このアプローチは、コードの明快さと書きやすさの点で私が思いつくことができる最高のものでした。問題は、ItemWrapperがItemから継承するリーフクラスごとに1つのコンストラクターを必要とすることです(これはたくさんあります)。

これが私の初めての試みなので、知りたいのですが、正しい方向に進んでいますか?またはそれを行うためのより良い方法がある場合(おそらく、非常に多くのコンストラクタを回避する方法)?


これがどのように実装されるのが一番いいのか本当に興味があります。
Tili

回答:


9

あなたが今やっていることは、論理的には思えるかもしれませんが、後で後悔することがあるかもしれません。

アイテムの数が12を超えると、アイテムの種類ごとに独自のクラスを作成するのが難しくなることに気付くでしょう。

私の在庫管理システムでは、アイテムの種類が異なります。代わりに、一連のプロパティを持つ「記述子」クラスを使用します(名前と値のペア、連想コンテナーに格納されます[C ++ではstd :: mapを使用する可能性があります])。

プロパティゲッターは、マップから適切なタイプにプロパティをキャストするテンプレート関数です。

私がアイテムタイプを区別する方法は、ItemTypeプロパティ、通常はWEAPON、ARMORなどのエントリを持つある種の列挙型を持つことです。他のプロパティには、損傷範囲、レンダリングに使用する画像への参照、名前の説明などが含まれます。

現在、これらの記述子はアイテムインスタンス用ではありません。彼らは全体としてアイテムタイプを記述します。アイテムインスタンスには、記述子への何らかの参照と、アイテムインスタンスに必要な値のプロパティの同様のマップが含まれます(たとえば、個々の耐久性など)。このようにして、同じ情報を何度も繰り返すことを避けます。結局のところ、1つの剣は他の剣とほぼ同じですが、使い古しに近いかもしれません。

コンストラクター呼び出しを回避する限り、ファクトリーメソッドをお勧めします。


4
あなたは私より少し遠いかもしれません。VorpalBladeOfPowerクラスとPunyIronHandaxeクラスを作成する理由はないと思います。しかし、武器や鎧などのタイプを分けると、悲しみを和らげることができます。コンパイラがヘルスポーションとヘルムを二重に使用することをプレイヤーに禁止します。
dhasenan

アイテムを1つのクラスに統合する点について理解しました。プロパティを連想コンテナに格納する理由がわかりません。私が正しく理解している場合(そして私が間違っているかどうかを伝える場合)、クラスfloat weight内の変数の代わりItemに、コンテナーに数値を保持します(おそらく「重み」に関連付けられています)。わずかなメモリと引き換えに、小さなパフォーマンスヒット(コンテナーからの読み取り)を取り、より多くのコードを記述しているようです。
Malabarba

もちろん、テクスチャとサウンドはすでにメモリ管理されていると想定しています(常にそうであるはずです)。その場合、(数百のItemインスタンスがある場合でも)Itemインスタンスごとに数float秒、enums、およびints をメモリ管理する利点はそれほど重要ではないと思います。
Malabarba

各記述子に何千ものプロパティがある場合、パフォーマンスヒットになります。一般に、これらのほとんどのプロパティは20未満です。
PlayDeezGames 2012年

申し訳ありませんが、「パフォーマンスヒット」は焦点ではありませんでした。ごくわずかであったり、存在していなかったりすることは承知しています。私のポイントは、より多くのコードを記述し、1または2 MBのメモリ(数百のアイテムを想定)を節約しているようです。
Malabarba

7

たとえば、剣と斧は2つの異なるクラスで、どちらも武器から継承しています。武器とポーションもアイテムから継承する異なるクラスです。

私はこのようにはしません。剣と斧は主に武器としての基本的な行動ではなく、データが異なります。同様にアイテム。私はこの継承の過剰使用を避け、まず自分自身をWeaponandに制限することから始めItemます(これらを単一のクラスに折りたたむこともできますが、それは別の議論です)。他に何もなければ、これはで見られるコンストラクタの爆発を軽減しますItemWrapper

したがって、アイテム(スマート)ポインターのコンテナーである必要があります。newとdeleteの呼び出しでコードを制限したくないので、ctor / dtorでニュース/削除するItemWrapperクラスにラップします。

自分でロールするのではなく、Boost スマートポインターを検討してください。それらはBoostの残りの部分からかなり隔離されています(したがって、何らかの理由でBoostの残りのきちんとした機能を利用したくない場合は、依存関係が大量にありません)。これらはC ++ 11の新しい標準ライブラリの一部として採用されているため、アップグレードの問題は発生しません。

これが私の初めての試みなので、知りたいのですが、正しい方向に進んでいますか?またはそれを行うためのより良い方法がある場合(おそらく、非常に多くのコンストラクタを回避する方法)?

上記のように、私はこれを継承しないように努め、独自のソリューションをロールする代わりに、確立されたサードパーティのソリューションを使用することを検討します。これらの手順だけで、発見している(またはすぐに発見する)メンテナンスの複雑さの多くを取り除くことができます。

とは言っても、別のアプローチはこのように見ることです。これItemWrapperは、実装の詳細問題の応急処置です。それ自体は、典型的な意味での実際の「オブジェクト」を表すものではありません。だから、自動電話newdelete電話をかける場所があるというあなたの問題を解決するより良い類似性があるかもしれないものについて考えてください。

ほとんどの在庫では、(一部の)アイテムを積み重ねることができますが、そうではありませんか?

アイテムの「スタック可能性」はそのアイテムのプロパティですが、アイテム自体がスタックの維持に責任を負うべきではありません。ItemStackスタックのサイズをクエリしたり、スタックを分割したりするためのwithメソッドを表すクラスがある場合は、アイテムポインターの有効期間を管理することができます(これは、スタックで少し複雑になる可能性があります) 、したがって、スタッククラスで残りの動作の実装とともにBoostなどの何かを使用するための別の引数を提供します。スタックできないアイテムは、常に1つのアイテムを含むスタックで表され、インベントリ自体は引き続きの同種のインスタンスを扱いますItemStack


+1結合するWeaponItem、棺の最後の爪になりItemWrapperます。Weapon拡張Itemすることも、両方とも共通の基本クラスを拡張することもできます。その後、次のようなものにすることができますvector<shared_ptr<Item> > inv;
Gyan aka Gary Buyn 2012

ItemStackを使用するのは良い考えのようです。
Malabarba
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.