Search code examples
c#design-patterns

Programming model - Manager + Tools


I have one question regarding a model which is descried bellow:

A manager class holds tools and does something with them... Every tool does something...

Every tool should register itself (in a static method) to the manager (manager can contain static field of tools) so when a manger starts should be able to see all tools.

What is the best practice to do this ?


Solution

  • If I have to do this I would do it differently. I would have an interface for tool, something like ITool which contains methods common to all tools like DoJob() or whatever you feel appropriate.

    Then I would design the manager class so accepts a list of ITool via the constructor, and have a method that enables your to add more tools at design time.

    interface ITool {
        DoJob();
    }
    
    class Manager 
    {
        public Manager(IEnumerable<ITool> tools);
        public AddTool(ITool tool);
    } 
    

    Edit: although I don't understand your requirements completely, but I think I have a feeling for what you are trying to do.

    You said assume the ITools are private or have been loaded before the manager, I don't know why that might cause a problem for unless the classes that implement ITool are private in an assembly different from the assembly the Manager, if this is the case then please read on.

    If your project is big or medium-to-big then I suggest to use MEF or Ninject DI container.

    If you have a small or small-to-medium project then I wouldn't let the ITools or the Manager register them self because this violates the Single Responsibility Principle.

    Instead I would use an interface called IToolRepository - kind of Repository design pattern -, let me show you:

    Assembly Where ITool is implemented:

    interface ITool {
        DoJob();
    }
    
    class Hammer : ITool { // impl details }
    
    interface IToolRepository {
        IEnumerable<ITool> GetTools();
    }
    
    class ToolsRepository : IToolRepository {
        IEnumerable<ITool> GetTools() {
            // return ITool implementations from this assembly using
            //any method you like, whether from database, web service, or reflection, etc ..
        }
    }
    
    static RepositoryFactory {
        IToolRepository CreateRepository() { // returns concrete repository };
    }
    

    Assembly where Manager exists:

    class Manager 
    {
        public Manager(IToolRepository repository);
        public Manager(IEnumerable<ITool> tools);
        public AddTool(ITool tool);
    }
    

    Now when you want to create an instance of the Manager you would call RepositoryFactory.CreateRepository() method from the other assembly and inject the result into the Manager like this:

    new Manager(RepositoryFactory.CreateRepository());
    

    This enhances testability, loose coupling and every class does what it is supposed to do, no more.