Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Professional C++ [eng].pdf
Скачиваний:
284
Добавлен:
16.08.2013
Размер:
11.09 Mб
Скачать

Applying Design Patterns

When used with the example file from Chapter 24, the output will be:

root name: dialogue subelement name: sentence subelement speaker: Marni

subelement text data: Let’s go get some ice cream. subelement name: sentence

subelement speaker: Scott

subelement text data: After I’m done writing this C++ book.

The Decorator Pattern

The decorator pattern is exactly what it sounds like — a “decoration” on an object. The pattern is used to change the behavior of an object at runtime. Decorators are a lot like subclasses, but their effects can be temporary. For example, if you have a stream of data that you are parsing and you reach data that represents an image, you could temporarily decorate the stream object with an ImageStream object. The ImageStream constructor would take the stream object as a parameter and would have built-in knowledge of image parsing. Once the image was parsed, you could continue using the original object to parse the remainder of the stream. The ImageStream acts as a decorator because it adds new functionality (image parsing) to an existing object (a stream).

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 <B> and </B> for bold and <I> and </I> for italic. The following line of HTML will display the message in bold:

<B>A party? For me? Thanks!</B>

The following line will display the message in bold italic:

<I><B>A party? For me? Thanks!</B></I>

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 26-5, but that design could be cumbersome and would grow exponentially as new styles were added.

Paragraph

BoldParagraph

ItalicParagraph

BoldItalicParagraph

Figure 26-5

773

Chapter 26

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 26-6, 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.

ItalicParagraph

BoldParagraph

Paragraph

Figure 26-6

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 able to be constructed 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, with inlined method implementations:

class Paragraph

{

public:

Paragraph(const string& inInitialText) : mText(inInitialText) {}

virtual string getHTML() const { return mText; }

protected: string mText;

};

The BoldParagraph class will be a subclass of Paragraph so that it can override getHTML(). However, vecause we intend to use it as a decorator, its only public noncopy 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().

774

Applying Design Patterns

class BoldParagraph : public Paragraph

{

public:

BoldParagraph(const Paragraph& inParagraph) : Paragraph(“”), mWrapped(inParagraph) {}

virtual string getHTML() const {

return “<B>” + mWrapped.getHTML() + “</B>”;

}

protected:

const Paragraph& mWrapped;

};

The ItalicParagraph class is almost identical:

class ItalicParagraph : public Paragraph

{

public:

ItalicParagraph(const Paragraph& inParagraph) : Paragraph(“”), mWrapped(inParagraph) {}

virtual string getHTML() const {

return “<I>” + mWrapped.getHTML() + “</I>”;

}

protected:

const Paragraph& mWrapped;

};

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 program that creates and outputs a paragraph, first in bold, then in bold and italic:

int main(int argc, char** argv)

{

Paragraph p(“A party? For me? Thanks!”);

// Bold

cout << BoldParagraph(p).getHTML() << endl;

// Bold and Italic

cout << ItalicParagraph(BoldParagraph(p)).getHTML() << endl;

}

775