4
\$\begingroup\$

I am a very beginning in PHP and Design Patterns. I have been studying the amazing Head First Design patterns. So, I have been trying to translate the original into PHP. The class is working well, however, I would like to ask:

  1. Is this considered good PHP?

  2. Someone mentioned that this is not a Strategy Pattern. Head First's definition: "Strategy lets the algorithm vary independently from clients that use it". Is this not correct?

<?php

interface FlyBehavior{
    public function fly();
}

class FlyWithWings implements FlyBehavior{
    public function fly(){
        echo "I am flying!<br>";
    }
}

class FlyNoWay implements FlyBehavior{
    public function fly(){
        echo "I cannot fly!";
    }
}

interface QuackBehavior{
    public function quack();
}


class QuackQuack implements QuackBehavior{
    public function quack(){
        echo "Quack<br>";
    }
}

class MuteQuack implements QuackBehavior{
    public function quack(){
        echo "Silence";
    }
}

class Squeak implements QuackBehavior{
    public function quack(){
        echo "Squeak";
    }
}

abstract class Duck{
    protected $quackBehavior;
    protected $flyBehavior;

    public function performQuack(){
        $this->quackBehavior->quack();
    }    

    public function performFly(){
        $this->flyBehavior->fly();
    }

    abstract public function display();    

    public function swim(){
        echo "All ducks float, even decoy";
    }

}

class MallardDuck extends Duck{
    public function __construct(){
        $this->quackBehavior=new QuackQuack();
        $this->flyBehavior=new FlyWithWings();
    }
    public function display(){
        echo "I am real Mallard duck!";
    }
}

$mallard=new MallardDuck();
$mallard->performQuack();
$mallard->performFly();


?>
\$\endgroup\$
8
  • 2
    \$\begingroup\$ You are not really implementing the Strategy Pattern here. But don't feel bad, you are actually doing something even better (it's almost Dependency Injection, with a touch of Command Pattern) \$\endgroup\$
    – amon
    Feb 22, 2014 at 15:26
  • 1
    \$\begingroup\$ First thing is to get rid of the error. Atm you have $this->$quackBehavior=new Quack(); The $ should be omissed here. So it should be $this->quackBehavior ... Next error is there is no fly class to initiate. Look into SOLID, it helps alot, but it really takes time to consume. \$\endgroup\$ Feb 22, 2014 at 15:32
  • 2
    \$\begingroup\$ Turn on PHP error reporting and make fields $quackBehavior, $flyBehavior protected instead of private. \$\endgroup\$
    – Peter Kiss
    Feb 22, 2014 at 15:33
  • 1
    \$\begingroup\$ @RonniSkansing Daniel has a fly function inside of FlyWithWings but never creates it inside of the Duck Class. \$\endgroup\$
    – Malachi
    Feb 22, 2014 at 16:12
  • \$\begingroup\$ @SimonAndréForsberg sorry I just realized that the question is off-topic. I would like to use the same space to ask the new edited question that is now on-topic I guess. Is it possible? \$\endgroup\$ Feb 22, 2014 at 16:38

1 Answer 1

4
\$\begingroup\$

Syntax errors aside, there's a major pitfall in your design choices that you need to watch out for. Namely, if you want to figure out if your animal is capable of some behaviour, you'll need to add an identifier function for every animal. This will limit you down the road.

For example, say you are iterating over a bunch of different types of animals, you'd need something like:

foreach ($noahs_ark->animals() as $animal) {
    if ($animal->canFly()) {
        $animal->performFly();
    }
}

The problem here is that if you want to be able to check that an animal can fly, every single animal will need a "canFly()" method otherwise you'll end up with an error. If you're dealing only with birds, this probably isn't a big deal. But when you add dogs to the mix, then you have to add a ->canFly() method for every dog object too!

The solution to this is to implement your interfaces directly on your animal classes. For example:

// Assuming all birds classes extend Avian
abstract class Avian implements Flier, Quacker { ... }

foreach ($noahs_ark->animals() as $animal) {
    if ($animal instanceof Flier) {
        $animal->performFly();
    }
]

This way you can use reflection to determine types, and along with it, make assumptions that if it possesses(implements) a certain behaviour(interface), your animal can perform that action.

I'd still recommend that you make your modules implement interfaces as well to ensure consistency across specialized modules though. It'll help in cases where you might want to create factories for creating animals or in other cases as well. Hope that helps!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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