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\$

    1 Answer 1

    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\$CommentedFeb 21, 2014 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\$CommentedMar 3, 2014 at 15:13

    Start asking to get answers

    Find the answer to your question by asking.

    Ask question

    Explore related questions

    See similar questions with these tags.