Note: It's been years since I did PHP, please forgive minor syntax mistakes.
It seems you're misunderstand what final
does and why it should be used.
final public function getName()
{
return this->name;
}
The final
keyword doesn't prevent this->name
from being changed. As evidence by the following example:
class Dog
{
private $name;
public function __constructor($name)
{
this->name = $name;
}
final public function getName()
{
return this->name;
}
public function setName($newName)
{
this->name = $newName;
}
}
What final
does is prevent inheritance overriding. Normally, derived classes can override the implementation of a base class' method, e.g.:
class NobleDog extends Dog
{
public function getName()
{
return "Sir " . $this->name;
}
}
If you assume my implementation of Dog
and NobleDog
, you can see that you can always change a dog's name, but even when you change their name to e.g. "NEWNAME", a noble dog will still be called "Sir NEWNAME", whereas a normal dog will just be called "NEWNAME".
By making getName()
final in the base class, the NobleDog
derived class cannot override this anymore, and it therefore cannot e.g. prepend "Sir" to a dog's getName()
result.
final public function registerDog(Dog $dog)
{
this->registredDogs[$dog->getName()] = $dog;
}
The problem here is that you register the dog with a specific name, and if that dog's name changes afterwards (for whatever reason), the registration in registredDogs
itself is not updated and still uses the old name.
Because this->name
is made reasonably immutable in your Dog
class, that is currently not an issue. However, it's always possible that someone later decides to add a method to Dog
that does change its name
.
Whether you consider that a real issue you need to actively protect against is up to you. I tend to expect developers to understand that this name shouldn't be changed since there's no setter in the class and it's made private
, but your mileage (and developer team) may vary.
Sadly, there is no real "readonly" property in PHP, which would prevent any issues with this name unexpectedly changing.
If your main concern is being able to query registredDogs
with the current name of the dog, regardless of whether it has been changed or not, then you shouldn't set its key based on the dog's name, but instead you have to dynamically query your collection and find the matching name. This ensure that the current name is the one you're matching against.
Something like:
public function getDog($name)
{
$found= null;
foreach($registredDogs as $dog)
{
if ($name == $dog->getName())
{
$found = $dog;
break;
}
}
return $found;
}
This is of course less performant than an index-based lookup, but it does allow for changed values to be accounted for.
Please excuse any syntax errors I may have made, it's been a while since I did PHP.
Overwriting the selectManyAndReturn method can change the behavior of the class, people can overwrite it and run selectOne for example instead of selectMany, OK, I can make this method final also to prevent this, but by this way I am also preventing people form adding new features to the method
Just because someone can override a method doesn't mean that literally any valid code they write is therefore a correct implementation.
Selecting one element instead of many feels like an incorrect implementation, but e.g. selecting many elements and then doing some additional filtering on that array sounds perfectly fine.
Developers writing incorrect (but syntactically valid) code is not something you can prevent. If it were, then we would've either solved P=NP or we wouldn't never need to test/debug ever again.
What should I do? Is forgetting final and private methods completely good idea? like many applications and many programming languages? or Can you tell me when exactly we should make a method final?
First of all, for this specific issue, assume that developers will write a correct implementation when overriding your method. The correctness of what they do isn't relevant here.
What is relevant, is whether you want developers to be able to change your method implementation in a derived class. Sometimes you want that, sometimes you don't. That's very contextual.
If you're making a base class which is intended to have some reusable component that is forced to be consistent between derivations of this base class, then you're likely going to want to be using final
.
This is slightly language specific. PHP makes everything overridable by default and requires you to explicitly prevent overriding. C#, by comparison, makes everything "final" by default and requires you to explicitly make a method virtual
to enable overriding.
I'm a C# dev, so I'm naturally a fan of opt-in overriding, i.e. nothing can be overridden except when explicitly made to be. But PHP devs are liable to think the other way, because that's what they're used to.
I'd suggest sticking with whatever your language chose as the default behavior, simply because it means that you don't have to pepper your final
/virtual
keywords all across your codebase.
Edit: Robert Harvey makes a reasonable point that you might want to be more liberal with final
when dealing with third parties using your code. It all depends on whether abusing it is actually harmful to you, or simply renders the third party's product defective. In the latter case, there's less reason for you to actively defend against this.
final
until I can't. And when I can't I usually make a classabstract
instead.