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 thesave()
method from the message classes to theElements
class. I also changed the name of the method fromsave
togetMessage
as there is aDOMDocument
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>";
1 Answer 1
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.
-
\$\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– user33331372014年02月21日 16:49:37 +00:00Commented Feb 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\$user3333137– user33331372014年03月03日 15:13:01 +00:00Commented Mar 3, 2014 at 15:13
Explore related questions
See similar questions with these tags.