The idea is to create a DOM-like tree. But there are some restrictions, that only certain types can actually contain the other.
I want to use an interface|abstract class|superclass to implement some well known js-functions as appendChild, replaceChild etc.
I'm using the classes page, block and item, where pages can contain blocks and blocks can contain either blocks or items.
Example: Page is a web page, block could be an list element and item could be an list item element.
But these objects contain more than just html-data and the concepts goes beyond just plain HTML representation. It's an overall idea of managing items, wether they have an actual representation or are just abstract objects. The concept itself works for many different hierarchies.
What I want to achieve is to reuse as much code of the parent class as possible (adding a child is basically the same for all classes) but to differ the type hints to match the allowed types to add as a child.
There are basically four ways I found out myself:
So, if anyone is still willing to answer I'm happy for any proposition, idea or hint, which option to choose. I hope I could describe the issue well enough.
And if there is something i missed I'm thankful to hear it ;)
Code
Superclass way (works, but breaks precondition inheriting practice)
class Base {
public|protected function appendChild(Base $child) {
// do stuff
}
}
class Block extends Base {
public function appendChild(Block $child) {
parent::appendChild($child);
}
}
Interface way (Does not work. It must not)
interface Interface1 {
public function appendChild(Base $child);
}
class Base implements Interface1 {
public|protected function appendChild(Base $child) {
// do stuff
}
}
class Block extends Base{
public function appendChild(Block $child) {
parent::appendChild($child);
}
}
Edited parts are bold
Interface makes most sense to me. You can have one class that plays multiple roles, as it can implement multiple interfaces.
// you don't need any methods in the interfaces
class Foo implements Inline, Block {}
will work with both:
appendChild(Inline $foo); and appendChild(Block $foo);
and interfaces can extend each other, so there can be common interface for all your objects.
You can still use inheritance to reuse implementation, and you'll have flexibility to use inhertiance tree strictly for reuse of implementation, not limited by your page logic (you'll never be forced to make StaticSimpleton
extend HeavyDatabaseyWidget
).
If not interfaces, I'd go for option 5: just make appendChild
call $child->canBeChildOf($this)
and/or $this->accepts($child)
. Again, logic and implementation will be independent, and you'll have a lot of freedom with your logic.
PHP does type checks at run tmie, so use of type system doesn't buy you much anyway.