Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Factory Method Using Is/As Operator

Tags:

c#

factory

I have factory that looks something like the following snippet. Foo is a wrapper class for Bar and in most cases (but not all), there is a 1:1 mapping. As a rule, Bar cannot know anything about Foo, yet Foo takes an instance of Bar. Is there a better/cleaner approach to doing this?

public Foo Make( Bar obj )
{
    if( obj is Bar1 )
        return new Foo1( obj as Bar1 );
    if( obj is Bar2 )
        return new Foo2( obj as Bar2 );
    if( obj is Bar3 )
        return new Foo3( obj as Bar3 );
    if( obj is Bar4 )
        return new Foo3( obj as Bar4 ); // same wrapper as Bar3
    throw new ArgumentException();
}

At first glance, this question might look like a duplicate (maybe it is), but I haven't seen one exactly like it. Here is one that is close, but not quite:

Factory based on Typeof or is a

like image 817
Swim Avatar asked Jul 21 '26 00:07

Swim


2 Answers

If these are reference types, then calling as after is is an unnecessary expense. The usual idiom is to cast with as and check for null.

Taking a step back from micro-optimization, it looks like you could use some of the techniques in the article you linked to. In specific, you could create a dictionary keyed on the type, with the value being a delegate that constructs an instance. The delegate would take a (child of) Bar and return a (child of) Foo. Ideally, each child of Foo would register itself to the dictionary, which could be static within Foo itself.

Here's some sample code:

// Foo creator delegate.
public delegate Foo CreateFoo(Bar bar);

// Lookup of creators, for each type of Bar.
public static Dictionary<Type, CreateFoo> Factory = new Dictionary<Type, CreateFoo>();

// Registration.
Factory.Add(typeof(Bar1), (b => new Foo1(b)));

// Factory method.
static Foo Create(Bar bar)
{
    CreateFoo cf;
    if (!Factory.TryGetValue(bar.GetType(), out cf))
        return null;

    return cf(bar);
}
like image 112
Steven Sudit Avatar answered Jul 23 '26 13:07

Steven Sudit


I'm not sure what you actually want to achieve. I would probably try to make it more generic.

You could use attributes on Foo, which Bar it supports, then you create a list in a initializing phase. We are doing quite a lot of stuff like this, it make adding and connecting new classes very easy.

private Dictionary<Type, Type> fooOfBar = new Dictionary<Type, Type>();
public initialize()
{
  // you could scan all types in the assembly of a certain base class
  // (fooType) and read the attribute

  fooOfBar.Add(attribute.BarType, fooType);
}

public Foo Make( Bar obj )
{
  return (Foo)Activator.CreateInstance(fooOfBar(obj.GetType()), obj);
}
like image 31
Stefan Steinegger Avatar answered Jul 23 '26 13:07

Stefan Steinegger