Domanda

Qual è un modo più elegante di avere il codice qui sotto dove voglio restituire una classe derivata in base al tipo di un'altra classe.

            if (option_ is Rectangle)
            {
                modelInputs = new Foo();
            }
            else if (option_ is Circle)
            {
                modelInputs = new Bar();
            }
            else if (option_ is Triangle)
            {
                modelInputs = new Bar2();
            }
È stato utile?

Soluzione

Chiedi a Rectangle, Circle e Triangle di implementare IHasModelInput:

interface IHasModelInput
{
    IModelInput GetModelInput();
}

allora puoi farlo

IModelInput modelInputs = option_.GetModelInput();

Altri suggerimenti

La mia opinione: il tuo " inelegante " il modo va bene. È semplice, leggibile e fa il lavoro.

Avere Rectangle, Circle e Triangle implementare la funzione di fabbrica necessaria tramite IHasModelInput funzionerebbe, ma ha un costo di progettazione: ora hai abbinato questo set di classi al set di classi IModelInput ( Foo, Bar e Bar2). Potrebbero essere in due librerie completamente diverse e forse non dovrebbero conoscersi.

Di seguito è riportato un metodo più complicato. Ti offre il vantaggio di poter configurare la logica di fabbrica in fase di esecuzione.

    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;
    }

Ma poi fai la domanda: quale preferiresti leggere?

È possibile inserire gli input e gli output in un Hashtable o archiviare i tipi che creano ogni classe all'interno di ciascuna delle classi create e quindi utilizzare Activator.CreateInstance per eseguire il factoryin ':

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

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

Ad ogni modo, Activator.CreateInstance è un modo piuttosto interessante di far funzionare le fabbriche in .NET. Goditi e usa il potere che ti ho dato saggiamente, figliolo.

Puoi associare un tipo a " opzione_ " ;, se lo consente, e quindi crearne un'istanza.

Di solito utilizzo un metodo factory come questo quando voglio convertire una stringa in un tipo in fase di esecuzione, uso un dizionario che mappa una stringa in un tipo.

Come questo da un recente progetto:

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;
        }
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top