A serialização e desserialização devem ser de responsabilidade da classe que está sendo serializada?

15

Atualmente, estou na fase de (re) design de várias classes de modelo de um aplicativo C # .NET. (Modelo como em M de MVC). As classes de modelo já têm muitos dados, comportamentos e inter-relações bem desenhados. Estou reescrevendo o modelo do Python para o C #.

No antigo modelo Python, acho que vejo uma verruga. Cada modelo sabe como serializar-se, e a lógica de serialização tem nada para fazer com o resto do comportamento de qualquer uma das classes. Por exemplo, imagine:

  • Image class com um método .toJPG(String filePath) .fromJPG(String filePath)
  • ImageMetaData class com um método .toString() e .fromString(String serialized) .

Você pode imaginar como esses métodos de serialização não são coesos com o resto da classe, mas somente a classe pode ter certeza de que sabe dados suficientes para serializar a si mesma.

É prática comum para uma classe saber como serializar e desserializar a si mesma? Ou estou perdendo um padrão comum?

    
por kdbanman 04.07.2015 / 02:41
fonte

2 respostas

14

Eu geralmente evito que a turma saiba se serializar, por alguns motivos. Primeiro, se você quiser (de) serializar para / de um formato diferente, agora precisa poluir o modelo com essa lógica extra. Se o modelo for acessado por meio de uma interface, você também polui o contrato.

public class Image
{
    public void toJPG(String filePath) { ... }

    public Image fromJPG(String filePath) { ... }
}

Mas e se você quiser serializá-lo para / de um PNG e GIF? Agora a aula se torna

public class Image
{
    public void toJPG(String filePath) { ... }

    public Image fromJPG(String filePath) { ... }

    public void toPNG(String filePath) { ... }

    public Image fromPNG(String filePath) { ... }

    public void toGIF(String filePath) { ... }

    public Image fromGIF(String filePath) { ... }
}

Em vez disso, normalmente gosto de usar um padrão semelhante ao seguinte:

public interface ImageSerializer
{
    void serialize(Image src, Stream outputStream);

    Image deserialize(Stream inputStream);
}

public class JPGImageSerializer : ImageSerializer
{
    public void serialize(Image src, Stream outputStream) { ... }

    public Image deserialize(Stream inputStream) { ... }
}

public class PNGImageSerializer : ImageSerializer
{
    public void serialize(Image src, Stream outputStream) { ... }

    public Image deserialize(Stream inputStream) { ... }
}

public class GIFImageSerializer : ImageSerializer
{
    public void serialize(Image src, Stream outputStream) { ... }

    public Image deserialize(Stream inputStream) { ... }
}

Agora, neste ponto, uma das ressalvas com esse design é que os serializadores precisam saber o identity do objeto que está sendo serializado. Alguns diriam que isso é um projeto ruim, já que a implementação vaza para fora da classe. O risco / recompensa disso depende muito de você, mas você pode ajustar as classes para fazer algo como

public class Image
{
    public void serializeTo(ImageSerializer serializer, Stream outputStream)
    {
        serializer.serialize(this.pixelData, outputStream);
    }

    public void deserializeFrom(ImageSerializer serializer, Stream inputStream)
    {
        this.pixelData = serializer.deserialize(inputStream);
    }
}

Este é mais um exemplo geral, uma vez que as imagens geralmente possuem metadados que acompanham o mesmo; coisas como nível de compressão, espaço de cores, etc., que podem complicar o processo.

    
por 04.07.2015 / 05:22
fonte
3

Serialização é um problema de duas partes:

  1. Conhecimento sobre como instanciar uma estrutura aka .
  2. Conhecimento sobre como persistir / transferir as informações necessárias para instanciar uma classe aka mechanics .

Na medida do possível, estrutura deve ser mantido separado da mecânica . Isso aumenta a modularidade do seu sistema. Se você enterrar a informação em # 2 dentro de sua classe, então você quebra a modularidade porque agora sua classe deve ser modificada para acompanhar os novos modos de serialização (se eles aparecerem).

No contexto da serialização de imagens, você manteria as informações sobre serialização separadas da própria classe e as manteria nos algoritmos que podem determinar o formato da serialização - portanto, classes diferentes para JPEG, PNG, BMP, etc. Amanhã, um novo algoritmo de serialização irá codificá-lo e seu contrato de classe permanecerá inalterado.

No contexto do IPC, você pode manter sua classe separada e depois declarar seletivamente as informações necessárias para a serialização (por anotações / atributos). Em seguida, seu algoritmo de serialização pode decidir se deseja usar JSON, Google Protocol Buffers ou XML para serialização. Ele pode até mesmo decidir se quer usar o analisador de Jackson ou seu analisador personalizado - há muitas opções que você obteria facilmente ao projetar de maneira modular!

    
por 04.07.2015 / 06:07
fonte