3

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.

nawfal
  • 70,104
  • 56
  • 326
  • 368
  • Does each Entity have to have it's own instance of a Manager class. Generally, I create one Manager instance for the life of the application or request. Through a Singleton Pattern, you can call the code like: FooManager.Instance.Save(fooObj); – joshgo Oct 16 '12 at 22:10
  • @joshgo that's the problem here, that I do have one manager class for every entity class. – nawfal Oct 16 '12 at 22:12

2 Answers2

4

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.

Michael Brown
  • 9,041
  • 1
  • 28
  • 37
  • 1
    +1 that's how such things usually done. BTW common name for such 'Managers' is Repository. – Sergey Berezovskiy Oct 16 '12 at 22:13
  • 1
    Agreed, though Repository has a special meaning in a domain-driven design context. I'd recommend looking into the [Repository pattern](http://martinfowler.com/eaaCatalog/repository.html) for ideas. – neontapir Oct 16 '12 at 22:15
  • @Mike Brown it is indeed the implementation details of `ManagerFactory.GetManagerFor()` that I am looking for, i am not sure if i will be able to make it out myself from the link u have given. Thanks, let me try – nawfal Oct 16 '12 at 22:15
  • I added some details on one approach to implementing your ManagerFactory. – Michael Brown Oct 17 '12 at 01:04
  • @MikeBrown that seems to be simple, but I am apprehensive about using reflection and string approaches for something I believe is achievable by design. Still +1 for an alternative approach – nawfal Oct 17 '12 at 07:30
  • I went through the abstract factory pattern mentioned in the wiki link. It again uses an "if else" condition. That's not very OOP or polymorphic :) – nawfal Oct 17 '12 at 07:32
  • If you're not comfortable with using conventions to register your types, you can register them manually. The downside being that everytime you add a new Manager to the system, you have to remember to add it to the Registration. – Michael Brown Oct 17 '12 at 15:08
0

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..

nawfal
  • 70,104
  • 56
  • 326
  • 368