Patterns
Patterns are currently a hot topic in the object-oriented programming community.
Patterns are abstractions which encapsulate a "way of doing things" that extends
beyond a single class. They represent likenesses between classes which are not
related by inheritance, or the pattern of interoperation between classes, often
classes within frameworks, but sometimes across frameworks.
The Converter Pattern
The Converter pattern is one which appears in a number of different places,
but for this example, we will start with the TStringList class of C++ Builder's
VCL.
TStringList is essentially a variable sized array of AnsiString, with the elements
addressible using a 0 based index. However, where the converter pattern comes
in is in the property "CommaString".
If you assign a comma delimited ASCII string to CommaString, it will automatically
be parsed into the Strings of the TStringList. By the same token, if you assign
the value of CommaString to some other AnsiString variable, that variable will
contain a comma-delimited "unparse" of the elements in the TStringList's Strings.
Once you have assigned a value to CommaString, you can immediately access the
fields of the string by a statement such as
myStringList->Strings[Index];
So here we have the concrete basis of the pattern. Let's extend this example,
by adding other translation capabilities to TStringList.
The Extended TStringList
Here's a header file:
class FieldParser: public TStringList
{
public:
__fastcall FieldParser(void);
__fastcall ~FieldParser(void);
published:
__property SpaceText = {read=GetSpaceText,
write=SetSpaceText};
__property TabText = {read=GetTabText,
write=SetTabText};
__property CharacterText = {read=GetCharacterText,
write=SetCharacterText};
private:
AnsiString __fastcall
GetTabText(void);
void __fastcall
SetTabText(AnsiString theTabText);
AnsiString __fastcall
GetSpaceText(void);
void __fastcall
SetSpaceText(AnsiString theSpaceText);
AnsiString __fastcall
GetCharacterText(void);
void __fastcall
SetCharacterText(AnsiString theCharacterText);
}; |
The implementation is relatively simple:
...
#include <vcl.h>
#pragma hdrstop
#include "FieldParser.h"
...
__fastcall FieldParser::FieldParser(void)
{
}
...
__fastcall FieldParser::~FieldParser(void)
{
}
...
AnsiString __fastcall FieldParser::GetTabText(void)
{
AnsiString Result;
for (int Index = 0; Index < Count; Index++)
{
Result+= Strings[Index];
if (Index < Count) Result+= "\t";
};
return Result;
}
...
void __fastcall FieldParser::SetTabText(AnsiString theTabText)
{
Clear();
AnsiString WithCommas;
for (int Index = 0; Index < theTabText.Length();
Index++)
{
WithCommas+= (char) ((theTabText[Index+1]
== '\t') ? ',' : theTabText[Index+1]);
};
CommaText = WithCommas;
}
...
AnsiString __fastcall FieldParser::GetSpaceText(void)
{
AnsiString Result;
for (int Index = 0; Index < Count; Index++)
{
Result+= Strings[Index];
if (Index < Count) Result+= "
";
};
return Result;
}
...
void __fastcall FieldParser::SetSpaceText(AnsiString theSpaceText)
{
Clear();
AnsiString WithCommas;
for (int Index = 0; Index < theSpaceText.Length();
Index++)
{
WithCommas+= (char) ((theSpaceText[Index+1]
== ' ') ? ',' : theSpaceText[Index+1]);
};
CommaText = WithCommas;
}
...
AnsiString __fastcall FieldParser::GetCharacterText(void)
{
AnsiString Result;
for (int Index = 0; Index < Count; Index++)
{
Result+= Strings[Index];
};
return Result;
}
...
void __fastcall FieldParser::SetCharacterText(AnsiString theCharacterText)
{
Clear();
AnsiString WithCommas;
for (int Index = 0; Index < theCharacterText.Length();
Index++)
{
WithCommas+= theCharacterText[Index+1];
if (Index < (theCharacterText.Length()-1))
WithCommas+= ',';
};
CommaText = WithCommas;
}
... |
Note how we leverage what is available from the ancestor class - for instance,
we convert incoming strings to comma delimited strings and then assign them
to CommaText, which takes care of actually splitting the string - why reinvent
the wheel, after all?
From this, you can see the core of the pattern: A central data structure which
is the source for all of the "get" methods, and which can be fed, in varying
ways, by the "set" methods. Remember, however, that the various settable property
formats should be well-suited to being part of the same object. For instance,
a date parser might better be implemented in its own class, rather than as an
augmentation of this class, since it is unlikely that a TabText, CommaText or
SpaceText would be interesting representations of a date.
|
|