La deserializzazione personalizzata dovrebbe avvenire in un costruttore o in un metodo statico?

3

Quando scrivi un codice di deserializzazione personalizzato, qual è la pratica migliore: un metodo statico che crea un oggetto non inizializzato (ad esempio utilizzando il costruttore predefinito) e quindi esegue la deserializzazione o un costruttore che esegue direttamente la deserializzazione?

Ad esempio, deserializzare un Foo:

class Foo
{
    int bar;

    this() { }

    static Foo fromMyDataFormat(byte[] serializedData)
    {
        auto foo = new Foo();
        foo.bar = /* deserialize bar from serializedData */;
        return foo;
    }
}

vs

class Foo
{
    int bar;

    this(byte[] serializedData)
    {
        bar = /* deserialize bar from serializedData */;
    }
}

Quali sono i pro e i contro di entrambi gli approcci? Ci sono altre opzioni?

Solo la serializzazione de dovrebbe essere implementata.

    
posta lesderid 31.07.2016 - 15:23
fonte

5 risposte

4

No, non dovrebbe.

La serializzazione è ortogonale all'oggetto e quindi dovrebbe essere tenuta all'esterno. La serializzazione personalizzata appartiene a una personalizzazione su un serializzatore.

    
risposta data 31.07.2016 - 21:24
fonte
1

Dipende dalla lingua e dal framework di serializzazione. In .NET, ad esempio, il serializzatore cerca specificamente un costruttore che accetta SerializationInfo e StreamingContext . JSON.NET cercherà costruttori che accettano parametri che corrispondono ai nomi dei campi JSON.

Ma al di fuori di questi casi, raccomanderei un metodo statico. La deserializzazione è una di quelle cose che possono fallire in genere, ei costruttori limitano le opzioni di progettazione in merito alla gestione e alla segnalazione degli errori in un modo piacevole.

    
risposta data 31.07.2016 - 17:13
fonte
1

Proprio come si usano gli argomenti del costruttore che sono molto trasparenti nei termini di ciò che è necessario per l'oggetto da creare, un costruttore può accettare il tipo di dati byte[] che contenga i dati serializzati per l'oggetto da creare.

Come Telastyn ha già sottolineato, c'è un problema con la deserializzazione. È molto soggetto a errori perché i tipi di dati byte[] non forniscono alcun accenno alla struttura dei dati che può essere facilmente corrotta da praticamente qualsiasi cosa. Naturalmente, lanciare un'eccezione da un costruttore non è nulla di cui aver paura e alcune persone, me incluso, incoraggiano a farlo quando i dati di input non sono in un formato valido. Per questo motivo, il metodo factory static non è davvero necessario poiché praticamente avvolge comunque il costruttore.

A mio parere il metodo static non porta nulla alla tabella e, proprio come si può lanciare un'eccezione con informazioni di errore dal costruttore, è molto probabile che tu faccia lo stesso dal metodo static . Potresti introdurre un metodo static getConstructionErrors che restituisce un elenco di stringhe di errore, ma lo stato statico è condiviso e quindi potresti avere problemi relativi alle condizioni di gara.

Ma a seconda delle tue esigenze, potresti non volere neanche un metodo static o un costruttore. Se desideri introdurre un altro modo di serializzazione / deserializzazione, devi creare un'interfaccia FooSerialization personalizzata (qui interface è la parola chiave della lingua e potresti fare riferimento a protocol o a pure abstract class in altre lingue) e hanno diverse classi implementarlo per fornire diverse strategie di serializzazione e deserializzazione. La serializzazione e la deserializzazione di solito non sono considerate una responsabilità diretta di un oggetto che stai modellando e quindi possono essere estrapolate a un servizio personalizzato (vedi Principio di responsabilità singola ). D'altra parte, se non avrai mai bisogno di più di un modo per serializzare l'oggetto, un metodo diretto serialize e la costruzione dall'array byte serializzato è probabilmente la strada da percorrere.

    
risposta data 31.07.2016 - 17:48
fonte
0

Dovrebbe essere ovvio che una matrice arbitraria di byte può o non può essere una rappresentazione serializzata valida dell'oggetto. Quindi devono succedere due cose: qualcuno deve controllare i dati, che potrebbero fallire il test, e qualcuno deve creare un oggetto se i dati superano il test.

Personalmente ritengo che sia più semplice e più pulito effettuare la verifica e la creazione dell'oggetto con lo stesso codice - forse questo è l'approccio migliore, forse sono solo io. (Nello stackoverflow si vede un numero spaventoso di domande in cui le persone ignorano completamente la parte di verifica).

Domanda: quanto è facile nella tua lingua eseguire un costruttore che potrebbe fallire? Se questo è molto facile, come in Objective-C o Swift, fallo in un costruttore. In C ++, è facile lanciare un'eccezione in un costruttore e gestirlo correttamente se il programmatore sa come farlo - il che è un presupposto piuttosto strong :-( Se è difficile, aggiungi un metodo di classe o un metodo di fabbrica.

    
risposta data 31.07.2016 - 21:09
fonte
-2

C'è una terza opzione da considerare: un metodo.

Foo foo = new Foo();
foo.deserialize(theByteData);

o, se ti piace l'immutabilità e vuoi che restituisca una nuova istanza

Foo temp = new Foo();  // there's other conventions you can do here
Foo foo = temp.deserialize(theByteData);

Il vantaggio in alcune lingue è che questo può essere dichiarato in un'interfaccia. Quindi se hai molte classi da deserializzare (o xmlize o serialuze o qualsiasi altra cosa) questa può essere un'opzione potente.

I vantaggi:

  1. Se si dispone di una raccolta mista di oggetti di questa interfaccia, non è necessario eseguire instanceofs per trovare il costruttore, la classe builder o il metodo statico.
  2. A differenza di un generatore o serializzatore separato (vedi la risposta di @David Packer, che è una buona soluzione) è possibile mantenere meglio i campi privati e possibilmente l'immutabilità. Generalmente un costruttore separato (non sempre) richiede setter (o accesso "amico") per i tuoi campi. In effetti, invece di un builder separato per ogni classe, l'oggetto stesso è il suo builder.

Se hai solo poche classi da deserializzare, dipende dalla tua lingua. In Java, la funzione statica valueOf() è una convenzione comune.

    
risposta data 31.07.2016 - 20:58
fonte

Leggi altre domande sui tag