Generics and factories

I am new to generics and trying to figure out how I can return an instance of a class whose base is common from the factory. See the sample code below. Problems stand out in the factory class:

public abstract class MyGenericBaseClass<T> { public string Foo() {...} } public sealed class MyDerivedIntClass : MyGenericBaseClass<int> { } public sealed class MyDerivedStringClass : MyGenericBaseClass<string> { } public static class MyClassFactory { public static MyGenericBaseClass<T> CreateMyClass<T>() { // ********************************************** if (typeof(T) == typeof(int)) { return new MyDerivedIntClass(); } if (typeof(T) == typeof(string)) { return new MyDerivedStringClass(); } // ********************************************** } } 

How do I get around this?

Thanks for the ton in advance

Ohgee

+4
source share
7 answers

In most of my common classes, I put a non-common interface. In fact, I would do something like this:

 public interface INonGenericInterface { string Foo(); } public abstract class MyGenericBaseClass<T> : INonGenericInterface { public string Foo() {...} } public static class MyClassFactory { public static INonGenericInterface CreateMyDerivedIntClass() { return new MyDerivedIntClass(); } public static INonGenericInterface CreateMyDerivedStringClass() { return new MyDerivedStringClass(); } } 

This way you clearly indicate which types can be created and still separate the caller from specific types.

Of course, for this scenario you do not necessarily need a common interface, but in fact you most likely will need it.

+7
source

First of all, it is not clear why you are creating classes derived from your common class, instead of directly using the common class. It is also not clear why you need a factory, and not just create any classes that you need directly. These patterns are important and useful, often the only way to solve certain problems, but they are not requirements. You must have good reasons for their implementation.

I assume that you really have good reasons why you left for the sake of brevity.

The point of using the factory pattern is that some code creates an instance of the correct class without this code, knowing the correct class. Note that in your example, this separation is missing : the one who calls MyClassFactory.CreateMyClass<T> () must know the correct class, since this code must pass the type as a general parameter that defines the correct class. If I know enough to call CreateMyClass<int> () , then I know enough to call new MyDerivedIntClass () .

There are two main ways to implement a factory pattern: static functions and factory classes.

In both methods, you will need an abstract base class or generic interface:

 public interface IMyInterface { string Foo (); } public abstract class MyGenericBaseClass<T> : IMyInterface { // ... abstract /* or virtual */ string Foo (); } 

Using a static function, you will need a specific delegate type (the scope of the class is omitted):

 // note: I'm not sure this is the correct syntax, but I think I'm in the ballpark delegate IMyInterface MyInterfaceFactory (); 

and classes can implement them to return the correct type.

 public sealed class MyDerivedIntClass : MyGenericBaseClass<int> { // ... static IMyInterface CreateObject () { return new MyDerivedIntClass (); } } public sealed class MyDerivedStringClass : MyGenericBaseClass<string> { // ... static IMyInterface CreateObject () { return new MyDerivedStringClass (); } } 

Passing a static function to a function that instantiates an object:

 // ... somewhere else in the code ... // create a IMyInterface object using a factory method and do something with it void Bar (MyInterfaceFactory factory) { IMyInterface mySomething = factory (); string foo = mySomething.Foo (); } // ... somewhere else in the code ... void FooBarAnInt () { Bar (MyDerivedIntClass.CreateObject); } 

The second way is to use factory classes:

 public interface IMyInterfaceFactory { IMyInterface CreateObject (); } public class MyDerivedIntFactory : IMyInterfaceFactory { public IMyInterface CreateObject () { return new MyDerivedIntClass (); } } public class MyDerivedStringFactory : IMyInterfaceFactory { public IMyInterface CreateObject () { return new MyDerivedStringClass (); } } // ... somewhere else ... // create a IMyInterface object using a factory class and do something with it void Bar (IMyInterfaceFactory factory) { IMyInterface mySomething = factory.CreateObject (); string foo = mySomething.Foo (); } // ... somewhere else in the code ... void FooBarAnInt () { Bar (new MyDerivedIntFactory ()); } 

Note that you can (and probably should) also make factory classes single, but that is another problem. Alternatively, you can use an abstract base class instead of an interface; you need to add abstract (or virtual ) and override as needed.

In fact, someone, somewhere, somehow needs to know the correct type of object to instantiate. The point of factory objects is not to completely distract this knowledge, but to separate the knowledge of what type of object to create from the code that actually creates the object. Unless you need this separation, the factory pattern is not particularly useful.

+3
source

I usually use the <string, Type> dictionary for each of my factories, where I register every available implementation type that the factory can return (see this for more information: Is the switch statement applicable to the factory method?

You can adapt this to your needs and save the dictionary <Type, Type> of all your derived classes.

  ... Factories.StaticDictionary.Add(typeof(int),typeof(MyDerivedIntClass)); Factories.StaticDictionary.Add(typeof(string),typeof(MyDerivedStringClass)); ... 

(or each of your derived classes automatically adds this dictionary)

Then your factory will be simple:

  public static class MyClassFactory { public static MyGenericBaseClass<T> CreateMyClass<T>() { Activator.CreateInstance( Factories.StaticDictionary[typeof(T)]); } } 
+2
source

If you don't want to do type checking and instantiate the corresponding classes, the only way I could think of would be reflection, which will save you a few lines if there are several classes, but it will be harder to implement

Honestly, I think that doing it the way you do now is a full-fledged implementation of the factory and cannot understand why to change it.

0
source

I do not think this is necessarily bad. At some point you need to declare the type of object being created. By hiding the constructor call of your various derived classes, the factory allows you to have one place where specific classes are known.

There are several ways to do this: a set of if statements, a dictionary, an enterprise xml configuration file, but ultimately you need to somehow map an int parameter to a new instance of MyDerivedIntClass. It may not be very pretty, but at least you only write code once.

0
source

If you want to go further than some of the examples published by other users, you should look at the dependency injection infrastructure.

0
source

Thanks a ton for all the answers. received a huge education here.

The main reason for switching this route is this: I have a base class with the "ID" property, whose data type is determined at runtime (basic, int or long - main processed). The factory class / method should return a derived class with the appropriate data type for the derived class identifier. thatโ€™s why I think I canโ€™t follow the path of the lack of a base class / interface.

found this idea about 30 minutes ago, which does this in the factory method:

 public static class MyClassFactory { public static MyGenericBaseClass<T> CreateMyClass<T>() { // ********************************************** if (typeof(T) == typeof(int)) { MyGenericBaseClass<int> typedDerived = new MyDerivedIntClass(); return (MyGenericBaseClass<T>)(object)typedDerived; } if (typeof(T) == typeof(string)) { MyGenericBaseClass<string> typedDerived = new MyDerivedStringClass(); return (MyGenericBaseClass<T>)(object)typedDerived; } // ********************************************** } } 

Looks hacked, especially with a dual application, but seems to work. What do you think primarily in terms of maintainability?

0
source

All Articles