クラス階層での過負荷 - ほとんど派生していない
-
28-10-2019 - |
質問
問題
私は次のようなコードを避けようとしています:
If(object Is Man)
Return Image("Man")
ElseIf(object Is Woman)
Return Image("Woman")
Else
Return Image("Unknown Object")
メソッドオーバーロードを介してこれを行うことができると思いましたが、常に派生したタイプを常に選択します。これは、過負荷がコンパイル時間(オーバーライドとは異なり)で決定されるため、次のコードでベースクラスのみを想定できるためだと思います。
コード構造:
NS:Real
RealWorld (Contains a collection of all the RealObjects)
RealObject
Person
Man
Woman
NS:Virtual
VirtualWorld (Holds a reference to the RealWorld, and is responsible for rendering)
Image (The actual representation of the RealWorldObject, could also be a mesh..)
ArtManager (Decides how an object is to be represented)
コード実装(キークラス):
class VirtualWorld
{
private RealWorld _world;
public VirtualWorld(RealWorld world)
{
_world = world;
}
public void Render()
{
foreach (RealObject o in _world.Objects)
{
Image img = ArtManager.GetImageForObject(o);
img.Render();
}
}
}
static class ArtManager
{
public static Image GetImageForObject(RealObject obj)// This is always used
{
Image img = new Image("Unknown object");
return img;
}
public static Image GetImageForObject(Man man)
{
if(man.Age < 18)
return new Image("Image of Boy");
else
return new Image("Image of Man");
}
public static Image GetImageForObject(Woman woman)
{
if (woman.Age < 70)
return new Image("Image of Woman");
else
return new Image("Image of Granny");
}
}
私のシナリオ:基本的に私はゲームを作成しており、画面上のクラス(人のイメージ)から、現実世界のクラス(男性など)を切り離したいと考えています。現実世界のオブジェクトは、画面上の表現の知識を持たないはずです。表現は、実際のオブジェクトを認識する必要があります(男性が何歳であるか、したがって、しわがどのくらいのしわが描かれているかを知るため)。 RealObjectが未知のタイプの場合、それはまだ何か(大きな赤十字のような)を表示するフォールバックが必要です。
このコードは私が使用しているものではなく、質問を明確にするための簡略化されたバージョンであることに注意してください。該当する場合は後で詳細を追加する必要があるかもしれません。このコードのソリューションもアプリケーションで機能することを願っています。
これを解決するための最もエレガントな方法は何ですか? -RealObject自体が、それをどのように表現すべきかについての情報を保持していません。 XNAゲームは、非常に重い概念の証明であり、実行可能であることが証明された場合、2Dから3Dに変更されます(おそらく下端コンピューターでは両方をサポートします)。
解決
工場を使用してください:
public class ImageFactory
{
Dictionary<Type, Func<IPerson, Image>> _creators;
void Assign<TPerson>(Func<IPerson, Image> imageCreator) where T : IPerson
{
_creators.Add(typeof(TPerson), imageCreator);
}
void Create(Person person)
{
Func<IPerson, Image> creator;
if (!_creators.TryGetValue(person.GetType(), out creator))
return null;
return creator(person);
}
}
工場の方法を割り当てる:
imageFactory.Assign<Man>(person => new Image("Man");
imageFactory.Assign<Woman>(person => new Image("Big bad mommy");
imageFactory.Assign<Mice>(person => new Image("Tiny little mouse");
そしてそれを使用してください:
var imageOfSomeone = imageFactory.Create(man);
var imageOfSomeone2 = imageFactory.Create(woman);
var imageOfSomeone3 = imageFactory.Create(mice);
男性のために異なる画像を返すことができるようにするには、状態を使用できます。
factory.Assign<Man>(person => person.Age > 10 ? new Image("Man") : new Image("Boy"));
明確にするために、より複雑な方法をすべてクラスに追加できます。
public static class PersonImageBuilders
{
public static Image CreateMen(IPerson person)
{
if (person.Age > 60)
return new Image("Old and gready!");
else
return new Image("Young and foolish!");
}
}
メソッドを割り当てます
imageFactory.Assign<Man>(PersonImageBuilders.CreateMen);
他のヒント
.NET 4を使用している場合は、以下を試してください。
Image img = ArtManager.GetImageForObject((dynamic)o);
ダイナミックにキャストすることにより、実際のタイプは実行時に決定され、その後、正しい過負荷が呼び出されます。
コンストラクターの引数として実際のオブジェクトを受け入れるファサードクラスを作成できます(つまり、Manfacade、Womanfacadeなど)
最も由来するクラスが呼ばれている理由は、外部クラスで仕事をしているからだと思います。 getImage()メソッドをRealObjectクラスの仮想メンバーにすると、最も由来するバージョンが呼び出されます。必要に応じて、getImage()をArtmanagerに委任できることに注意してください。しかし、 @Seairthのソリューションは同じことを達成し、おそらく邪魔にならないでしょう。
RealObjectクラスにgetImage()を置くことは単一の責任に違反すると主張することができます...それはクラスの残りの部分がどのように見えるかに依存すると思います。しかし、RealWorld.Renderが各RealObjectの画像を取得する責任を負うべきではないように思えます。そして、そのまま、Open/Closedに違反するRealObjectのサブクラスを追加するたびにArtManagerにタッチする必要があります。
のhirearchyの場合 RealWorld
安定しています Visitor
パターン。
public abstract class RealObject
{
public abstract void Accept(RealObjectVisitor visitor);
}
public class Man : RealObject
{
public override void Accept(RealObjectVisitor visitor)
{
visitor.VisitMan(this);
}
}
public class Woman : RealObject
{
public override void Accept(RealObjectVisitor visitor)
{
visitor.VisitWoman(this);
}
}
public abstract class RealObjectVistor
{
public abstract void VisitMan(Man man);
public abstract void VisitWoman(Woman woman);
}
public class VirtualObjectFactory
{
public VirtualObject Create(RealObject realObject)
{
Visitor visitor = new Visitor();
realObject.Accept(visitor);
return visitor.VirtualObject;
}
private class Visitor : RealObjectVistor
{
public override void VisitMan(Man man)
{
VirtualObject = new ManVirtualObject(man);
}
public override void VisitWoman(Woman woman)
{
VirtualObject = new WomanVirtualObject(woman);
}
public VirtualObject VirtualObject { get; private set; }
}
}