Skip to content

Instantly share code, notes, and snippets.

@DForshner
Last active December 18, 2015 02:19
Show Gist options
  • Save DForshner/5710514 to your computer and use it in GitHub Desktop.
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.
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