c# - 存储映射到它们所操作的具体类型的处理程序集合

时间:2014-05-30 00:29:29

标签: c# generics collections

我正在向我的应用添加指标跟踪。对于每个具体的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类型?

1 个答案:

答案 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); ;

    }
}
相关问题