3

Maybe overloading a method is not exactly what is necessary but this is the best i could come up with.

I have a class:

public class Worker {
    private string jobType;

    public Worker(string jt)
    {
        this.jobType = jt;
    }

    public void ProcessJob()
    {
         if(jobType.Equals("Pizza") MakePizza();
         else if (jobType.Equals("Burger") MakeBurger();
    }

    private void MakePizza()
    {
        // make pizza
    }

    private void MakeBurger()
    {
        // make burger
    }
}

The above is just an example of illustration. When the class is constructed, it is constructed with a specific job type, and that won't change. However it may need to perform millions of jobs, always of the same type. The ProcessJob() will be called all the time, but the caller won't know what type of worker this is. I would like to avoid running the if check every single time, there has to be a way to do that check only once and prep it.

In my case, making child classes (pizza worker, burger worker, etc.) is not an option, as in my real case, the class is large and there is only one tiny difference. Changing it will impact the whole architecture so it needs to be avoided.

Ry-
  • 218,210
  • 55
  • 464
  • 476
Isaac Pounder
  • 177
  • 1
  • 4
  • 14
  • 5
    *"The class is large and there is only one tiny difference."* That's kind of one of the points of OOP. Explain again why inheritance isn't an option? – Ry- Jan 08 '12 at 17:53
  • these objects are already built and maintained by other classes in lists. there are many iterators, if i make different classes, then will need to rewrite the iterators – Isaac Pounder Jan 08 '12 at 17:57
  • So they're generated dynamically? Is storing delegates in a `Dictionary` possible? – Ry- Jan 08 '12 at 17:58
  • As minitech noted, inheritance where there is very little difference is the reason it exists. Whenever you have switch statements, you may be missing out on an inheritance solution. In this case, the base class should have a Run method and you have a Pizza, Burger class that inherits and implements the abstract Run. – bryanmac Jan 08 '12 at 17:59

7 Answers7

4

Create an abstract base class, which contains common things a worker can do. Then declare derived classes for specialized workers.

public abstract class Worker
{    
    public abstract void ProcessJob();
}

public class PizzaWorker : Worker
{
    public override void ProcessJob()
    {
        // Make pizza
    }
}

public class BurgerWorker : Worker
{
    public override void ProcessJob()
    {
        // Make burger
    }
}

Now you can create workers of different types and let them do their job:

var workers = new List<Worker>();
workers.Add(new PizzaWorker());
workers.Add(new BurgerWorker());
foreach (Worker worker in workers) {
    woker.ProcessJob();
}

This will automatically call the right implementation of ProcessJob for each type of worker.


Note: If-else-if cascades and switch statements are often an indication that the code works in a procedural rather than object-oriented way. Refactor it to be object-oriented!

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
  • I like this approach as well as the command pattern. Two equally acceptable solutions - although a factory usually requires some logic such as if-else or switch statements. – IAbstract Jan 09 '12 at 16:25
3

You could use a delegate created when the object is constructed, this way the dispatch is done automatically:

public class Worker
{
    private delegate void MakeSomething();

    private MakeSomething makeWhat;
    private string jobType;

    public Worker(string jt)
    {
        this.jobType = jt;

        switch (jt)
        {
            case "Pizza":
                makeWhat = new MakeSomething(MakePizza);
                break;
            case "Burger":
                makeWhat = new MakeSomething(MakeBurger);
                break;
            default:
                throw new ArgumentException();
        }
    }

    public void ProcessJob()
    {
        makeWhat();
    }

    private void MakePizza()
    {
        //make pizza
    }

    private void MakeBurger()
    {
        //make burger
    }
}
user703016
  • 37,307
  • 8
  • 87
  • 112
  • -1: I don't think this particular example is SOLID - especially in the open-closed area. If a new job type is needed, then you are modifying this `Worker` class. – IAbstract Jan 09 '12 at 16:14
  • 1
    @IAbstract IMO this is the best possible option since OP wants to avoid the creation of derived classes. – user703016 Jan 09 '12 at 16:51
  • To keep from creating more derived classes, this would work, yes ...however, IMHO this is a short-term 'fix' ... :) I'm not intending to attack you - but I think it is worth noting that while this may provide the answer that the OP wants to *hear*, doing it correctly *now* will make life much easier later on. – IAbstract Jan 10 '12 at 03:59
1

This is exactly why there are patterns: Command, Strategy, Decorator.

I believe the command pattern is what you are looking for. First you have a basic 'command' template:

public interface IJob {
    void ProcessJob();
}

Different jobs would then be performed as follows:

public class MakePizza : IJob {
    // implement the interface
    public void ProcessJob() {
        // make a pizza
    }
}

Now, you could have a JobFactory as follows:

public static class JobFactory {
    public static IJob GetJob(string jobType) {    
        if(jobType.Equals("Pizza"){
            return new MakePizza();
        } else (jobType.Equals("Burger") {
            return new MakeBurger();
        }
        // to add jobs, extend this if-else-if or convert to switch-case
    }
}

Worker can now look like this:

public class Worker {
    private IJob job;

    public Worker(string jt) {
        job = JobFactory.GetJob(jt);
    }

    public void ProcessJob() {
         job.ProcessJob();
    }
}

If you don't have access to code to make these changes, then another pattern you may want to look into is the Adapter.

IAbstract
  • 19,551
  • 15
  • 98
  • 146
1

I would still recommend to use sub classes. If you cannot inherit from Worker then create new class hierarchy that is used inside the worker. This way anyone using Worker class doesn't have to know that there are sub classes. If you really really hate sub classes or you have some other reason you don't want them you can use dictionary. It contains job type as key and Action as the method it calls. If you need more jobs just create the private method and register it in the RegisterWorkers method.

private Dictionary<string, Action> actions = new Dictionary<string, Action>();

public Worker(string jt)
{
    this.jobType = jt;
    this.RegisterWorkers();
}

private void RegisterWorkers
{
    this.actions["Pizza"] = this.MakePizza;
    this.actions["Burger"] = this.MakeBurger;
}

public void ProcessJob()
{
    var action = this.actions[this.jobType];
    action();
}
Toni Parviainen
  • 2,217
  • 1
  • 16
  • 15
1

No, I don't think it should be avoided. Any common functionality should go in a base class. I think you need a static factory method, that returns a child class based on the string parameter.

public abstract class Worker {
    public virtual void ProcessJob();

    public static Worker GetWorker(string jobType) {
        if(jobType.Equals("Pizza")
            return new PizzaWorker();
        else if (jobType.Equals("Burger")
            return new BurgerWorker();
        else
            throw new ArgumentException();
    }

    // Other common functionality
    protected int getFoo() {
        return 42;
    }
}

public class PizzaWorker : Worker {
    public override void ProcessJob() {
        // Make pizza
        int y = getFoo() / 2;
    }
}

public class BurgerWorker : Worker {
    public override void ProcessJob() {
        // Make burger
        int x = getFoo();
    }
}

So to use this:

Worker w = Worker.GetWorker("Pizza");
w.ProcessJob();   // A pizza is made.
Jonathon Reinhart
  • 132,704
  • 33
  • 254
  • 328
0

You're talking about basic inheritance here. There are a couple of ways that you could do this.

Make a Base Class that is

public class Job
{
    virtual void ProcessJob();
}

Then a MakePizza class

public class MakePizza : Job
{
    public void ProcessJob()
    {
       //make Pizza
    }
}

Then in your worker class instead of having a JobType as a string which will lead to all kinds of potential bugs.

public class Worker{


  private Job jobType;


  public Worker(Job jt){

    this.jobType = jt;
  }


  public void ProcessJob()
  {
    Job.ProcessJob();  
  }

}

If you have to pass through a string you could simply load up the JobType through reflection, throwing a error if the type doesn't exist.

msarchet
  • 15,104
  • 2
  • 43
  • 66
0

having to change other classes means you need to change code, not that you need to change architecture. the best answer is just to change the code. in the long term, the maintenance burden of having to write this in a less-than-ideal fashion will cost you more than just changing the code. use inheritance and bite the bullet on making the change now. if you have iterators that will have problems with dealing with subtypes, your iterators are doing more than being iterators, and you are better off fixing that than going forward with them. if the other classes care about what subtype of worker they are dealing with, that's a problem in and of itself that you should fix. ultimately, the dependent code should not care which type of worker it is. that's really what you are after anyway. the instance of a type that has work as its base type is still a worker and that is all the class using a worker should care about.

Dave Rael
  • 1,759
  • 2
  • 16
  • 21