È male creare classi il cui unico scopo è convertire implicitamente in un'altra classe?


10

Immagina una situazione in cui stiamo utilizzando una libreria che ti consente di creare Circleoggetti, in cui puoi specificare il raggio e il centro del cerchio per definirlo. Tuttavia, per qualche motivo, accetta anche un flavourparametro richiesto . Ora diciamo che ho davvero bisogno di usare la Circlemia app, ma ai fini della mia app posso impostare il sapore per essere Flavours.Cardboardogni volta.

Per "risolvere" questo, creo la mia Circleclasse in uno spazio dei nomi diverso, che accetta solo radiuse centercome parametri, ma ha un convertitore implicito nella Circleclasse della libreria esterna che crea semplicemente un Circle(this.radius, this.center, Flavours.Cardboard)oggetto. Quindi ovunque ho bisogno dell'altro tipo Circle, lascio che la conversione automatica abbia luogo.

Quali sono le conseguenze della creazione di una tale classe? Ci sono soluzioni migliori? Farebbe differenza se la mia applicazione fosse un'API costruita su questa libreria esterna, destinata all'uso da parte di altri programmatori?


Questa sembra una variazione del modello di adattatore , sebbene qui abbia un valore dubbio (è semplicemente una comodità per evitare di impostare un parametro).
Robert Harvey,

5
Perché non creare una MakeCircle funzione ?
user253751,

1
@immibis Thay è stato il mio primo pensiero. Quando creo i giochi, creo spesso una funzione in linea con makePlayerquella stessa che accetta solo le coordinate per posizionare il giocatore, ma delega a un costruttore molto più complesso.
Carcigenicato,

Risposte:


13

Anche se non è sempre male, è piuttosto raro che le conversioni implicite siano l'opzione migliore.

Ci sono problemi

  1. Le conversioni implicite non sono molto leggibili.
  2. Poiché stai creando un nuovo oggetto, qualsiasi modifica all'oggetto originale non verrà vista da quella in cui stai convertendo, portando a bug.
  3. Se stai gettando via l'oggetto, è una spazzatura extra da pulire.
  4. Se c'è un problema, accadrà quando si verifica la conversione, non quando viene creata la cosa, rendendo più difficili da rintracciare i bug.

In generale, ci sono soluzioni migliori.

  1. Crea il tuo wrapper sottile che utilizza l'altra classe / framework internamente.
  2. Crea un metodo helper che prenda gli argomenti del conduttore desiderati e fornisca quello che è stato risolto, restituendo l'oggetto reale senza dover specificare l'argomento che non ti interessa.
  3. Eredita dalla classe problematica e fornisci il tuo costruttore più bello.
  4. Renditi conto che passare l'argomento extra non è poi un grosso problema.

Personalmente, trovo che il numero 2 sia il più semplice da implementare e il meno oneroso in termini di progettazione. Gli altri possono andare bene, vista la situazione e cos'altro stai cercando di fare con queste lezioni.

La conversione implicita è l'ultima risorsa, e mi sembra davvero valsa la pena solo quando ho i funzionali in stile C ++ che sto cercando di fare - oggetti di strategia che converto implicitamente in tipi delegati.


1

Dato lo scenario che stai descrivendo, puoi pensare a questo in termini di applicazione parziale delle funzioni.

Un costruttore è una funzione (almeno in teoria; in C #, è possibile creare una "funzione di fabbrica" ​​che chiama il costruttore):

Func<double, Point, Flavour, Circle> MakeCircle = (r, p, f) => new Circle(r, p, f);

per un'applicazione parziale, sarà sufficiente:

public static Func<T1, T2, R> Partial<T1, T2, T3, R>(this Func<T1, T2, T3, R> func, T3 t3)
   => (t1, t2) => func(t1, t2, t3);

Ora puoi ottenere il tuo costruttore che richiede solo 2 parametri:

Func<double, Point, Circle> MakeCardboardCircle = Circle.Partial(Flavours.Cardboard)

Quindi ora hai una funzione di fabbrica con i parametri desiderati

Circle c = MakeCardboardCircle(1.0, new Point(0, 0)))

A proposito, questo è chiaramente equivalente all'opzione 2 sopra, solo da una prospettiva più funzionale.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.