views:

87

answers:

3

Hi folks,

I was wondering if I could - and how - I could use the Factory Pattern in this scenario?

I have a the following classes...

public interface IStub<T> where T : class
{
    IEnumerable<T> CreateStubs();
}

public FooStub : IStub<Foo>
{
    public IEnumerable<Foo> CreateStubs() { ... }
}


public BarStub : IStub<Bar>
{
    public IEnumerable<Bar> CreateStubs() { ... }
}

.. etc ...

and I was wondering if it's possible to create the instances through a factory method like ...

// This ends up returning an enumerable of Stubs.
var stubs = StubsFactory.CreateStubs<Foo>(); 

Is this possible / am I on the right track, here?

A: 

Like this. Modify the Type.GetType line if the implementation and stub isnt in the same assembly or namespace.

class UberCoolFactory
{
      public static T CreateStubs<T>()  
      {
          string ns = typeof(T).Namespace;
          string name = typeof(T).Name;
          string assembly = typeof(T).Assembly.GetName().Name;
          Type type = Type.GetType(string.Format("{0}.{1}Stub, {2}", ns, name, assembly));
          return (T)Activator.CreateInstance(type);
      }
 }

Alternative:

 class FactoryTwo
 {
    /// <summary>
    /// Search through all loaded assemblies that exists in the current directory.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public static T CreateStub<T>() where T : class
    {
        string currentDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
        string wantedTypeName = typeof(T).Name + "Stub";

        List<Type> foundTypes = new List<Type>();
        foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
        {
            if (!currentDir.Equals(Path.GetDirectoryName(assembly.Location)))
                continue;

            foreach (var type in assembly.GetTypes())
            {
                if (!type.Name.Equals(wantedTypeName))
                    continue;

                foundTypes.Add(type);
            }
        }


        if (!foundTypes.Any())
            return null;
        if (foundTypes.Count > 2)
            throw new AmbiguousMatchException("Found multiple stubs implementing '" + typeof(T).FullName + "'.");

        return (T) Activator.CreateInstance(foundTypes[0]);
    }
}

Usage:

var instance = UberCoolFactory.CreateStubs<Foo>();
var instance2 = FactoryTwo.CreateStub<Foo>();
jgauffin
@jgauffin : this is a constructor class? on what? a factory? i thought factory methods were generally static..... ???
Pure.Krome
Since when can constructors return a value? It can be a static method, a virtual method or anything you like. Your imagination (or knowledge) is the limit. I've updated the samples to wrap them in classes and make them static.
jgauffin
I must have been looking at some old code / version...
Pure.Krome
Yes.I edited the code quite a lot. Misread your question first.
jgauffin
I think that searching types by string, specially in own assembly, is bad idea. This is only acceptable when a string comes from a config file - i.e. you were configuring types used dynamically by some internal or external config
abatishchev
why is it a bad idea in my examples? and what strings are you talking about? Stubs are usually fake implementations used when testing.
jgauffin
@jgauffin: I'm talking about `typeof(T).Name + "Stub";`
abatishchev
@jgauffin: I have a name for such things - shamanism :) Too dynamic without any restrictions, relying only on developer's memory to keep in mind to name types properly. If he want to use your code, I think he should have some config called `TestStubs.config` to keep types correlations
abatishchev
Why? Stubs are used for only one thing and that is for testing. If you have a implementation called UserService you usually create a UserServiceStub in your test assembly. I think that a "magic" string is OK in that scenario. I would have done it in another way if it was used in a "real" application.
jgauffin
@jgauffin: My main caution is about don't use such magic in real app! :)
abatishchev
A: 

Extending jgauffin's answer about searching a target type:

string targetType = "BlahStub";
IEnumerable<Type> result = 
        from assembly in AppDomain.CurrentDomain.GetAssemblies()
        where !String.Equals(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), assembly.Location, StringComparison.OrdinalIgnoreCase)
        from type in assembly.GetTypes()
        where String.Equals(type.Name, targetType, StringComparison.OrdinalIgnoreCase)
        select type;
abatishchev
A: 

Factory declaration:

static class StubsFactory<TStub, TClass>
            where TStub : IStub<TClass>
            where TClass : class
{
    public static IEnumerable<TClass> CreateStubs()
    {
        return ((TStub)Activator.CreateInstance<TStub>()).CreateStubs();
    }
}

Usage:

IEnumerable<Foo> q = StubsFactory<FooStub, Foo>.CreateStubs();

Is it what you're looking for?

abatishchev
This code is just a replacement for "new FooStub().CreateStubs()".
jgauffin
@jgauffin: No because you need to specify only a type, no instances at all
abatishchev
Yes, but you create the specified type. Hence it's just a replacement of new FooStub().CreateStubs().
jgauffin
@jgauffin: As far as I understood OP need to call `IStub.CreateStubs()` or don't? If he need just to find `Some : IStub` (probably an interface at all) instead of call via the interface - your code is more suitable.
abatishchev
I understood it as he wanted to create factory classes and gave examples of two different ones, not that they had to be used.
jgauffin
@abatishchev this was something VERY VERY close to what I have/had. But mine didn't work. Each time i see your static method with TStub *and* TClass i keep thinking : The TStub already includes the TClass ... so why do I need to declare that also? can't it derive that? The second thing I keep thinking is .. i don't want to know or care about the name of the Stub class (eg. FooStub) otherwise i can just create an instance of that, right? Isn't that the whole idead of the Factory? I only know the POCO, so it returns the associated Stub?
Pure.Krome
@Pure: Glad to hear that my answer helped somehow :) The main place to declare generic type parameters (`T`, etc) is class name declaration point: `StubsFactory<TStub, TClass>` (if you want to affect the whole class). In methods' delclaration you can omit them. So next you impose restrictions on generic type parameters. `TStub : IStub<TClass>` - the main, manual restriction, `TClass : class` is inherited from `interface IStub<T> where T : class`
abatishchev
@Pure.Krome: I tried to make some changes to avoid declaration of both FooStub and Foo but got no elegant result. Only one idea - the factory should keep classes accordance in some dictionary, e.g. Dictionary<Type,Type> { { typeof(FooStub), typeof(Foo) } }`, Otherwise where from we will known which type should be returned from which stub
abatishchev