我正在向我的应用添加指标跟踪。对于每个具体的Metric对象,都有一个特定的MetricsTracker,它负责解析数据并将其发送到服务器。我原本想为每个MetricsTracker使用泛型,但我在设计过程中遇到了一个问题,无法找到一个优雅的解决方案。我已经创建了一个简化的模型来说明我被困在哪里。
让我假装我正在经营一个动物园,当我注意到其中一只动物生病时,我需要找一个专门照顾这种动物的动物园饲养员。
这是我的Animal类。它包含ZooCode,动物园使用它来识别它。 (注意" ZooCode"在我的真实模型中是必要的,因为它标识了服务器上的度量标准类型)
public abstract class Animal
{
//The zoo code of this animal.
public abstract int ZooCode { get; }
//other things specific to an animal.
}
这是一个具体动物的例子。
public class Elephant : Animal
{
//An Elephant has a zoo code of 1
public override string ZooCode { get { return 1; } }
}
这是ZooKeeper的基类,ZooKeeper只知道如何关注特定类型的Animal。
public abstract class ZooKeeper
{
//The Zoo Code of the animal we care for.
public abstract int AnimalTendsTo { get; }
//Tend to our animals needs
public abstract void CareForAnimal(Animal animal);
}
以下是具体ZooKeeper的示例。
public class ElephantKeeper : ZooKeeper
{
//We care for elephants, they have ZooCode of '1'
public override int AnimalTendsTo
{
get { return 1; }
}
public override void CareForAnimal(Animal animal)
{
//cast animmal as elephant, then feed peanuts.
}
}
现在当我发现动物生病时,我去了ZooKeeperManagers办公室,我告诉他们哪些动物需要治疗。 ZooKeeperManager负责找到专门研究生病动物的ZooKeeper。
public class ZooKeeperManager
{
/// <summary>
/// All of the ZooKeepers, mapped to the ZooCode of the animal they
/// care for.
/// </summary>
private Dictionary<int, ZooKeeper> _zooKeepers;
/// <summary>
/// Called when we hire a new zoo keeper.
/// </summary>
public void RegisterZooKeeper(ZooKeeper newbie)
{
//data validation, etc...
//store our new zookeeper.
_zooKeepers.Add(newbie.AnimalTendsTo, newbie);
}
public void CareForAnimal(Animal animal)
{
//An animal needs care, find the ZooKeeper that can care for it.
ZooKeeper careTaker;
if (_zooKeepers.TryGetValue(animal.ZooCode, out careTaker) == false)
return;
careTaker.CareForAnimal(animal);
}
}
这是我对我的设计不满意的地方!我原本打算让ZooKeeper成为Generic,这样ZooKeeper就会得到它所关注的Concrete Animal的引用,并且它不必担心指定ZooCodes或将其Animal转换为正确的类型。这是我第一次想出的......
public abstract class ZooKeeper<T>
where T : Animal
{
//Tend to our animals needs
public abstract void CareForAnimal(T animal);
}
ZooKeeperManager需要知道哪些ZooKeepers关心哪个Animal ..我真的很喜欢ZooKeeperManager可以说&#34;大象病了?那意味着我需要这个ZooKeeper&#34;然而,当它们是通用的时候,我无法找到存储ZooKeepers的优雅方式。这就是我陷入困境的地方:
public class ZooKeeperManager
{
/// <summary>
/// All of the ZooKeepers, mapped to the ZooCode of the animal they
/// care for.
/// </summary>
private List<ZooKeeper<Animal>> _zooKeepers;
/// <summary>
/// Called when we hire a new zoo keeper.
/// </summary>
public void RegisterZooKeeper(ZooKeeper<Animal> newbie)
{
//data validation, etc...
//store our new zookeeper.
_zooKeepers.Add(newbie);
}
public void CareForAnimal(Animal animal)
{
//How do I find the Zoo Keeper that Cares for this Animal??
//A List definitely isn't what I want.. I won't have o(1) Lookup, plus how do I know
//Which concrete Animal that ZooKeeper cares about?
}
}
有没有人知道如何在ZooKeeperManager中存储一个集合,将ZooKeeper映射到他们关心的Animal类型?
答案 0 :(得分:2)
好的,是的,首先你必须实现一个基本的ZooKeeper:
public class ZooKeeperBase
{ }
你的抽象ZooKeeper必须继承它:
public abstract class ZooKeeper<T> : ZooKeeperBase
where T : Animal
{
//Tend to our animals needs
public abstract void CareForAnimal(T animal);
}
最后你修改你的经理来存储ZooKeeperBase的实例,并在必须照顾你的动物时使用装箱和通用功能:
private List<ZooKeeperBase> _zooKeepers = new List<ZooKeeperBase>();
public void CareForAnimal<T>(T animal) where T : Animal
{
//How do I find the Zoo Keeper that Cares for this Animal??
//A List definitely isn't what I want.. I won't have o(1) Lookup, plus how do I know
//Which concrete Animal that ZooKeeper cares about?
foreach (ZooKeeperBase keeper in _zooKeepers)
{
var thisKeeper = keeper as ZooKeeper<T>;
if (thisKeeper != null)
{
thisKeeper.CareForAnimal(animal);
return;
}
}
}
为了清楚起见,我发布了完整的模型和测试类。
public abstract class Animal
{
//The zoo code of this animal.
public abstract int ZooCode { get; }
//other things specific to an animal.
}
public class Elephant : Animal
{
//An Elephant has a zoo code of 1
public override int ZooCode { get { return 1; } }
}
public class ZooKeeperBase
{ }
public abstract class ZooKeeper<T> : ZooKeeperBase
where T : Animal
{
//Tend to our animals needs
public abstract void CareForAnimal(T animal);
}
public class ElephantKeeper : ZooKeeper<Elephant>
{
public override void CareForAnimal(Elephant animal)
{
Console.WriteLine("Hi elephant! take some peanuts.");
}
}
public class ZooKeeperManager
{
/// <summary>
/// All of the ZooKeepers, mapped to the ZooCode of the animal they
/// care for.
/// </summary>
private List<ZooKeeperBase> _zooKeepers = new List<ZooKeeperBase>();
/// <summary>
/// Called when we hire a new zoo keeper.
/// </summary>
public void RegisterZooKeeper(ZooKeeperBase newbie)
{
//data validation, etc...
//store our new zookeeper.
_zooKeepers.Add(newbie);
}
public void CareForAnimal<T>(T animal) where T : Animal
{
//How do I find the Zoo Keeper that Cares for this Animal??
//A List definitely isn't what I want.. I won't have o(1) Lookup, plus how do I know
//Which concrete Animal that ZooKeeper cares about?
foreach (ZooKeeperBase keeper in _zooKeepers)
{
var thisKeeper = keeper as ZooKeeper<T>;
if (thisKeeper != null)
{
thisKeeper.CareForAnimal(animal);
return;
}
}
}
}
public static class ZooKeepingSystemTest
{
public static void KeepIt()
{
ZooKeeperManager manager = new ZooKeeperManager();
ElephantKeeper keeper = new ElephantKeeper();
manager.RegisterZooKeeper(keeper);
manager.CareForAnimal(new Elephant());
}
}
编辑:没有注意到关于不使用列表的评论,确实可能,在这个例子中我使用字典来存储所有关注具体的ZooKeeper的列表动物,所以我们可以随机使用它们中的任何一个(如果你只有每个动物类型的守护者,你可以直接将守护者添加到词典中),只有ZooKeeperManager必须被修改。
public class ZooKeeperManager
{
/// <summary>
/// All of the ZooKeepers, mapped to the Animal's full class name they
/// care for.
/// </summary>
private Dictionary<string, List<ZooKeeperBase>> _zooKeepers = new Dictionary<string, List<ZooKeeperBase>>();
/// <summary>
/// Called when we hire a new zoo keeper.
/// </summary>
public void RegisterZooKeeper<T>(ZooKeeper<T> newbie) where T : Animal
{
//data validation, etc...
var type = typeof(T);
List<ZooKeeperBase> keeperPool = null;
if (_zooKeepers.ContainsKey(type.FullName))
keeperPool = _zooKeepers[type.FullName];
else
{
keeperPool = new List<ZooKeeperBase>();
_zooKeepers.Add(type.FullName, keeperPool);
}
//store our new zookeeper.
keeperPool.Add(newbie);
}
public void CareForAnimal<T>(T animal) where T : Animal
{
var type = typeof(T);
if (!_zooKeepers.ContainsKey(type.FullName))
throw new Exception("We don't know how to care that animal!");
Random rnd = new Random();
((ZooKeeper<T>)(_zooKeepers[type.FullName].OrderBy(k => rnd.NextDouble()).First())).CareForAnimal(animal); ;
}
}