Come scrivere costruttori che potrebbero non riuscire a creare un'istanza corretta di un oggetto


11

A volte è necessario scrivere un costruttore che può fallire. Ad esempio, supponiamo di voler creare un'istanza di un oggetto con un percorso file, qualcosa del genere

obj = new Object("/home/user/foo_file")

Finché il percorso punta a un file appropriato, tutto va bene. Ma se la stringa non è un percorso valido, le cose dovrebbero rompersi. Ma come?

Potresti:

  1. generare un'eccezione
  2. return null object (se il tuo linguaggio di programmazione consente ai costruttori di restituire valori)
  3. restituisce un oggetto valido ma con una bandiera che indica che il suo percorso non è stato impostato correttamente (ugh)
  4. altri?

Presumo che le "migliori pratiche" di vari linguaggi di programmazione lo implementerebbero diversamente. Ad esempio, penso che ObjC preferisca (2). Ma (2) sarebbe impossibile da implementare in C ++ dove i costruttori devono avere il vuoto come tipo di ritorno. In quel caso, ritengo che sia usato (1).

Nel tuo linguaggio di programmazione preferito puoi mostrare come gestiresti questo problema e spiegare perché?


1
Le eccezioni in Java sono un modo per gestirlo.
Mahmoud Hossam,

I costruttori C ++ non restituiscono void: restituiscono un oggetto.
gablin,

6
@gablin: In realtà, non è nemmeno vero. newchiama operator newper allocare la memoria, quindi il costruttore per riempirla. Il costruttore non restituisce nulla e newrestituisce il puntatore da cui proviene operator new. voidTuttavia, se "non restituisce nulla" implica "ritorni " è in palio.
Jon Purdy,

@Jon Purdy: Hm, sembra ragionevole. Ottima scelta.
gablin

Risposte:


8

Non è mai bello fare affidamento su un costruttore per fare il lavoro sporco. Inoltre, non è anche chiaro a un altro programmatore se il costruttore eseguirà o meno un lavoro a meno che non ci sia una documentazione esplicita che lo affermi (e che l'utente della classe lo abbia letto o gli sia stato detto).

Ad esempio (in C #):

public Sprite
{
    public Sprite(string filename)
    {
    }
}

Cosa succede se l'utente non desidera caricare immediatamente il file? Cosa succede se desiderano eseguire la memorizzazione nella cache su richiesta del file? Non possono. Potresti pensare di mettere un bool loadFileargomento nel costruttore, ma questo rende questo disordinato poiché ora hai ancora bisogno di un Load()metodo per caricare il file.

Dato lo scenario attuale, sarà più flessibile e più chiaro per gli utenti della classe fare questo:

Sprite sprite = new Sprite();
sprite.Load("mario.png");

O in alternativa (per qualcosa come una risorsa):

Sprite sprite = new Sprite();
sprite.Source = "mario.png";
sprite.Cache();

// On demand caching of file contents.
public void Cache()
{
     if (image == null)
     {
         try
         {
             image = new Image.FromFile(Source);
         }
         catch(...)
         {
         }
     }
}

nel tuo caso se il caricamento (..) fallisce, abbiamo un oggetto totalmente inutile. Non sono sicuro di volere lo sprite senza alcuno sprite ... Ma la domanda assomiglia più a una questione sacra che a una vera domanda :)
avtomaton

7

In Java, è possibile utilizzare eccezioni o utilizzare il modello di fabbrica, che consente di restituire null.

In Scala, è possibile restituire un'opzione [Foo] da un metodo di fabbrica. Funzionerebbe anche in Java, ma sarebbe più ingombrante.


Utilizzo di Eccezioni o factory anche nei linguaggi .NET.
Beth Whitezel,

3

Lancia un'eccezione.

Null deve essere verificato se è possibile restituirlo (e non verrà verificato per)

Ecco a cosa servono le eccezioni controllate. Sai che potrebbe fallire. I chiamanti devono gestirlo.


3

In C ++ , i costruttori vengono utilizzati per creare / inizializzare i membri della classe.

Non c'è una risposta giusta a questa domanda. Ma quello che ho osservato finora è che il più delle volte è il client (o chiunque userà la tua API) che sceglie come gestire queste cose.

A volte potrebbero chiederti di allocare tutte le risorse di cui l'oggetto potrebbe aver bisogno sul costruttore e generare un'eccezione se qualcosa non riesce (interrompere la creazione dell'oggetto), o fare nulla di tutto ciò sul costruttore, e assicurarsi che la creazione avrà sempre successo , lasciando queste attività per alcune funzioni membro.

Se sta a te scegliere il comportamento, le eccezioni sono il modo C ++ predefinito per la gestione degli errori e dovresti usarle quando puoi.


1

È inoltre possibile creare un'istanza dell'oggetto senza parametri o solo con parametri che sicuramente non falliranno mai e quindi utilizzare una funzione o un metodo di inizializzazione da cui è possibile generare un'eccezione o fare ciò che si desidera.


6
Penso che restituire un oggetto inutilizzabile che richiede un'ulteriore inizializzazione sia la scelta peggiore. Ora hai un frammento di più righe che deve essere copiato ogni volta che la classe viene utilizzata o fattorizzata in un nuovo metodo. Potresti anche avere il costruttore a fare tutto il lavoro e lanciare un'eccezione se l'oggetto non può essere costruito.
Kevin Cline,

2
@kevin: dipende dall'oggetto. Gli errori saranno probabilmente dovuti a risorse esterne, quindi un'entità potrebbe voler registrare l'intenzione (ad es. Nome file) ma consentire tentativi ripetuti di inizializzazione completa. Per un oggetto di valore probabilmente propenderei per segnalare un errore che può catturare l'errore sottostante, quindi probabilmente lanciare un'eccezione (racchiusa?).
Dave,

-4

Preferisco non inizializzare alcuna classe o lista nel costruttore. Inizializza una classe o un elenco ogni volta che è necessario. Per esempio.

public class Test
{
    List<Employee> test = null;
}

Non farlo

public class Test
{
    List<Employee> test = null;

    public Test()
    {
        test = new List<Employee>();
    }
}

Inizializzare invece quando richiesto Ad es.

public class Test
{
    List<Employee> emp = null;

    public Test()
    { 
    }

    public void SomeFunction()
    {
         emp = new List<Employee>();
    }
}

1
Questo è un consiglio scadente. Non si deve consentire agli oggetti di essere in uno stato non valido. Ecco a cosa serve il costruttore. Se hai bisogno di una logica complessa, usa il modello di fabbrica.
AlexFoxGill

1
I costruttori sono lì per stabilire gli invarianti di classe, il codice di esempio non aiuta a affermarlo affatto.
Niall,
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.