web-dev-qa-db-ja.com

シリアル化と逆シリアル化は、シリアル化されるクラスの責任ですか?

現在、C#.NETアプリケーションのいくつかのモデルクラスの(再)設計段階にあります。 (MVCのMと同じモデル)。モデルクラスには、十分に設計されたデータ、動作、および相互関係がすでにたくさんあります。モデルをPython=からC#に書き換えています。

古いPythonモデルでは、いぼが見えると思います。各モデルはそれ自体をシリアル化する方法を知っており、シリアル化ロジックにはなしすべてのクラスの残りの動作を処理します。たとえば、次のように想像します。

  • Imageクラスと.toJPG(String filePath).fromJPG(String filePath)メソッド
  • ImageMetaDataクラスと.toString()および.fromString(String serialized)メソッド。

これらのシリアル化メソッドが他のクラスと一貫性がないことは想像できますが、クラスだけがそれ自体をシリアル化するのに十分なデータを知っていることが保証されます。

クラスがそれ自体をシリアル化および逆シリアル化する方法を知っていることは一般的な習慣ですか?それとも一般的なパターンが欠けていますか?

17
kdbanman

いくつかの理由により、通常、クラスにそれ自体をシリアル化する方法を知らせないようにします。まず、別の形式との間で(逆)シリアル化する場合は、その追加のロジックでモデルを汚染する必要があります。モデルがインターフェースを介してアクセスされる場合、契約も汚染します。

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

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

しかし、PNGおよびGIFとの間でシリアル化する場合はどうでしょうか。今クラスは

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) { ... }
}

代わりに、通常は次のようなパターンを使用します。

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) { ... }
}

さて、この時点で、この設計の注意点の1つは、シリアライザがシリアル化するオブジェクトのidentityを知る必要があることです。実装がクラスの外にリークするため、これは悪い設計だと言う人もいます。これのリスク/報酬は本当にあなた次第ですが、クラスを少し微調整して次のようなことをすることができます

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);
    }
}

画像は通常それに伴うメタデータを持っているため、これはより一般的な例です。プロセスを複雑にする可能性がある圧縮レベル、色空間などのようなもの。

18
Zymus

シリアライゼーションは2つの部分からなる問題です。

  1. クラスをインスタンス化する方法に関する知識別名構造
  2. クラスのインスタンス化に必要な情報を永続化/転送する方法についての知識aka mechanics

structureは、可能な限りmechanicsから分離する必要があります。これにより、システムのモジュール性が向上します。クラス内の#2に情報を埋め込むと、モジュール化が崩れます。これは、新しいシリアル化の方法(それらが発生した場合)に合わせてクラスを変更する必要があるためです。

画像のシリアル化のコンテキストでは、シリアル化に関する情報をクラス自体から分離し、シリアル化の形式を決定できるアルゴリズムに情報を保持します。そのため、JPEG、PNG、BMPなど。明日新しいシリアル化アルゴリズムが登場した場合、そのアルゴリズムをコーディングするだけで、クラスコントラクトは変更されません。

IPCのコンテキストでは、クラスを分離して、シリアル化に必要な情報を(注釈/属性によって)選択的に宣言できます。次に、シリアライゼーションアルゴリズムで、シリアライゼーションにJSON、Google Protocol Buffers、XMLのいずれを使用するかを決定できます。ジャクソンパーサーを使用するか、カスタムパーサーを使用するかを決定することもできます。モジュール方式で設計するときに簡単に利用できるオプションがたくさんあります。

3
Apoorv Khurasia