3
\$\begingroup\$

I want to create XML messages for webservice communication. These messages should be created from a pool of reusable elements. Therefore I have created different classes. A "factory" class, that only returns a message class. An element class, that consists of the reusable parts and the message classes that are the blueprints for the desired XML messages.

My code delivers the expected result, but I'm looking for a best practice. Especially a way to get rid of rewriting the same save() and __construct method in every of the message classes.

Update:

I have improved my example and moved the root property and the save() method from the message classes to the Elements class. I also changed the name of the method from save to getMessage as there is a DOMDocument method with the same name. Due to these changes I have little less redudant code. I still don't know if it is good practice, particularly to use the "grandparent" methods in the message classes.

// class to create webservice messages
class Messages{

    private function __construct(){}

    public static function get($type) {
        //some error handling if class not exists
        return new $type;
    }
}

// message no.1
class Message_1 extends Elements{

    public function __construct() {
        parent::__construct();
        $this->root = $this->createElement("message1");
    }

    public function add_anotherElement(){
        $this->root->appendChild($this->add_anotherElementBlock("foo", "bar"));
    }

    public function add_element(){
        $this->root->appendChild($this->add_someElementBlock("foo", "bar"));
    }
}

// message no.2
class Message_2 extends Elements {

    public function __construct() {
        parent::__construct();
        $this->root = $this->createElement("message2");
    }

    public function add_elements(){
        $this->root->appendChild($this->add_anotherElementBlock("foo", "bar"));
        $this->root->appendChild($this->add_someElementBlock("foo", "bar"));
    }

}

// message no.3
class Message_3 extends Elements {

    public function __construct() {
        parent::__construct();
        $this->root = $this->createElement("message3");
    }

    public function add_element(){
        // unique Element
        $this->root->appendChild($this->createElement("foo", "bar"));
    }

}

// reusable elements
class Elements extends DOMDocument{

    protected $root;


    protected function add_someElementBlock($foo, $bar) {
        $node = $this->createElement("root");

        $attr = $this->createAttribute("id");
        $attr->value = $foo;
        $node->appendChild($attr);

        $subnode = $this->createElement("sub",$bar);

        $node->appendChild($subnode);

        return $node;
    }

    protected function add_anotherElementBlock($foo, $bar) {
        $node = $this->createElement("anotherRoot");

        $subnode = $this->createElement("anotherSubNode",$bar);
        $attr = $this->createAttribute("anotherAttribute");
        $attr->value = $foo;
        $subnode->appendChild($attr);

        $node->appendChild($subnode);

        return $node;
    }

    public function getMessage(){
        return $this->saveXML($this->root);
    }
}

$message1 = Messages::get('Message_1');
$message1->add_element();
$message1->add_anotherElement();

$message2 = Messages::get('Message_2');
$message2->add_elements();

$message3 = Messages::get('Message_3');
$message3->add_element();

//********************************************
echo "<pre>";
print_r(htmlentities($message1->getMessage()));
echo "</pre>";
echo "<hr />";
echo "<pre>";
print_r(htmlentities($message2->getMessage()));
echo "</pre>";
echo "<hr />";
echo "<pre>";
print_r(htmlentities($message3->getMessage()));
echo "</pre>";
\$\endgroup\$
3
\$\begingroup\$

Your message classes currently have two responsibilities: representing an application logic message and transforming itself into a representation. If you want to add other ways of serialization you have to modify the message classes itself.

The message classes only should represent the message while some other facility should be responsible for serializing your messages into xml. This keeps the interface of your message classes clean (not inheriting all those methods from DOMDocument) and allows to easily add other serialization formats.

This particular situation wants a strategy pattern: a dedicated MessageXmlSerializer takes a message, creates the surrogate DOMDocument and itself delegates the serialization of the message further to specialized classes for each message:

<?php

class MessageXmlSerializer {

    private $messageSerializers;

    public function serialize(Message $message) {
        $node = $this->createDocumentContainer();

        foreach ($this->messageSerializers as $serializer) {
            if ($serializer->canHandle($message)) {
                $node->appendChild($serializer->serialize($message));
            }
        }
    }

    protected function createDocumentContainer() {
        $node = new DOMDocument();
        // Do your intialization here
        return $node;
    }
}

The dedicated serializers are rather simple. Of course they all share a common interface:

interface MessageSerializer {
    public function canHandle(Message $message);

    public function serialize(Message $message);
}

And finally the serializer specialized on serializing Message_1

class MessageOneSerializer implements MessageSerializer {
    public function canHandle(Message $message) {
        return $message instanceof Message_1;
    }

    public function serialize(Message $message) {
        // create your node here and return it
    }
}

So far the basic concept. Now new message types can easily be added and serializiation the messages itself are separated. Of course, as for any pattern, this concept can (should) be extended and adjusted to your needs.

\$\endgroup\$
2
  • \$\begingroup\$ Looks very promising to me. The non encapsulated DOMDocument methods were really unpleasent. I yet don't have an idea how the messages should look like in this pattern, but I try to implement this pattern this weekend. \$\endgroup\$ – user3333137 Feb 21 '14 at 16:49
  • \$\begingroup\$ I didn't used this approach for creating the messages, but for parsing the response. Thanks a lot for your answer. \$\endgroup\$ – user3333137 Mar 3 '14 at 15:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.