Design Pattern Question: Encapsulation vs. Inheritance

I have a question that I have been working on for a long time. I am building a templating engine with two main classes Template.php

and Tag.php

, with a bunch of extension classes like Img.php

and String.php

.

The program works as follows:

The Template object creates Tag objects. Each tag object defines which extension class (img, string, etc.) should be implemented.

The point of the Tag class is to provide helper functions for each extension class like wrap('div'),

addClass('slideshow')

etc.

Each Img or String class is used to render code specific to what is required, so $Img->render()

will give something like<img src='blah.jpg' />

My question is:

Should I encapsulate all the extension functionality inside a Tag object like this:

Tag.php

function __construct($namespace, $args) {
    // Sort out namespace to determine which extension to call
    $this->extension = new $namespace($this); // Pass in Tag object so it can be used within extension

    return $this; // Tag object
}

function render() {
    return $this->extension->render();
}

      

Img.php

    function __construct(Tag $T) {
        $args = $T->getArgs();
        $T->addClass('img');
    }

    function render() {
        return '<img src="blah.jpg" />';
    }

      

Using:

$T = new Tag("img", array(...);
$T->render();

      


.... or I need to create more inheritance structure because "Img is a tag"

Tag.php

public static create($namespace, $args) {
    // Sort out namespace to determine which extension to call
    return new $namespace($args);

}

      

Img.php

class Img extends Tag {
    function __construct($args) {
        // Determine namespace then call create tag
        $T = parent::__construct($namespace, $args);
    }

    function render() {
        return '<img src="blah.jpg" />';
    }
}

      

Using:

$Img = Tag::create('img', array(...));
$Img->render();

      


One thing I need is a generic interface for creating custom tags, that is, I can instantiate Img (...) and then instantiate String (...), I need to instantiate each extension using a tag.

EDIT: Also just for clarification: the tag class has functions common to all extension classes, there shouldn't be any methods in the tag class that should be implemented in the extension classes. The tag class just provides helper functions.

I know this is somewhat vague from the question, I hope some of you have dealt with this in the past and can anticipate certain challenges when choosing each design template. If you have any other suggestions, I would love to hear them.

Thanks! Matt Mueller

+2


a source to share


2 answers


Inheritance will make more sense than encapsulating all the functions for different tags within the same class Tag

. Separation of concern is important, so it is best to go with an inheritance approach. Otherwise, you end up with a bunch of different tags specific to the same class, which is bad. This is a maintenance nightmare!

If you have any Img

-specific logic, put it in your class. You can put all common methods in a class Tag

. If it were Java, I would make it an Tag

abstract class or even an interface and have different implementations ( Img

, Div

etc.) Extend (in the case of an abstract class) or implement (in the case of an interface).



Better yet would be to have an interface Tag

and then an abstract class AbstractTag

that implements all of the common logic. Then your specific tags can implement the interface Tag

and extend AbstractTag

. However, I don't know if this is possible in PHP, but you can try to do something like this.

+2


a source


Neither new Tag('img')

nor Tag::create('img')

does it look convincing to me. I think that the decision to use the tag comes from the wrong place - in the Tag class, while it clearly belongs to the template. The tag and its descendants do not have to control how they are used.

I would suggest the following: create a tag hierarchy

 abstract class Tag { ...common methods.... }
 class ImgTag extends Tag { ...image specific methods... }
 class SpanTag extends Tag { ...image specific methods... }

      

and in Template just use the new WhateverTag when you need it.



 class Template...
    function insertImage
      $tag = new ImgTag($atts);
      $tag->render();

      

If tags are created in multiple places, it's nice to have a factory method, but it should still belong to the template

 class Template...
    function createImage
      return new ImgTag($atts);

    function insertOneImage
      $tag = $this->createImage
      $tag->render();

    function insertAnotherImage
      $tag = $this->createImage
      $tag->render();

      

As a general advice, avoid static methods at all costs. Static are just fancy aliases for global functions, nothing object oriented.

+1


a source







All Articles