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:
Is this considered good PHP?
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();
?>
1 Answer 1
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!
fly
function inside ofFlyWithWings
but never creates it inside of the Duck Class. \$\endgroup\$