Pergunta

O que é uma maneira mais elegante de ter o código abaixo onde eu quero retornar uma classe derivada com base no tipo de outra classe.

            if (option_ is Rectangle)
            {
                modelInputs = new Foo();
            }
            else if (option_ is Circle)
            {
                modelInputs = new Bar();
            }
            else if (option_ is Triangle)
            {
                modelInputs = new Bar2();
            }
Foi útil?

Solução

Tem Retângulo, Círculo e Triângulo implementar IHasModelInput:

interface IHasModelInput
{
    IModelInput GetModelInput();
}

então você pode fazer

IModelInput modelInputs = option_.GetModelInput();

Outras dicas

A minha opinião: o seu caminho "deselegante" é bom. É simples, legível e faz o trabalho.

Ter o Retângulo, Círculo e Triângulo implementar a função de fábrica necessárias via IHasModelInput iria funcionar, mas tem um custo do projeto: você acoplada agora este conjunto de classes com o conjunto IModelInput de aulas ( foo, Bar e Bar2). Eles poderiam estar em duas bibliotecas completamente diferentes, e talvez eles não devem saber sobre o outro.

Um método mais complicado é abaixo. Ele lhe dá a vantagem de ser capaz de configurar sua lógica fábrica em tempo de execução.

    public static class FactoryMethod<T>  where T : IModelInput, new()
    {
        public static IModelInput Create()
        {
            return new T();
        }
    }

    delegate IModelInput ModelInputCreateFunction();

    IModelInput CreateIModelInput(object item)
    {

        Dictionary<Type, ModelInputCreateFunction> factory = new Dictionary<Type, ModelInputCreateFunction>();


        factory.Add(typeof(Rectangle), FactoryMethod<Foo>.Create);
        factory.Add(typeof(Circle),    FactoryMethod<Bar>.Create);
        // Add more type mappings here




        IModelInput modelInput;
        foreach (Type t in factory.Keys)
        {
            if ( item.GetType().IsSubclassOf(t) || item.GetType().Equals(t))
            {
                modelInput = factory[t].Invoke();
                break;
            }
        }
        return modelInput;
    }

Mas então a pergunta: qual deles você prefere ler

Você pode colocar as entradas e saídas em um Hashtable, ou armazenar os tipos que criam cada classe dentro de cada uma das classes que você cria e, em seguida, usar Activator.CreateInstance para fazer o factoryin':

Hashtable ht = new Hashtable();
ht.Add(typeof(Rectangle), typeof(Bar));
ht.Add(typeof(Square), typeof(Bar2));

modelInputs = Activator.CreateInstance(ht[option.GetType()]);

De qualquer maneira, Activator.CreateInstance é uma maneira muito legal de fazer as fábricas trabalham em .NET. Aproveite, e usar o poder que vos tenho dado sabedoria, filho.

Você pode associar um tipo com "option_", se ele permite que, em seguida, apenas criar uma instância dele.

Eu costumo usar um método de fábrica como esta quando eu quiser converter uma string para um tipo em tempo de execução, eu uso um dicionário que mapeia uma string para um tipo.

Como isso de um recente projeto:

public class TaskFactory
{
    private Dictionary<String, Type> _taskTypes = new Dictionary<String, Type>();

    public TaskFactory()
    {
        // Preload the Task Types into a dictionary so we can look them up later
        foreach (Type type in typeof(TaskFactory).Assembly.GetTypes())
        {
            if (type.IsSubclassOf(typeof(CCTask)))
            {
                _taskTypes[type.Name.ToLower()] = type;
            }
        }
    }

    public CCTask CreateTask(XmlElement task)
    {
        if (task != null)
        {
            string taskName = task.Name;
            taskName =  taskName.ToLower() + "task";

            // If the Type information is in our Dictionary, instantiate a new instance of that task
            Type taskType;
            if (_taskTypes.TryGetValue(taskName, out taskType))
            {
                return (CCTask)Activator.CreateInstance(taskType, task);
            }
            else
            {
                throw new ArgumentException("Unrecognized Task:" + task.Name);
            }                               
        }
        else
        {
            return null;
        }
    }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top