Factory Pattern in C++0x: un piccolo esempio…

Qualche giorno fa l’amico Martino (che fra l’altro ha appena aperto un suo blog: in bocca al lupo!) mi ha sottoposto un bel problema, ossia creare un oggetto per creare altri oggetti (factory) usando i variadic template del C++0x per gestire il diverso numero di parametri di ciascun tipo di oggetto da creare.

Il problema

In altre parole, seguire il Factory Pattern ma consentire all’utente di impostare il corretto numero di parametri per ciascuna classe creata.

Ad esempio, se stiamo creando delle figure, un cerchio può richiedere (x, y, raggio => 3 parametri) mentre un rettangolo potrebbe richiedere le coordinate di due punti (x1, y1, x2, y2 => 4 parametri).

Quello che vogliamo realizzareè qualcosa che crei gli oggetti ma gestica anche ed opportunamente il diverso numero di parametri.

Al posto dei variadic_template, ho suggerito a Martino le più pratiche initializer_list (già trattate in precedenza).

Codice d’esempio

Eccovi quindi un piccolo codice-esempio riassuntivo, certamente limitato (il numero dei tipi è variabile, il tipo stesso no), ma che spero vi piaccia.

In aggiunta a tutto quanto detto poc’anzi, il codice mostra anche l’uso dei nuovi strongly-typed enums: si tratta di un nuovo modo per definire tipi enumerativi ma senza conversioni implicite da/verso interi (come avviene in linguaggi come il C#).

Inoltre è possibile definire esplicitamente il tipo interno per le varie costanti. Internamente si possono usare, ad esempio, unsigned int, ma dall’esterno l’enumerativo non ammette conversioni implicite al tipo interno: non a caso è detto “fortemente tipizzato”.

/**
 *	FILE      : FactoryPatternTest.cpp
 *	AUTHOR    : Gian Paolo "JP" Ghilardi (http://rejex.wordpress.com)
 *	LICENSE   : released under the terms of GPL v2.0 ("only")
 *	COMPILE   : g++ -Wall -Winline -pedantic -std=c++0x
 *	            FactoryPatternTest.cpp -o FactoryPatternTest
 *	PURPOSE   : simple C++0x example showing the new initializer list
 *	            and uniform initialization facilities applied to
 *	            Factory Pattern Design [5]
 *
 *	TESTED ON :
 *	- Windows 7, x86 32-bit, G++ 4.4.1
 *
 *	REFERENCES:
 *	[1]: http://en.wikipedia.org/wiki/C%2B%2B0x#Initializer_lists
 *	[2]: http://en.wikipedia.org/wiki/C%2B%2B0x#Type_inference
 *	[3]: http://en.wikipedia.org/wiki/C%2B%2B0x#Uniform_initialization
 *	[4]: http://en.wikipedia.org/wiki/C%2B%2B0x#Strongly_typed_enumerations
 *	[5]: http://en.wikipedia.org/wiki/Factory_pattern
 */

#include <cstdlib>
#include <iostream>

using namespace std;

// base class
struct Shape
{
	Shape(){}
	virtual void whoAmI() { cout << "I am a generic shape" << endl; }
};

// simple circle class (3 params required)
class Circle: public Shape
{
	private:
		double x;
		double y;
		double r;

	public:
		Circle(std::initializer_list<double> params)
		{
			if(params.size() != 3)
				throw string("Circle needs 3 params (x, y, radius)!");

			auto i = params.begin();
			x =  *i++;
			y =  *i++;
			r =  *i;
		}

		void whoAmI()
		{
			cout << "I am a circle (" << x << "," << y
					<< "," << r << ")" << endl;
		}
};

// simple square class (4 params required)
class Rectangle: public Shape
{
	private:
		double x1;
		double x2;
		double y1;
		double y2;

	public:
		Rectangle(std::initializer_list<double> params)
		{
			if(params.size() != 4)
				throw string("Rectangle needs 4 params (x1, y1, x2, y2)!");

			auto i = params.begin();
			x1 = *i++;
			y1 = *i++;
			x2 = *i++;
			y2 = *i;
		}
		virtual void whoAmI()
		{
			cout << "I am a rectangle (" << x1 << "," << y1 << ","
					<< x2 << "," << y2 << ")" << endl;
		}
};

// strongly-typed enum [4]
enum class ShapeType
{
	SHAPE, CIRCLE, RECTANGLE
};

Shape* make_shape(ShapeType type, std::initializer_list<double> params)
{
	return	(type == ShapeType::CIRCLE)    ? new Circle(params)    :
			(type == ShapeType::RECTANGLE) ? new Rectangle(params) :
					                         new Shape();
}

int main(int argc, char **argv)
{
	// create a circle...
	Shape* circle = make_shape(ShapeType::CIRCLE, { 1.0, 2.0, 3.0 });
	circle->whoAmI();

	// create a rectangle...
	Shape* rectangle = make_shape(ShapeType::RECTANGLE, { 0, 0, 10, 10 });
	rectangle->whoAmI();

	try
	{
		// this will raise an exception
		Shape* rectangleErr = make_shape(ShapeType::RECTANGLE, { 0, 0, 10, 10, 5 });
		rectangleErr->whoAmI();
	}
	catch(string ex)
	{
		cerr << ex << endl;
	}
	return EXIT_SUCCESS;
}

Il codice in esecuzione

Questo è l’output del programma in esecuzione (si noti che Eclipse CDT non supporta ancora le nuove feature del C++0x)…

Factory Pattern in C++: a simple example

P.S.: lanciare eccezioni dai/nei costruttori è perfettamente normale ed accettabile (cfr. FAQ).

Contrassegnato da tag , , , , , , ,

5 thoughts on “Factory Pattern in C++0x: un piccolo esempio…

  1. [...] puntata vi lascio il link al favoloso e sempre interessante blog di Paolo (JP in arte) dove nel suo ultimo post tratta sempre di questo pattern con un’altra implementazione con la [...]

  2. contezero74 scrive:

    Ciao JP,
    carino il post, soprattutto perché hai mostrato le initializer_list… ma come sei io sono alla vecchia maniera e quindi preferisco questa variante:

    class Shape {
    public:
    	Shape() {}
    	virtual void whoAmI() = 0;
    };
    
    // simple circle class (3 params required)
    class Circle: public Shape {
    public:
    	Circle(double x, double y, double r) : _x(x), _y(y), _r(r) {}
    
    	void whoAmI() {
    		cout	<< "I am a circle (" << _x << "," << _y
    				<< "," << _r << ")" << endl;
    	}
    
    private:
    	double _x , _y, _r;
    };
    
    // simple square class (4 params required)
    class Rectangle: public Shape {
    public:
    	Rectangle(int x1, int y1, int x2, int y2) : _x1(x1), _y1(y1), _x2(x2), _y2(y2) {}
    
    	void whoAmI() {
    		cout	<< "I am a rectangle (" << _x1 << "," << _y1 << ","
    			<< _x2 << "," << _y2 << ")" << endl;
    	}
    
    private:
    	int _x1, _x2, _y1, _y2;
    };
    
    class ShapeType {
    protected:
    	ShapeType(){}
    };
    
    class CircleShape : public ShapeType {
    public:
    	CircleShape() {}
    };
    
    class RectangleShape : public ShapeType {
    public:
    	RectangleShape() {}
    };
    
    class factory {
    public:
    	static CircleShape CIRCLE_SHAPE;
    	static RectangleShape RECTANGLE_SHAPE;
    
    	Shape* make_shape(CircleShape type, double x, double y, double r) {
    		return new Circle(x, y, r);
    	}
    
    	Shape* make_shape(RectangleShape type, int x1, int y1, int x2, int y2) {
    		return new Rectangle(x1, y1, x2, y2);
    	}
    
    	static factory& getInstance() {
    		if (NULL == Instance) {
    			Instance = new factory();
    		}
    
    		return *Instance;
    	}
    
    private:
    	factory() {}
    
    	static factory *Instance;
    };
    
    factory *factory::Instance = NULL;
    CircleShape factory::CIRCLE_SHAPE = CircleShape();
    RectangleShape factory::RECTANGLE_SHAPE = RectangleShape();
    
    int main(int argc, char **argv)
    {
    	// create a circle...
    	Shape* circle = factory::getInstance().make_shape(factory::CIRCLE_SHAPE, 1.0, 2.0, 3.0);
    	circle->whoAmI();
    
    	// create a rectangle...
    	Shape* rectangle = factory::getInstance().make_shape(factory::RECTANGLE_SHAPE, 0, 0, 10, 10);
    	rectangle->whoAmI();
    
    	/*
    	try
    	{
    		// this will raise an exception
    		Shape* rectangleErr = factory::getInstance().make_shape(factory::CIRCLE_SHAPE, 0, 0, 10, 10);
    		rectangleErr->whoAmI();
    	}
    	catch(Exception ex)
    	{
    		cerr << ex << endl;
    	}
    	*/
    	return EXIT_SUCCESS;
    }
    

    A fronte di poco codice in più hai il vantaggio di avere il controllo della creazione a compile time ;)

    cheers :)

  3. jp scrive:

    Il Singleton Pattern mi piace, però la tua variante nel complesso è troppo “old school” (scherzo, ovviamente). :)

    Lo scopo era giusto mostrare dal vivo le initializer list e le strongly-typed enum.

    Ciao & grazie di essere passato! :)

    PS: ho dato un’occhiata all’ultimo draft dello standard C++0x appena uscito e ho trovato una cosa che proprio non mi va giù sulle lambda. Appena esce il G++ 4.5, ne parlerò di sicuro…

  4. contezero74 scrive:

    Ciao JP,
    sai che sono old school :)

    P.S. Io ho messo il comando per formattare il codice… ma si vede che ho fatto qualche cavolata ;)

    cheers e buon we :)

  5. jp scrive:

    Ho ricontrollato: era tutto corretto tranne “cpp” al posto di “c++” come nome del linguaggio per il syntax highlighting. ^^’

    Per certi aspetti il C++ nuovo non è davvero male e dovrai fartene una ragione, mi sa. :P

Lascia un Commento

Fill in your details below or click an icon to log in:

Logo WordPress.com

You are commenting using your WordPress.com account. Log Out / Modifica )

Foto Twitter

You are commenting using your Twitter account. Log Out / Modifica )

Foto di Facebook

You are commenting using your Facebook account. Log Out / Modifica )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 259 other followers