Dopo aver riflettuto ulteriormente, se vuoi farlo nel modo più pulito possibile, puoi utilizzare una combinazione del modello Strategia e Localizzatore di servizio per raggiungere il tuo obiettivo in un modo orientato agli oggetti.
Ecco la strategia generale (nessun gioco di parole):
- Metti la tua implementazione per ogni strategia in una classe separata.
- Registra ogni strategia in un elenco statico.
- Quando devi gestire un oggetto di un tipo sconosciuto, esegui un'iterazione delle strategie registrate finché non ne trovi una che funzioni.
La parte netta di questo approccio è che puoi stabilire se seguire o meno una strategia specifica basata su criteri più personalizzati rispetto a Type. Supponiamo che tu abbia un'interfaccia che in realtà vuoi gestire in due modi diversi a seconda del valore di una proprietà o se desideri applicare la stessa strategia a più interfacce. Tutto dipende da come lo si implementa.
Questo è decisamente eccessivo per classi come fabbriche, ecc., dove il modello if / else if dovrebbe essere sufficiente ed è in effetti abbastanza appropriato. Supponendo che tu abbia esplorato altre opzioni più semplici o più oo-friendly, ecco un esempio completamente sovradimensionato con il puro scopo di evitare un switch
su Type, con conseguente Ultimate Extensible Code (TM).
Supponiamo che tu abbia a che fare con due interfacce esterne non correlate, IBlinger
e ISnapper
:
interface IBlinger
{
public string BlingName { get; }
}
interface ISnapper
{
public string SnapName { get; }
}
E stai cercando di scrivere una classe che gestisca la scrittura di ogni interfaccia sulla console in un modo personalizzato. Dovresti prima definire un'interfaccia IWriteHandler
come segue:
interface IWriteHandler
{
bool Write(object target);
}
E quindi fornire implementazioni:
class BlingWriter : IWriteHandler
{
public bool Write(object target)
{
var bling = target as IBlinger;
if (bling == null)
return false;
Console.WriteLine("BLING: " + bling.BlingName);
return true;
}
}
class SnapWriter : IWriteHandler
{
public bool Write(object target)
{
var snap = target as ISnapper;
if (snap == null)
return false;
Console.WriteLine("SNAP: " + snap.SnapName);
return true;
}
}
Ed ecco un esempio di utilizzo:
class Writer
{
static List<IWriteHandler> _handlers = new List<IWriteHandler>();
static Writer()
{
//add them to the list in order of preference, in case the types overlap
//due to inheritance.
_handlers.Add(new BlingWriter());
_handlers.Add(new SnapWriter());
}
public void Write(object unknown)
{
foreach (var handler in _handlers)
{
if (handler.Write(unknown))
return;
}
//default
Console.WriteLine("UNKNOWN: " + unknown.ToString());
}
}
E se sembra troppo "boilerplatey", potresti renderlo più generico:
abstract class GenericWriteHandler<T> : IWriteHandler where T : class
{
public bool Write(object target)
{
var asT = target as T;
if (asT == null)
return false;
WriteInternal(asT);
return true;
}
protected abstract void WriteInternal(T target);
}
Che potrebbe essere implementato come:
class SnapWriter : GenericWriteHandler<ISnapper>
{
protected override void WriteInternal(ISnapper target)
{
Console.WriteLine("SNAP: " + target.SnapName);
}
}
OPPURE, se ti piace lambda / non mi piace creare molte classi, potresti fare:
class ActionWriteHandler<T> : IWriteHandler where T : class
{
Action<T> _a;
public ActionWriteHandler(Action<T> action)
{
_a = action;
}
public bool Write(object target)
{
var asT = target as T;
if (asT == null)
return false;
_a(asT);
return true;
}
protected abstract void WriteInternal(T target);
}
Dove la classe Writer
è inizializzata come:
static Writer()
{
_handlers.Add(new ActionWriteHandler<IBlinger>(b => Console.WriteLine(b.BlingName)));
_handlers.Add(new ActionWriteHandler<ISnapper>(s => Console.WriteLine(s.SnapName)));
}
In effetti, potresti anche generare l'intera Writer
generica, ma questo è per un altro giorno ...
Domanda correlata (che ha una risposta simile): link