Я выполняю рефакторинг кодовой базы и наткнулся на фабричный класс, который создал объекты на основе подтипа, переданного в метод.
У класса в основном есть один открытый метод с одним параметром, из которого он является потомкомбазовый класс.В этом методе есть оператор switch, который определяет, какой подтип передается, и условно вызывает различные методы для получения результата.
Я пытаюсь немного привести в порядок и подумал, что шаблон стратегии может соответствовать требованиям, поскольку коднарушает принцип открытого-закрытого.
Поскольку используется Autofac, я подумал, что переход будет прямым, однако я столкнулся с препятствием на дороге.
Проблема не в том,t относится к Autofac, а скорее к выбору дизайна.
Следующий код иллюстрирует композицию классов, но ее не хватает.
public abstract class Parent { }
public class ChildA : Parent { }
public class ChildB : Parent { }
public interface IChildStrategy<T> where T:Parent
{
IEnumerable<object> CreateObjects(Parent child);
}
public class ChildAStrategy : IChildStrategy<ChildA>
{
private IEnumerable<object> CreateObjects(ChildA child)
{
yield return "child A";
}
public IEnumerable<object> CreateObjects(Parent child) =>
CreateObjects(child as ChildA);
}
public class ChildBStrategy : IChildStrategy<ChildB>
{
private IEnumerable<object> CreateObjects(ChildB child)
{
yield return "child b";
yield return "child b's pet";
}
public IEnumerable<object> CreateObjects(Parent child) =>
CreateObjects(child as ChildB);
}
[TestMethod]
public void TestStrategyPattern()
{
var container = builder.Build();
Parent child = new ChildA();
var type = child.GetType();
var strategy = container.Resolve(typeof(IChildStrategy<>)
.MakeGenericType(type));
// strategy.CreateObjects(child);
// Assert.AreEqual("child A", fromDict);
var dict = new Dictionary<Type, Func<Parent, IEnumerable<object>>>();
dict.Add(typeof(ChildA), x => new ChildAStrategy().CreateObjects(x));
dict.Add(typeof(ChildB), x => new ChildBStrategy().CreateObjects(x));
var fromDict = dict[type](child);
Assert.AreEqual("child A", fromDict);
}
Я пытался зарегистрировать интерфейс ссам универсальный тип, например, так:
public interface IChildStrategy<T> where T:Parent
{
IEnumerable<object> CreateObjects(T child);
}
Но это не меняет сложностей.
Есть ли хорошие альтернативы шаблонам проектирования для подклассов?
Обновлено
Вот чем я закончил.Изменения в основном удаляют параметр из метода CreateObjects, а скорее вводят его в конструктор как зависимость и регистрируют стратегии как Keyed<T>
регистрации.
public abstract class Parent { }
public class ChildA : Parent { }
public class ChildB : Parent { }
public interface IChildStrategy
{
IEnumerable<object> CreateObjects();
}
public class ChildAStrategy : IChildStrategy
{
private readonly ChildA childA;
public ChildAStrategy(ChildA childA)
{
this.childA = childA;
}
public IEnumerable<object> CreateObjects()
{
yield return childA;
}
}
public class ChildBStrategy : IChildStrategy
{
private readonly ChildB childB;
public ChildBStrategy(ChildB childB)
{
this.childB = childB;
}
public IEnumerable<object> CreateObjects()
{
yield return childB;
yield return "child b's pet";
}
}
[TestMethod]
public void TestStrategyPattern()
{
var builder = new ContainerBuilder();
builder.RegisterType<ChildAStrategy>().Keyed<IChildStrategy>(typeof(ChildA));
builder.RegisterType<ChildBStrategy>().Keyed<IChildStrategy>(typeof(ChildB));
var container = builder.Build();
IChildStrategy resolve(Parent x) => container.ResolveKeyed<IChildStrategy>(x.GetType(), new TypedParameter(x.GetType(), x));
Parent root;
IChildStrategy strategy;
root = new ChildA();
strategy = resolve(root);
Assert.IsInstanceOfType(strategy, typeof(ChildAStrategy));
Assert.AreSame(root, strategy.CreateObjects().Single());
root = new ChildB();
strategy = resolve(root);
Assert.IsInstanceOfType(strategy, typeof(ChildBStrategy));
Assert.AreSame(root, strategy.CreateObjects().First());
Assert.AreEqual("child b's pet", strategy.CreateObjects().Last());
}