tags:

views:

133

answers:

6

Hello,

I have a process class, where if the input contains a value it will also do something else.

For example

Person { name; age }

ProcessPerson(person) //takes in a person

in the ProcessPerson if the age is = over 18 (Adult) then send an email.

The ProcessPerson already exists, and the base logic has to be run no matter what (save the person to the DB).

At the moment, there is only 1 condition where this extra logic is run, but it looks like there will be points in the future where there will be more added.

I can think of 2 ways of solving this, and wanted a soundboard session:

  1. Inheritance -> create a derived class called ProcessAdult
  2. Events -> post saving the person to the DB, raise a processed event. Add a handler which sends the email.

I prefer the latter of the 2. Or perhaps there is another clean and extensible method that I have not thought of?

+3  A: 

Your second option is surely a lot better.

Marek
+1  A: 

I vote for 2nd option.

Saar
+2  A: 

For the case you describe, I also like #2. Sounds cleaner and more directly addresses your situation.

Chris Ballance
+1  A: 

I might be misunderstanding you but I wouldn't subclass the Process class but rather the Person class and require the children to implement a "PostProcess" method or something. This actually allows you to also require "PreProcess", too, if you think in a couple of years you might ever have a need for it. Then your Process method takes a Person-base, calls PreProcess(), inserts into the DB and then calls PostProcess(). Children must implement the method but obviously don't have to do anything.

But honestly, depending on where this lives and the number of possible scenarios I'd rather just do this from within in the Process() and not deal with Events or Inheritance.

if(person.Age>=18){}else if(person.Pets){}...
Chris Haas
A: 

Maybe your description is simpler than your actual problem, but what is wrong with:

void ProcessPerson(Person const& p) {
    // do what you always do

    if(p.age>=18) {
        // send mail
    }
    // add other "special" handling here
}

?

You would derive from Person if you had additional data or special methods or both but you don't. You could then implement a specialization of ProcessPerson to handle the derived class.

If all information needed to process a Person object is available locally, do everything necessary in this method.

When you need different pieces of information to process the Person, that have nothing to do with each other, distribute the processing. You could have e.g. different handlers for your database and your mail system and sth like:

Person p;
foreach(handler in handler list) {
    handler.Process(p);
}

and the mail handler would send notifications, the database handler would write to the db etc.

Sebastian
Every time you add a special case, you have to add another if block. This increases the complexity of the ProcessPerson method and violates the Single Responsibility Principle. Your ProcessPerson saves a user to the database, sends an email, and does other "special" handeling. Each of these is its own concern and should be isolated.
Ryan Michela
At the same time, the code has to go somewhere, the Single Responsibility of THIS is to "process the person", including saving, emailing whatever. This provides a Single Source of Truth for the PersonProcess. If there's only a few rules, then it's appropriate. Otherwise, you need to set up the infrastructure to support the event dispatch, handling, errors, etc. Plus all of the logic is scattered about, and now represented by code and metadata. The Event case is valid, and useful, but without existing infrastructure, hard to argue it's worth the time for 1 or 2 events.
Will Hartung
@Ryan: So you propose adding an event instead? Every time you add a special case, you have to add another event handler somewhere. I go with Will: ProcessPerson processes the person and does whatever is necessary. As long as no further information is necessary, that required passing in 25 arguments, this sounds manageable to me.
Sebastian
+1  A: 

I vote for number 2, but to keep things extensible, be sure to include the processed person in the event signature.

public delegate void PersonProcessedEventHandler(Person p);
public event PersonProcessedEventHandler PersonProcessed;

This keeps your event invocation generalized. Your person processor simply raises the event that a person was processed, and any other handler can do with that person as it pleases.

public void EmailAdult(Person p)
{
    if(p.age >= 18)
    {
        //send email
    }
}

public void SendWaffles(Person p)
{
    if(p.name = "John Skeet")
    {
        //send waffles
    }
 }

thePersonProcessor.PersonProcessed += EmailAdult;
thePersonProcesser.PersonProcessed += SendWaffles;
Ryan Michela
according to .NET framework design guidelines, delegates used in events should have two arguments - first of type object, usually named sender and a second argument of type inheriting from System.EventArgs that wraps the data you want to pass in the event.
Marek