私が直面している問題を説明するために、少し抽象的なドメインを作成しました。
プレイヤーが軍隊の将軍である中世のゲームがあり、戦闘全体は、戦闘が始まる前に、たとえば準備モードで作成された戦闘計画によってほとんど影響を受けます。
必要なものを実現するために、インターフェイスを作成し、IBattleUnit
物事を非常にシンプルに保ちました。
public interface IBattleUnit
{
void Move();
void Attack();
string Salute();
}
3 種類のユニットを用意することで、今のところ仕事ができるのでArcher.cs
、ほとんど同じ方法でインターフェイスPikeman.cs
を実装します。Swordsman.cs
public class Swordsman : IBattleUnit
{
private Swordsman() {}
public void Move()
{
//swordsman moves
}
public void Attack()
{
//swordsman attacks
}
public string Salute()
{
return "Swordsman at your service, master.";
}
}
プライベートコンストラクターに注意してください。これは、でのみ採用される戦闘ユニットを対象としていますBarracks
。これは一般的なファクトリです。
public static class Barracks<T> where T : class, IBattleUnit
{
private static readonly Func<T> UnitTemplate = Expression.Lambda<Func<T>>(
Expression.New(typeof(T)), null).Compile();
public static T Recruit()
{
return UnitTemplate();
}
}
注: 空のコンストラクターのプリコンパイル済みラムダ式により、(私のマシン上で) ユニットの作成が高速化されますが、軍隊は非常に大きくなる可能性がありますが、高速なジェネリックの作成はまさに私が達成したいことです。
戦闘を開始するために必要なすべてをカバーしたため、BattlePlan
説明が唯一の欠けている部分であるため、ここで説明します。
public static class BattlePlan
{
private static List<Type> _battleUnitTypes;
private static List<Type> _otherInterfaceImplementors;
//...
private static Dictionary<string, string> _battlePlanPreferences;
private static Type _preferedBattleUnit;
private static Type _preferedTransportationUnit;
//...
static BattlePlan()
{
//read the battle plan from file (or whereever the plan init data originate from)
//explore assemblies for interface implementors of all kinds
//and finally fill in all fields
_preferedBattleUnit = typeof (Archer);
}
public static Type PreferedBattleUnit
{
get
{
return _preferedBattleUnit;
}
}
//... and so on
}
これに到達した場合は、ドメイン全体を認識しています-コンパイルも行われ、すべてが明るく見えます...
今まで: 私はコンソール アプリケーションを作成し、上記への参照を追加し、ボンネットの下にあるものから利益を得ようとします。私の混乱を完全に説明するために、最初に何が機能しているかに注意してください。
- 兵舎に特定の BattleUnit を与えてもらいたい場合は、それをインスタンス化して、戦わせ、移動させ、敬礼させることができます。インスタンス化が次のように行われる場合:
IBattleUnit unit = Barracks<Pikeman>.Recruit();
- 戦闘計画に基づいて優先されるユニットが何であるかを知りたい場合は、それを取得できます。その を尋ねることができます
AssemblyQualifiedName
。タイプを取得します (実際にはArcher
であり、 にとどまるのと同じですBattlePlan
)。私が電話するとき、私は期待しています:
Type preferedType = BattlePlan.PreferedBattleUnit;
ここで、BattlePlan が Type を提供することを期待し、ある種の Unit をインスタンス化するために Type を Barracks に渡すだけで、VisualStudio2012 (現在のバージョンの resharper) が停止し、コードをコンパイルしませんが、コードは、エラーにつながるのは次のとおりです。
Type t = Type.GetType(BattlePlan.PreferedBattleUnit.AssemblyQualifiedName);
IBattleUnit u = Barracks<t>.Recruit();
私が何をしても、 を渡すか、t
として渡すかtypeof(t)
、または に変換しようとしてIRepository
も... エラーリストに (少なくとも) 2 つのエラーがあり、そのようなコードをコンパイルできません。
Error 1 Cannot implicitly convert type 't' to 'BattleUnits.cs.IBattleUnit' Program.cs
Error 2 The type or namespace name 't' could not be found (are you missing a using directive or an assembly reference?) Program.cs
実際の質問に:
- 基盤となるインフラストラクチャを変更せずに、タイプを Barracks に渡す方法はありますか?
- または、設計上間違っていることはありますか?
私は過去 2 日間、グーグルで検索を続けましたが、唯一明確な方法は兵舎を変更することでした。
編集番号 1 : コンセプトとすべてを再考するとき:IBattleUnit
すべてのユニットが実行できるコア戦闘アクションのセットとして最初に説明されました (そして、私たちはそれをこのようにしたいと考えています)。GroundUnitBase
私は基本クラスを導入したくありませんでした。理由は、FlyingUnitBase
抽象クラスが存在する可能性があることを知っていたからです。明確で論理的な設計が必要です...しかし、 static は絶対に 1 つだけである必要がありますBarracks
。
BattleUnits についてはまだですが、1 つの基本クラスを目にすることで、実行可能なコードの状況が変わるUnitBase
可能性があるように思えます。私はそれを試してみるつもりです ... 読んで、私が書いたことから、クラスについて考えるようになりました。設計ではなく、何らかの形でそのコンパイル可能性です。ですから、これは、書かれていることを再考した後、私の心に浮かんだ最初のアイデアです。