Professional C__ - Marc Gregoire [491]
Example: Defining Styles in Web Pages
As you may already know, web pages are written in a simple text-based structure called HyperText Markup Language (HTML). In HTML, you can apply styles to a text by using style tags, such as and for bold and and for italic. The following line of HTML will display the message in bold:
A party? For me? Thanks!
The following line will display the message in bold italic:
A party? For me? Thanks!
Assume that you are writing an HTML editing application. Your users will be able to type in paragraphs of text and apply one or more styles to them. You could make each type of paragraph a new subclass, as shown in Figure 29-6, but that design could be cumbersome and would grow exponentially as new styles were added.
FIGURE 29-6
The alternative is to consider styled paragraphs not as types of paragraphs, but as decorated paragraphs. This leads to situations like the one shown in Figure 29-7, where an ItalicParagraph operates on a BoldParagraph, which in turn operates on a Paragraph. The recursive decoration of objects nests the styles in code just as they are nested in HTML.
FIGURE 29-7
Implementation of a Decorator
To decorate the Paragraph class with zero or more styles, you will need a hierarchy of styled Paragraph classes. Each of the styled Paragraph classes will be constructible from an existing Paragraph. This way, they can all decorate a Paragraph or a styled Paragraph. The most convenient way to implement the styled classes is as subclasses of Paragraph. Here is the Paragraph base class:
class Paragraph
{
public:
Paragraph(const string& inInitialText) : mText(inInitialText) {}
virtual string getHTML() const { return mText; }
protected:
string mText;
};
Code snippet from Decorator\Decorator.cpp
The BoldParagraph class will be a subclass of Paragraph so that it can override getHTML(). However, because we intend to use it as a decorator, its only public non-copy constructor takes a const reference to a Paragraph. Note that it passes an empty string to the Paragraph constructor because BoldParagraph doesn’t make use of the mText data member — its only purpose in subclassing Paragraph is to override getHTML().
class BoldParagraph : public Paragraph
{
public:
BoldParagraph(const Paragraph& inParagraph) :
Paragraph(""), mWrapped(inParagraph) {}
virtual string getHTML() const {
return "" + mWrapped.getHTML() + "";
}
protected:
const Paragraph& mWrapped;
};
Code snippet from Decorator\Decorator.cpp
The ItalicParagraph class is almost identical:
class ItalicParagraph : public Paragraph
{
public:
ItalicParagraph(const Paragraph& inParagraph) :
Paragraph(""), mWrapped(inParagraph) {}
virtual string getHTML() const {
return "" + mWrapped.getHTML() + "";
}
protected:
const Paragraph& mWrapped;
};
Code snippet from Decorator\Decorator.cpp
Again, remember that BoldParagraph and ItalicParagraph only subclass Paragraph so that they can override getHTML(). The content of the paragraph comes from the wrapped object, not from the mText data member.
Using a Decorator
From the user’s point of view, the decorator pattern is appealing because it is very easy to apply, and is transparent once applied. The client doesn’t need to know that a decorator has been employed at all. A BoldParagraph behaves just like a Paragraph.
Here is a quick example that creates and outputs a paragraph, first in bold, then in bold and italic:
Paragraph p("A party? For me? Thanks!");
// Bold
cout << BoldParagraph(p).getHTML() << endl;
// Bold and Italic
cout << ItalicParagraph(BoldParagraph(p)).getHTML()