www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Static Factory

(Guess is didn't get sent, I guess I'm just a big spam bot cause
I keep getting flagged every post)

The following code demonstrates a way to have an easy factory in
D requiring very little work. I imagine it can be improved to
handle the abstract case(basically dependencies/constraints).

Any ideas on how to improve it? I think auto serialization of the
data of the object will end up being the default behavior. One
should be able to templatize the save/restore code. Once this is
all done I would think very little work would be required in
creating pluggable code(simply use the templates).


module main;
import std.file, std.stdio;

// Mixin iStaticFactory into an interface to provide generic
pluggable derived instantiation.
// Must use New(id, data) as a way to instantiate a new object of
type A(specified by ID) : T. New() is allowed but only provides
// default type and is not pluggable. A msg is given anywhere
New() is used(should only be used when pluggability is not
desired) or during mock up.
//
// The corresponding mixin template cStaticFactory must be used
in all derived types that are to be pluggable.
//
// The user must provide a way to store and retrieve the object
data to allow generic and configurable pluggability. As is,
// any derived type of T may be substituted for T dynamically..
mixin template iStaticFactory(T)
{
	 property string _getID(); 									// returns the type name for
this object
	static final T function(string data)[string] _Creators; 	// An
AA of functions that are registered by the classes which are
desired to be plugged into the interface T.
	
	// Generic New function that returns an initiated instance of a
derived type of T corresponding to data.ID.
	static final T New(string file = __FILE__, size_t line =
__LINE__, string mod = __MODULE__)(string id, string data = null)
	{
		if (id != null &&_Creators[id] != null) return
_Creators[id](data);
		return myA.New(null); // provides default type
	}
	
	// Non-Generic New function returning a default derived type of
T used for testing purposes or default object
	static final T New(string file = __FILE__, size_t line =
__LINE__, string mod = __MODULE__)()
	{
		pragma(msg, "StaticFactory: Not pluggable at
"~mod~":"~std.string.chomp(line.stringof, "u")~" ["~file~"]");
		return New(null);
	}
}

// Mixin cStaticFactory into any class A derived from interface T
to allow it to be pluggable. Mixin static final A New to provide
data initialization of object.
mixin template cStaticFactor(A, T) if (is(A : T))
{
	enum _ID = std.traits.fullyQualifiedName!A;
	 property string _getID() { return _ID; }
	
	// Registers this class with the _Creators of T's StaticFactory
allowing it to be used to create it's own type. (interface is
only used to ensure the class is registered at runtime. Could be
done elsewhere or dynamically)
	static interface iStaticFactory { static this() {
T._Creators[_ID] = &New; } }
	
	// Creates and instantiates this type with data. Override to
instantiate data.
	static final T New(string data) { A t = new A; if (data == null)
return t; return t; }
}



// Demo:

interface A
{
	mixin iStaticFactory!A;
	void foo();
}

class myA : A
{
	mixin cStaticFactor!(myA, A);
	void foo() { writeln("-------Called from myA"); }
}

class otherA : A
{
	mixin cStaticFactor!(otherA, A);
	void foo() { writeln("------------Called from otherA"); }
}



void main()
{
	enum fn = "tempSFdata.tmp";
	
	// load/create our object.
	A a = A.New(exists(fn) ? cast(string)read(fn, 100) : null);
	
	
	// Display object's typeDo something with the object
	writeln("Current object type is "~a._getID~" with output :");	
	a.foo();
	
	// Provide mechanism to change object
	foreach(k, v; A._Creators)
	{
		if (k == a._getID) continue;
		writeln("Would you like to change to "~k~" [y/n]");	if
(readln() == "n") continue;
		
		// Set a to new object type, assume no data
		a = v(null);
		std.file.write(fn, a._getID);
		writeln("Changed to "~k~"");
		break;
	}
	
}
Jan 29 2014