views:

60

answers:

1

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:

  1. I use an interface, which allows me to type hint to the superclass but not to change these.
  2. I use a superclass with public methods, so i can redefine the type hints (which is totally against usual practices when heriting preconditions).
  3. I use a superclass with protected methods, which seems still being quite quirky.
  4. I get rid of any superclass and just define almost the same class several times.
  5. I use a method to check for the type, despite the feature of type hints.

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

A: 

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.

porneL
Ok, maybe it's just too late but I don't get it. If I use an interface I can't redeclare any preconditions (or type hints). So I need to stick to the type hints indicated in the interface (any, superclass, whatever). How do multiple interfaces help me there?Added some code to the question, hope it is easier to see.
PvB
I know, the catchable fatal error ;) Many thanks for your quick answer, I just needed to hear from another person than myself that it's not going to work without $this->accepts() or alike. I will flag your answer as the correct one tomorrow morning, maybe someone has a brilliant idea to solve this issue ;)
PvB