Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.
abstract class Animal {
  function eat() {..}
  function sleep() {..}
  function isSmart()
}

class Dog extends Animal {
  public $blnCanBark;
  function isSmart() {
    return $this->blnCanBark;
  }
}

class Cat extends Animal {
  public $blnCanJumpHigh;
  function isSmart() {
    return $this->blnCanJumpHigh;
  }
}

.. and so on up to 10-20 animals.

Now I created a factory using simple factory method and try to create instances like this:

class AnimalFactory {
  public static function create($strName) {
       switch($strName) {
          case 'Dog':
            return new Dog();
          case 'Cat':
            return new Cat();
          default:
            break;
       }
  }
}

The problem is I can't set the specific attributes like blnCanBark, blnCanJumpHigh in an efficient way.

I can send all of them as extra params to create but this will not scale to more then a few classes. Also I can't break the inheritance because a lot of the basic functionality is the same.

Is there a better pattern to solve this?

share|improve this question

3 Answers 3

up vote 2 down vote accepted

Factory combined with Strategy comes to mind. Each animal could support the notion of a set of behaviours that can be added / passed as an array of behaviour interface references or array of instances of descendants of an abstract behaviour class.

Don't think this is the way to go though, as it means you put the decision of which behaviours to pass in outside of the factory and that seems to defeat the purpose of having a factory in the first place.

Builder seems specifically suited to your problem. I have always viewed Builder as an extension (not in the inheritance but the "taking it one step further" sense). Instead of focusing on instantiation, it specifically caters to constructing instances of classes that need more than just a call to a constructor.

Update:

Below is an example of the Builder pattern. It uses Delphi syntax as that is the language I'm most familiar with, but I have refrained from using Delphi specific constructs (meta classes for example). Error checking and freeing your instances has also been kept out of the example.

Please note that instead of passing the director an instance of a builder, you could also pass a string and let the director work out which builder to instantiate and use. In that case I would opt for a registry of builders in the director and a register method on the director so you can declare and builders without changing the director.


type
  TBehaviour = class(TObject)
  end;
  TSpeakBehaviour = class(TBehaviour);
  TJumpBehaviour = class(TBehaviour);

  TAnimal = class(TObject)
  public
    procedure AddBehaviour(const aBehaviour: TBehaviour);
  end;

  TCat = class(TAnimal);
  TDog = class(TAnimal);

  TAnimalBuilder = class(TObject)
  public
    procedure BuildBody; virtual; abstract;
    procedure AddBehaviours; virtual; abstract;
    function GetAnimal: TAnimal; virtual; abstract;
  end;

  TCatBuilder = class(TBuilder)
  private
    FCat: TCat;
  public
    procedure BuildBody; override;
    procedure AddBehaviours; override;
    function GetAnimal: TAnimal; override;
  end;

  TDogBuilder = class(TBuilder)
  private
    FDog: TDog;
  public
    procedure BuildBody; override;
    procedure AddBehaviours; override;
    function GetAnimal: TAnimal; override;
  end;

  TAnimalDirector = class(TObject)
  private
    FBuilder: TAnimalBuilder;
  public
    constructor Create(const aBuilder: TAnimalBuilder);
    function BuildAnimal: TAnimal;
  end;

procedure BuildMeAnAnimal;    
var
  Builder: TAnimalBuilder;
  Director: TAnimalDirector;
  Animal: TAnimal;
begin
  Builder := TCatBuilder.Create;
  Director := TAnimalDirector.Create(Builder);
  Animal := Director.BuildAndimal;
end;

//--- Animal Director --------------

constructor TAnimalDirector.Create(const aBuilder: TAnimalBuilder);
begin
  FBuilder := aBuilder;
end;

function TAnimalDirector.BuildAnimal: TAnimal;
begin
  FBuilder.BuildBody;
  FBuilder.AddBehaviours;
  Result := FBuilder.GetAnimal;
end;

//--- Cat Builder --------------

procedure TCatBuilder.BuildBody; 
begin
  FCat := TCat.Create;
end;

procedure TCatBuilder.AddBehaviours; 
begin
  FCat.AddBehaviour(TSpeakBehaviour.Create('miauw'));
  FCat.AddBehaviour(TJumpBehaviour.Create(jaHigh));
end;

function TCatBuilder.GetAnimal: TAnimal; 
begin
  Result := FCat;
end;

//--- Dog Builder --------------

procedure TDogBuilder.BuildBody; 
begin
  FDog := TDog.Create;
end;

procedure TDogBuilder.AddBehaviours; 
begin
  FDog.AddBehaviour(TSpeakBehaviour.Create('woof'));
  FDog.AddBehaviour(TJumpBehaviour.Create(jaMediumHigh));
end;

function TDogBuilder.GetAnimal: TAnimal; 
begin
  Result := FDog;
end;
share|improve this answer
    
I'm not sure how to apply the Builder pattern. I would have an Animal and AnimalBuilder class? All the properties will be inside Animal? –  danip Jun 30 '13 at 17:05
    
No you would have an animal "Builder" and specific CatBuilder and DogBuilder classes that can be registered with the builder for the specific type of animal that they can build. So when you pass "cat" to the builder. It would see whether that is in its list of concrete builders and if it is call that to build the cat. The cat builder would know that a cat can't bark and both the cat and dog builder would know their respective animal can jump high. –  Marjan Venema Jun 30 '13 at 17:07
    
I get the theory for a single concrete builder but I fail to see the solution you suggested with multiple builders. Can you please update the answer with a pseudo-code? –  danip Jun 30 '13 at 17:15
    
Added an example in Delphi for you. –  Marjan Venema Jun 30 '13 at 17:59
    
OK, so the concrete builders take all the responsability of creation. The answer to my question is correct but the problem I was actually having was the fact that I can only inject the behaviour from ouside the builder class because it's something computed on application level. I understand this was not clear so I will accept your answer, still if you have any idea pls let me know. –  danip Jun 30 '13 at 18:30

It depends on why you are using the factory method pattern.

If the main purpose is to be able to return a subclass of Cat instead of a plain Cat, using multiple factory methods might suit the purpose better, with the additional advantage of removing the "magic strings" passed to create:

class AnimalFactory {
  public static function createCat($canJumpHigh) {
    // in the future, this can be extended to return a HouseCat instead
    return new Cat($canJumpHigh);
  }
  public static function createDog($canBark) {
    return new Dog($canBark);
  }
}
share|improve this answer

Why do you have public variables like blnCanBark and blnCanJumpHigh instead of proper set_property functions.

If I simple try to understand the above code then i think there should have been a function set_properties() with unlisted number of parameters or better set_properties(array $properties).

It may not be manageable with simple functions and I would have gone for abstract class and interface for more complex logic.

But if simplicity is desirable then set_properties(array $properties) is the solution.

share|improve this answer
    
The quick answer to "why" is because the OP's code is in PHP which allows for particular constructs. Try to focus your answer on the core of the OP's question instead of syntactical differences from your preferred language. –  GlenH7 Jul 1 '13 at 10:57

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.