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
a source to share
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.
a source to share
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.
a source to share