1

I have the following classes

  • Abstract class duck
    This class have flyBehavoir of type FlyBehavoir
    Function to perform flying preformFly()
    Function to set flyBehavoir setFlyBrhavoir(FlyBehavoir $flyBehavoir)
  • Class DonaldDuck extends Duck
    in this class, I have a __construct method, inside this constructor, I instantiate new fly behavior FlyWithWings.

The problem is when I need to change flyBehavoir in the runtime via setFlyBrhavoir() method and set it to FlyWithRocket it does not change as long as flyBehavoir is private if I make it public it works fine. how can I do that?

thought that we can change any property in the superclass from the child class, as long as I access this private property vis setter.

below my attempt

 <?php

//abstract class that defines what it takes to be a duck
//inside Duck we will have $flyBehavoir object of FlyBehavoir Type 
abstract class Duck{
    private  $flyBehavoir; 

    public function preformFly(){
        $flyBehavoir.fly();
    }
    public function setFlyBehavoir(FlyBehavoir $flyBehavoir){
        $this->flyBehavoir =  $flyBehavoir;
    }
}

//creating type FlyBehavoir 
interface FlyBehavoir{
    function fly();
}

//this concrete class of type FlyBehavoir  this will provide our ducks with the functionality they need to fly
class FlyWithWings implements FlyBehavoir{
    public function fly(){
        echo "I am Flying with my own Wings<br>";
    }
}

//this concrete class of type FlyBehavoir  this will provide our ducks with the functionality they need to fly
class FlyWithRocket implements FlyBehavoir{
    public function fly(){
        echo "I am the fastest duck ever, see my rockets wings <br>";
    }
}


// creating our first duck and given it the ability to fly with wings
class DonaldDuck extends Duck{

    public function __construct(){
        $this->flyBehavoir =  new FlyWithWings;
    }

}


$donaldDuck = new DonaldDuck( ) ;

$donaldDuck->flyBehavoir->fly();
//changing behavoir in run time 
$donaldDuck->setFlyBehavoir(new FlyWithRocket);
$donaldDuck->flyBehavoir->fly();


Output
I am Flying with my own Wings
I am Flying with my own Wings
yasir ALQAISI
  • 59
  • 1
  • 8
  • 1
    Unless I misunderstand the problem, you have already mentioned the solution in your question: You need to use the setter instead of accessing the property directly. – jeroen Aug 24 '18 at 12:15
  • 1
    That is what `private` means… You want `protected` instead. – deceze Aug 24 '18 at 12:15
  • use public/protected instead of private. – amku91 Aug 24 '18 at 12:18
  • jeroen i am using setter if you check the code, – yasir ALQAISI Aug 24 '18 at 12:19
  • deceze protected give another error ( ! ) Fatal error: Uncaught Error: Cannot access protected property DonaldDuck::$flyBehavoir in /home/yazfarqj/dev/headFirstDesignPatrenBook/ch1/duck3.php on line 48 – yasir ALQAISI Aug 24 '18 at 12:21

1 Answers1

3

A private property is not accessible in child classes.

class DonaldDuck extends Duck {
    public function __construct(){
        $this->flyBehavoir = new FlyWithWings;
    }
}

For all intents and purposes, this class does not formally declare flyBehaviour at all, so $this->flyBehaviour in the constructor creates a new public property. You can see that clearly when var_dumping the object:

object(DonaldDuck)#1 (2) {
  ["flyBehavoir":"Duck":private]=>
  NULL
  ["flyBehavoir"]=>
  object(FlyWithWings)#2 (0) {
  }
}

The parent's private property is a) separate, b) private to it and c) null since nobody has set it yet. Otherwise it would also not be possible for you to access $donaldDuck->flyBehavoir->fly() from without the class!

If you have a private property, you need to only let code of the same class act on it:

class DonaldDuck extends Duck {
    public function __construct(){
        $this->setFlyBehaviour(new FlyWithWings);
    }
}

$donaldDuck = new DonaldDuck();
$donaldDuck->setFlyBehavoir(new FlyWithRocket);
$donaldDuck->preformFly();

This works as you expect, since you're using the correctly privileged methods to access the property. If you want to access the property directly in child classes, it needs to be protected (which then won't let you access it from outside the class though, it would have to be public for that).

deceze
  • 510,633
  • 85
  • 743
  • 889