views:

110

answers:

3

Over the past few years I've been on projects where we've run into a similar problem in our object hierarchy that always seems to cause problems. I was curious if anyone here knew of a classical OOP (Java, C#, PHP5, etc) design pattern that could gracefully handle this situation.

Say we have an existing system. This system has, among other things, two types of entities, each modeled with an individual class. Let's say

  1. Customer

  2. SalesRepresentative

For historical reasons, neither of these classes inherit from the same base class or share a common interface.

The problem I've seen is, inevitably, a new feature gets specced out that requires us to treat the Customer and the SalesRepresentative as the same type of Object. The way I've seen this handled in the past is to create a new class that includes a member variable for both, and then each method will operate on the objects differently depending on which is set

//pseudo PHPish code
class Participator
{
 public $customer;
 public $salesRepresentative;

 public function __construct($object)
 {
  if(object is instance of Customer)
  {
   $this->customer = $object;
  }

  if(object is instance of SalesRepresentative)
  {
   $this->salesRepresentative = $object;
  }   
 }

 public function doesSomething()
 {

  if($customer)
  {
   //We're a customer, do customer specific stuff
  }
  else if($salesRepresentative)
  {
   //We're a salesRepresentative, do sales 
   //representative specific stuff
  }   
 }
}

Is there a more graceful way of handling this type of situation?

+7  A: 

Maybe a Wrapper can be used here. Create a Wrapper Interface say ParticipatorWrapper that specifies the new functionality and build concrete Wrappers for each class, say CustomerWrapper and SalesRepresentativeWrapper that both implement the new functionality.

Then simply wrap the object in its appropriate wrapper and write code that targets the ParticipatorWrapper.

Update: Javaish code:

interface ParticipatorWrapper{
    public void doSomething();
}

class CustomerWrapper extends ParticipatorWrapper{
    Customer customer;
    public void doSomething(){
       //do something with the customer
    }
}

class SaleREpresentativeWrapper{
    SaleRepresentative salesRepresentative;
    public void doSomething(){
       //do something with the salesRepresentative
    }

}

class ClientOfWrapper{
    public void mymethod{
         ParticipatorWrapper p = new ParticipatorWrapper(new Customer());
         p.doSomething();
   }
}
Vincent Ramdhanie
+1: Good example of the Adapter pattern.
Adamski
+1 Similar logic to the OP's suggestion, but much easier to read and maintain than if/else block's in every method. Possibly couple with a static factory method if you don't know the type until runtime.
David Berger
+1 - but you would need to either add a factory to create the appropriate ParticipatorWrapper, or change the new ParticipatorWrapper(new Customer()) line to create a CustomerWrapper instead.
Eric Petroelje
+1  A: 

I think you could apply the concept of mixins to your classes to get the functionality you want.

Peter Bailey
+2  A: 

This is an alternative to that Vincent's answer, taking an opposite sort of approach. As I note below, there are some downsides, but your specific problem may obviate those and I think this solution is simpler in those cases (or you may want to use some combination of this solution and Vincent's).

Rather than wrapping the classes, introduce hooks in the classes and then pass them the functions. This is a reasonable alternative if you're looking to do the same thing with the same data from both classes (which I am guessing you are, based on lamenting that the two classes don't have a shared superclass).

This is using Visitor instead of Wrapper. Javaish this be something like:

public <Output> Output visit(Vistor<Output> v) {
return v.process(...all shared the fields in Customer/SalesRep...);
}

And then you have a Visitor interface which all your functions inherit from that looks like:

interface Visitor<Output> {
public Output process(...shared fields...);
}

There are someways to chop what gets passed to your Visitor, but the involves introducing new classes to specify which inputs to use, which becomes wrapping anyways, so you might as well use Vincent's answer.

The downside to this solution is if you do something that alters the structure of the class fields, you can buy yourself lots of refactoring, which is less of a problem in Vincent's answer. This solution is also a little bit less useful if you're making modifications to the data stored in the Customer/SalesRep instance, as you'd effectively have to wrap those inside the Visitor.

Carl
oh, this also assumes you can make modifications to the legacy classes. I'm assuming that you aren't refactoring because of other existing dependencies, not because you aren't allowed to do so, which would preclude my solution.
Carl
+1 for good info, as I was mainly interested in seeing the various design patterns that COULD solve the problem. You don't get exposed to a lot of real world examples when you live in PHP land.
Alan Storm