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();
?>
Alex L
5,7832 gold badges26 silver badges69 bronze badges
asked Feb 22, 2014 at 15:14
\$\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\$ Commented 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\$ Commented Feb 22, 2014 at 15:32
  • 2
    \$\begingroup\$ Turn on PHP error reporting and make fields $quackBehavior, $flyBehavior protected instead of private. \$\endgroup\$ Commented 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\$ Commented 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\$ Commented 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!

answered Dec 15, 2014 at 1:26
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.