I have an application in which a number of objects are all extending an abstract class which defines methods like create()
edit()
retrieve()
and delete()
. Since each of the child classes use the same logic for these functions, the abstract class defines that default behaviour, and in the few cases where it needs to be augmented, the child classes can override or use the hooks I've built in.
Now I'm having the situation where some of the child classes need to be made immutable, meaning that they shouldn't have edit()
or delete()
methods. This need sounds to me like a job for an interface named something like immutable
which the immutable classes could implement. Problem is that interfaces don't stop methods from being called, they just enforce a method's existence. So this is obviously not going to work.
Making two parent classes, one for mutable objects and one for immutable ones is ugly and is probably asking for problems down the line which maintenance. I could have the immutable objects override the offending methods with an empty method that did nothing, but that also seems messy and like I'm not doing proper OOP at that point.
So what would you suggest as the best way to allow a large set of classes to all inherit a set of methods, but for some of them to not inherit all of the methods? (The application in question is written php, but general OOP techniques from any language can still be helpful).
create an immutable-base class as a child of the base class. the immutable-base should implement final overrides of edit() and delete() which do nothing or throw an error.
Final, so that all immutable children are guaranteed not to be able to edit or delete
bonuses of this strategy
easily check if an object is immutable by testing for instanceof immutable-base
easily change objects from immutable and back again by modifing what it extends
I like Java's approach to this. Throw an exception. Create an UnsupportedOperationException
and for those implementations that shouldn't use a specific method throw one to let the user know they can't use this functionality for this implementation.
Another thought I wanted to throw out as a possible solution. Classes could implement an interface that looks like the following:
Interface Immutable {
const immutable = true;
}
and then the Base abstract class can write the delete()
and edit()
methods with
if (!$this->immutable) {
//do_stuff
}
This would also extend well to other classifications of class, like NonDeletable and NonEditable to allow for more fine grained behaviour.
Actually creating classes that have empty methods or throw errors is bad - such methods are confusing, they take up space and do nothing.
A better approach would be to make the immutable class the base class and make the mutable class extend it with adding methods for modification. This way each class has only those methods, that really belong there.
Here is super short workaround, make your method final and start it with:
if(self::class!=static::class) return;#or throw an error
It will not prevent inheritance itself, but methods will not work in children classes (with error or without - is up to you).
As of PHP 5.4, you can use Traits.
For example, you could make a base class that only includes the methods that all child classes have:
class EntityManager {
public function create() {/*...*/}
public function retrieve() {/*...*/}
}
Then you could define a couple of traits:
trait EditTrait {
public function edit() {/*...*/}
}
trait DeleteTrait {
public function delete() {/*...*/}
}
You would then create an immutable child class like this:
class LogManager extends EntityManager {
...
}
And a mutable child class like this:
class ContactManager extends EntityManager {
use EditTrait;
use DeleteTrait;
...
}
Traits have some advantages over some of the other solutions here such as: