1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
|
/****************************************************************************
** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
**
** This file is part of the KD Tools library.
**
** Licensees holding valid commercial KD Tools licenses may use this file in
** accordance with the KD Tools Commercial License Agreement provided with
** the Software.
**
**
** This file may be distributed and/or modified under the terms of the
** GNU Lesser General Public License version 2 and version 3 as published by the
** Free Software Foundation and appearing in the file LICENSE.LGPL included.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** Contact info@kdab.com if any conditions of this licensing are not
** clear to you.
**
**********************************************************************/
#include "kdgenericfactory.h"
/*!
\class KDGenericFactory
\ingroup core
\brief Template based generic factory implementation
\since_c 2.1
(The exception safety of this class has not been evaluated yet.)
KDGenericFactory is an implemention of of the factory pattern. It can be used to
"produce" instances of different classes having a common superclass
T_Product. The user of the
factory registers those producable classes in the factory by using an identifier
(T_Identifier, defaulting to QString). That identifer can then be used to
produce as many instances of the registered product as he wants.
The advanced user can even choose the type of the map the factory is using to store its
FactoryFunctions by passing a T_Map template parameter. It defaults to QHash. KDGenericFactory
expects it to be a template class accepting T_Identifier and FactoryFunction as parameters.
Additionally it needs to provide:
\li\link QHash::const_iterator a nested %const_iterator \endlink typedef for an iterator type that when dereferenced has type ((const) reference to) FactoryFunction (Qt convention),
\li\link QHash::insert %insert( T_Identifier, FactoryFunction ) \endlink, which must overwrite any existing entries with the same identifier.
\li\link QHash::find %find( T_Identifier ) \endlink,
\li\link QHash::end %end() \endlink,
\li\link QHash::size %size() \endlink,
\li\link QHash::remove %remove( T_Identifier ) \endlink, and
\li\link QHash::keys %keys ) \endlink, returning a QList<T_Identifier>.
The only two class templates that currently match this concept are
QHash and QMap. QMultiHash and QMulitMap do not work, since they
violate the requirement on insert() above, and std::map and
std::unordered_map do not match because they don't have keys() and
because a dereferenced iterator has type
std::pair<const T_Identifier,FactoryFunction>
instead of just FactoryFunction.
\section general-use General Use
The following example shows how the general use case of KDGenericFactory looks like:
\code
class Fruit
{
};
class Apple : public Fruit
{
};
class Pear : public Fruit
{
};
int main()
{
// creates a common fruit "factory"
KDGenericFactory< Fruit > fruitPlantation;
// registers the product "Apple"
fruitPlantation.registerProduct< Apple >( "Apple" );
// registers the product "Pear"
fruitPlantation.registerProduct< Pear >( "Pear" );
// lets create some stuff - here comes our tasty apple:
Fruit* myApple = fruitPlantation.create( "Apple" );
// and a pear, please:
Fruit* myPear = fruitPlantation.create( "Pear" );
// ohh - that doesn't work, returns a null pointer:
Fruit* myCherry = fruitPlantation.create( "Cherry" );
}
\endcode
*/
/*!
\fn KDGenericFactory::~KDGenericFactory
Destructor.
*/
/*!
\typedef KDGenericFactory::FactoryFunction
This typedef defines a factory function producing an object of type T_Product.
*/
/*!
\fn KDGenericFactory::registerProduct( const T_Identifier& name )
Registers a product of type T, identified by \a name in the factory.
Any type with the same name gets unregistered.
If a product was registered via this method, it will be created using its
default constructor.
*/
/*!
\fn KDGenericFactory::unregisterProduct( const T_Identifier& name )
Unregisters the previously registered product identified by \a name from the factory.
If no such product is known, nothing is done.
*/
/*!
\fn KDGenericFactory::productCount() const
Returns the number of different products known to the factory.
*/
/*!
\fn KDGenericFactory::availableProducts() const
Returns the list of products known to the factory.
*/
/*!
\fn KDGenericFactory::create( const T_Identifier& name ) const
Creates and returns a product of the type identified by \a name.
Ownership of the product is transferred to the caller.
*/
/*!
\fn KDGenericFactory::registerProductionFunction( const T_Identifier& name, FactoryFunction create )
Subclasses can use this method to register their own FactoryFunction \a create to create products of
type T, identified by \a name. When a product is registered via this method, it will be created
by calling create().
*/
#ifdef KDTOOLSCORE_UNITTESTS
#include <KDUnitTest/test.h>
#include <QStringList>
#include <QMap>
class Fruit
{
public:
virtual ~Fruit() {}
};
class Apple : public Fruit
{
};
class Pear : public Fruit
{
};
std::ostream& operator<<( std::ostream& stream, const QStringList& list )
{
stream << "QStringList(";
for( QStringList::const_iterator it = list.begin(); it != list.end(); ++it )
{
stream << " " << it->toLocal8Bit().data();
if( it + 1 != list.end() )
stream << ",";
}
stream << " )";
return stream;
}
class KDGenericFactoryTest : public KDUnitTest::Test {
public:
KDGenericFactoryTest() : Test( "KDGenericFactory" ) {}
void run() {
doRun<QHash>();
doRun<QMap>();
}
template <template <typename U, typename V> class T_Map>
void doRun();
};
KDAB_EXPORT_UNITTEST( KDGenericFactoryTest, "kdcoretools" )
template <template <typename U, typename V> class T_Map>
void KDGenericFactoryTest::doRun() {
{
KDGenericFactory< Fruit, QString, T_Map > fruitPlantation;
assertEqual( fruitPlantation.productCount(), 0U );
assertEqual( fruitPlantation.availableProducts(), QStringList() );
fruitPlantation.template registerProduct< Apple >( QLatin1String( "Apple" ) );
assertEqual( fruitPlantation.productCount(), 1U );
assertEqual( fruitPlantation.availableProducts(), QStringList( QLatin1String( "Apple" ) ) );
fruitPlantation.template registerProduct< Pear >( QLatin1String( "Pear" ) );
assertEqual( fruitPlantation.productCount(), 2U );
Fruit* fruit = 0;
fruit = fruitPlantation.create( QLatin1String( "Apple" ) );
assertNotNull( fruit );
assertNotNull( dynamic_cast< Apple* >( fruit ) );
fruit = fruitPlantation.create( QLatin1String( "Pear" ) );
assertNotNull( fruit );
assertNotNull( dynamic_cast< Pear* >( fruit ) );
fruit = fruitPlantation.create( QLatin1String( "Cherry" ) );
assertNull( fruit );
fruitPlantation.unregisterProduct( QLatin1String( "Apple" ) );
assertEqual( fruitPlantation.productCount(), 1U );
assertEqual( fruitPlantation.availableProducts(), QStringList( QLatin1String( "Pear" ) ) );
fruit = fruitPlantation.create( QLatin1String( "Apple" ) );
assertNull( fruit );
fruit = fruitPlantation.create( QLatin1String( "Pear" ) );
assertNotNull( fruit );
assertNotNull( dynamic_cast< Pear* >( fruit ) );
fruitPlantation.unregisterProduct( QLatin1String( "Pear" ) );
assertEqual( fruitPlantation.productCount(), 0U );
fruit = fruitPlantation.create( QLatin1String( "Pear" ) );
assertNull( fruit );
}
}
#endif // KDTOOLSCORE_UNITTESTS
|