Last active
December 18, 2015 02:19
-
-
Save DForshner/5710514 to your computer and use it in GitHub Desktop.
A factory that uses a variation of the strategy pattern to create new products based on a product key and passes in a dependency using constructor injection. In this particular design the factory gets configured once during application start. Contains the example classes and some tests.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.VisualStudio.TestTools.UnitTesting; | |
using System; | |
using System.Collections.Generic; | |
namespace SimpleStratagyFactory | |
{ | |
#region Classes | |
/// <summary> | |
/// Enumeration of different types of products to create | |
/// </summary> | |
public enum ProductTypes | |
{ | |
TypeA, | |
TypeB, | |
TypeC | |
} | |
/// <summary> | |
/// Abstract base class for products | |
/// </summary> | |
public abstract class AbstractProduct | |
{ | |
public AbstractProduct(IDependancy dependancy) | |
{ | |
this.Dependancy = dependancy; | |
} | |
protected IDependancy Dependancy { get; set; } | |
abstract public ProductTypes Type { get; } | |
public int DoSomething() | |
{ | |
return Dependancy.GetNumber() * 5; | |
} | |
} | |
public class ProductA : AbstractProduct | |
{ | |
public override ProductTypes Type { get { return ProductTypes.TypeA; } } | |
public ProductA(IDependancy dependancy) | |
: base(dependancy) | |
{ | |
} | |
public static AbstractProduct Create(IDependancy dependancy) | |
{ | |
return new ProductA(dependancy); | |
} | |
} | |
public class ProductB : AbstractProduct | |
{ | |
public override ProductTypes Type { get { return ProductTypes.TypeB; } } | |
public ProductB(IDependancy dependancy) | |
: base(dependancy) | |
{ | |
} | |
public static AbstractProduct Create(IDependancy dependancy) | |
{ | |
return new ProductA(dependancy); | |
} | |
} | |
public class ProductC : AbstractProduct | |
{ | |
public override ProductTypes Type { get { return ProductTypes.TypeC; } } | |
public ProductC(IDependancy dependancy) | |
: base(dependancy) | |
{ | |
} | |
public static AbstractProduct Create(IDependancy dependancy) | |
{ | |
return new ProductA(dependancy); | |
} | |
} | |
public interface IDependancy | |
{ | |
int GetNumber(); | |
} | |
/// <summary> | |
/// Represents a dependency that has to be injected. | |
/// </summary> | |
public class Dependancy : IDependancy | |
{ | |
public int GetNumber() | |
{ | |
return 43; | |
} | |
} | |
/// <summary> | |
/// A factory that that used constructor injection to inject dependencies into its products. | |
/// | |
/// This particular factory has to be initialized by calling the Map method during application start. | |
/// However this could easily be re-written so the mapping could be done every time a new instance of | |
/// the factory is created. | |
/// </summary> | |
/// <typeparam name="T">Product base class</typeparam> | |
/// <typeparam name="K">Product Key</typeparam> | |
/// <typeparam name="D">The dependency to be injected into the product</typeparam> | |
public class SimpleStratagyFactory<T, K, D> where D : new() | |
{ | |
public SimpleStratagyFactory(D dependancy) | |
{ | |
this.Dependancy = dependancy; | |
} | |
/// <summary> | |
/// Creates a new product of the type mapped to the key. | |
/// </summary> | |
public T Create(K key) | |
{ | |
Func<D, T> stratagy; | |
stratagies.TryGetValue(key, out stratagy); | |
if (stratagy == null) | |
throw new Exception("Key was not mapped in factory"); | |
return stratagies[key].Invoke(Dependancy); | |
} | |
/// <summary> | |
/// Configure the factory. Normally done during application start. | |
/// </summary> | |
public static void Map(K key, Func<D, T> createMethod) | |
{ | |
stratagies.Add(key, createMethod); | |
} | |
/// <summary> | |
/// Erases any configured mappings. | |
/// </summary> | |
public static void ClearMappings() | |
{ | |
stratagies.Clear(); | |
} | |
private D Dependancy { get; set; } | |
private static Dictionary<K, Func<D, T>> stratagies = new Dictionary<K, Func<D, T>>(); | |
} | |
#endregion Classes | |
#region Tests | |
[TestClass] | |
public class SimpleStratagyFactoryTests | |
{ | |
#region SETUP | |
[TestInitialize()] | |
public void MyTestInitialize() | |
{ | |
// Simulate initializing the factory once per app start | |
SimpleStratagyFactory<AbstractProduct, ProductTypes, Dependancy>.Map(ProductTypes.TypeA, (x) => ProductA.Create(x)); | |
SimpleStratagyFactory<AbstractProduct, ProductTypes, Dependancy>.Map(ProductTypes.TypeB, (x) => ProductB.Create(x)); | |
SimpleStratagyFactory<AbstractProduct, ProductTypes, Dependancy>.Map(ProductTypes.TypeC, (x) => ProductC.Create(x)); | |
} | |
[TestCleanup()] | |
public void MyTestCleanup() | |
{ | |
SimpleStratagyFactory<AbstractProduct, ProductTypes, Dependancy>.ClearMappings(); | |
} | |
#endregion SETUP | |
[TestMethod] | |
public void Create_WhenCreateTypeA_ExpectProductAReturned() | |
{ | |
// Arrange | |
var factory = new SimpleStratagyFactory<AbstractProduct, ProductTypes, Dependancy>(new Dependancy()); | |
// Act | |
var result = factory.Create(ProductTypes.TypeA); | |
// Assert | |
Assert.AreEqual(ProductTypes.TypeA, result.Type); | |
} | |
[TestMethod] | |
public void Create_WhenCreateWithDependancy_ExpectDependancyPassedToFactoryProducts() | |
{ | |
// Arrange | |
var factory = new SimpleStratagyFactory<AbstractProduct, ProductTypes, Dependancy>(new Dependancy()); | |
// Act | |
var result = factory.Create(ProductTypes.TypeB); | |
// Assert | |
Assert.AreEqual(215, result.DoSomething()); | |
} | |
[TestMethod] | |
[ExpectedException(typeof(Exception))] | |
public void Create_WhenTryCreateAnUnmappedKey_ExpectException() | |
{ | |
// Arrange | |
var factory = new SimpleStratagyFactory<AbstractProduct, ProductTypes, Dependancy>(new Dependancy()); | |
// Call my test cleanup to wipe out the factory mappings | |
MyTestCleanup(); | |
// Act & Assert | |
var result = factory.Create(ProductTypes.TypeA); | |
} | |
} | |
#endregion Tests | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment