05.
Creating an Abstract Ship
Keep on Learning!
If you liked what you've learned so far, dive in! Subscribe to get access to this tutorial plus video, code and script downloads.
If you liked what you've learned so far, dive in! Subscribe to get access to this tutorial plus video, code and script downloads.
Whoops, an error! Please, try again later.
15 Comments
Hello,
Why would we call
parent::construct($name)if we already have a
public function __construct()?Hey @Bilal-S ,
If you do not call
parent::__construct($name);- the parentconstuct()method will be compeltely overridden, i.e. the code in the parentconstruct()will not be executed at all. If that's the behaviour you want - OK then, it's a valid point too. But if you want to also execute the code from parent (base)constuct()- you need to callparent::__construct($name);in your class where you extend a base class. In our case we just don't want to override (miss) the code from the parent base class, that's why we have thatparent::__construct($name);call.
I hope it clarifies things for you :)
Cheers!
In the code challenge, why are we defining the
makeFiringNoise()method in the abstract deathstar (ds)? Is basically a different method in DSI and DSII, so I would prefer defining it in every star than having one star overriding it.Hey theNaschkatze,
Yea, in this example, it seems like the
makeFirignNoise()does not do too much, but if you would have to add more DS derivatives, they all can share the same logic by extending from their abstract class. Besides that, type-hinting for an abstract class is better than type-hinting for a concrete implementation because it decouples your code and makes the callers agnostic from the implementation detailsCheers!
Why should we keep getJediFactor() in AbstractShip ? ("So let's copy and paste that function into Ship:")
This class doesn't own the $jediFactor property.
Is it because we have $this->getJediFactor() in the getNameAndSpecs() method ?
Do we have to refer to a method that must be existing even if this method is referring to a nonexistent property ?
(sorry for my english)
Hey Marc R. !
Excellent question :). Part of the answer is explained in the next chapter, but it can still be a bit confusing. Let's focus first on just this chapter - and you already are thinking the right thing!
Here's what we know: AbstractShip has a
getNameAndSpecs()method and that needs to know the "jedi factor" to do its job. And so, it calls agetJediFactor()method to do that. If EVERY ship type calculated their jedi factor the same way (by returning the jediFactor property), then we should put both thejediFactorproperty and thegetJediFactor()method inAbstractShip. But in reality, the way that the "jedi factor" is calculated is different betweenShipandRebelShip. For example, onlyShip</cod> needs ajediFactor` property, which is why it lives there.Because of this, the tricky part is balancing these two things:
1) AbstractShip needs to guarantee that it has a
getJediFactor()method... because it's calling it ingetNameAndSpecs()!2) But...
AbstractShipcan't have agetJediFactormethod... because each sub-class determines this in a different way.So, we have 2 options really:
A) Add
getJediFactor()toAbstractShipwith some default implementation. This makes sense if we have several classes that extend AbstractShip and maybe only one of them behaves differently. So, we addgetJediFactor()toAbstractShipwith the most common implementation and then override it in the one sub-class that needs to behave differently.B) OR, do what we do in the next chapter: add a
abstract public function getJediFactor()to AbstractShip. This will guarantee that this method must be implemented in every class. This allowsAbstractShipto safely use the method: we know it will exist, and each sub-class can figure out its own code for it.Neither of these options is always right or always wrong - it depends on the situation. But it is true that if
AbstractShipis calling a method like$this->getJediFactor(), then we SHOULD have that method inAbstractShipeither as a real or abstract method.Phew! I hope that helps. If I completely answered the wrong question, please let me know :).
Cheers!
Thank you for that answer.
I finished the next chapter and I understand the use of abstract methods.
So I suppose that the choice to have kept the getJediFactor() method in AbstractShip in this chapter (even if the $jediFactor property is not there and that it can be disturbing) is made on purpose in order to introduce later the notion of abstract methods.
Hey Marc R.!
Excellent :). Nice work
Well actually, by the end of this chapter, the
getJediFactor()method is not insideAbstractShipanymore. We separated all the "different" code betweenShipandRebelShipand this ultimately meant that each class had its owngetJediFactor(). And, functionally, this worked ok: thegetSpecs()method callsgetJediFactor()and, because both sub-classes have this, the code runs. But, this is "weird": there is nothing "enforcing" that every sub-class ofAbstractShipmust have agetJediFactor()method. If we created a 3rd sub-class today and forgot to add that method, thegetSpecs()method would blow up :).So this chapter was all about: how can we share some code in this parent
AbstractShipmethod but move "specific" code into each sub-class. The next chapter is all about "Hey! It's weird that there is nothing enforcing that each sub-class has a getJediFactor() method. Let's enforce that with an abstract method.It sounds like things were already making sense to you, but I hope this can clarify even more :).
Cheers!
In challenge 5.1, how public function setCrewSize($numberOfPeople) of DeathStar class can access the private $crewSize property of AbstractDeathStar class ? Isn't it required that the $crewSize property be a protected one for accessing it from the DeathStar sub-class ?
=============== AbstractDeathStar.php ==============
`(php tag)
class AbstractDeathStar
{
}`
=========== DeathStar.php ===========
`(php tag)
class DeathStar extends AbstractDeathStar
{
}`
Hey Tariq I.
But are you sure that
AbstractDeathStarshould have this property? ;)Cheers!
No, it shouldn't .................
But the above mentioned code works !!
Please see this image.
Hey Tariq I.!
Awesome discovery :). Here's what's going on. In PHP, it's legal (but not recommended) to set a property on a class that doesn't exist. Let me explain. Here are two facts:
1) Check out this code:
In PHP, if you say
$this->name = $nameand that class has no name property... PHP simply creates anameproperty in the background and sets it. Basically, you don't technically need to sayprivate $nameon the class - PHP allows you to set properties that don't exist. This is not recommended... it's more of a "left over" feature of PHP from earlier versions.2) Because
crewSizeis private inAbstractDeathStar, when you're inside <DeathStar`, that property basically doesn't exist.If you combine these two facts, here's what's happening:
So basically, AbstractDeathStar has a
crewSizeproperty ANDDeathStarhas acrewSizeproperty...but they are two totally different properties! You can see this in the coding challenge - if you re-add the code that you printed above and hit "Check", on the browser, you will see that the "Crew Size" of "DeathStar 1" is empty. That's because this is coming from$deathStar1->getCrewSize(). And because thegetCrewSizemethod lives inAbstractDeathStar, it references itscrewSizeproperty, which was never set (only thecrewSizeproperty inDeathStar1was set.Phew! Does that make sense? It's actually a super fun, little PHP trivia question :).
Cheers!
Is the AbstractShip class supposed to be a true abstract class? I did not see it get declared as abstract.
It will be - next chapter ;). We're building towards the idea.
"Houston: no signs of life"
Start the conversation!