I have classes for entities like Ship, Shampoo, Horse etc and each of them will have a manager class like ShipManager, ShampooManager, HorseManager etc. All the manager classes implement IManager and entities implement IEntity.

Now I have a static function written just to save the entities which would look like:

public static bool Save<S, T>(S entity, string msg) where S : IEntity 
                                                    where T : IManager<S>, new()
{
    string possibleError;
    switch (new T().Save(entity, out possibleError))
    {
        //---------------------
    }
    return true;
}

The inelegance here is that I have to call the Save like Save<Radio, RadioManager> etc. What I would love to have is something like this:

public static bool Save<T>(T entity, string msg) where T : IEntity
{
    string possibleError;
    switch ((/*find manager here*/).Save(entity, out possibleError))
    {
        //---------------------
    }
    return true;
}

There are few ways to do this, but I would love to have something that involves generics and type inference. I am thinking it would be good to couple the entity classes with corresponding manager classes by design to achieve some type safety as well.

Here is what I could do, with the help of an extension method:

public static bool Save<T>(this IManager<T> mgr, T entity, string msg) where T : IEntity
{
    string possibleError;
    switch (mgr.Save(entity, out possibleError))
    {
        //---------------------
    }
    return true;
}

So that I can call:

FooManager fMgr = new FooManager();
fMgr.Save(foo, "success");

But here I always need to instantiate the IManager and then call the Save method on its instance. I would love to avoid that much repetitive code and give the duty to a static function. How can I design the classes to have a relationship between IManager and IEntity so that manager of entity is automatically inferred?

Edit: Please note that I dont have the luxury to move the Save method in manager class to entity class (which would have made life easier). Our entire design is based on one entity class and a corresponding manager class. So I am thinking of sticking to it.

有帮助吗?

解决方案 2

Here is how I solved it.

I created a function Manager in the IEntity like this:

IManager<T> Manager<T>() where T : IEntity;

Now whenever I implement IEntity in any of the entity classes, I am forced to implement their respective Manager as well. For eg.

public class Foo : IEntity
{
    public IManager<T> Manager<T>() where T : IEntity
    {
        return (IManager<T>)new FooManager();
    }
}

Now I can call:

public static bool Save<T>(T entity, string msg) where T : IEntity, new()
{
    string possibleError;
    switch (entity.Manager<T>().Save(entity, out possibleError))
    {
        //--------------------------------------
    }
    return true;
}

Not the best of designs, but this does the job..

其他提示

Does the Client of FooManager care that it's a FooManager or that it's an IManager<Foo> ? If it just wants an IManager<Foo>, you probably want to look at the AbstractFactory pattern. Your factory would be responsible for instantiating/retrieving the correct IManager on demand. With the factory in place your save method would look something like this

public static bool Save<S>(S entity, string msg) 
  where S : IEntity
  {     
     string possibleError;     
     switch (ManagerFactory.GetManagerFor<S>().Save(entity, out possibleError))     
     {
       //---------------------     
     }     
     return true; 
  } 

The simplest path for implementing your ManagerFactory is to use a service locator/dependency injector, and have it auto discover your IManager<T> implementations. Then your GetManagerFor method would just ask for an instance from the DI Container. For example, here's how to do it with AutoFac

var dataAssembly = Assembly.GetExecutingAssembly();

builder.RegisterAssemblyTypes(dataAssembly)
    .Where(t => t.Name.EndsWith("Manager"))
    .AsClosedTypesOf(typeof(IManager<>);

This will cause autofac to find all classes that end with "Manager" in their name in the current assembly and register as closed types of IManager<T>. So it would find FooManager and register it as an instance of IManager<Foo>

Now ManagerFactory just needs to call

return container.Resolve<IManager<Foo>>()

And you're golden. I'm not going to write your application for you but this should give you enough to get you going. Read the docs on AutoFac, it's really powerful because it can also build up objects for you using dependency injection as well and it's a great tool to have in your belt.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top