Serileştirme ve serileştirme serileştirilecek sınıfın sorumluluğu olmalı mı?


16

Şu anda bir C # .NET uygulamasının birkaç model sınıfının (yeniden) tasarım aşamasındayım. (MVC M'de olduğu gibi model). Model sınıfları zaten çok iyi tasarlanmış verilere, davranışlara ve ilişkilere sahiptir. Modeli Python'dan C # 'a yeniden yazıyorum.

Eski Python modelinde sanırım bir siğil görüyorum. Her model kendini nasıl serileştireceğini bilir ve serileştirme mantığının sınıfların herhangi birinin davranışıyla ilgisi yoktur. Örneğin, hayal edin:

  • Imagebir .toJPG(String filePath) .fromJPG(String filePath)yöntemle sınıf
  • ImageMetaDataa .toString()ve .fromString(String serialized)yöntemiyle sınıf .

Bu serileştirme yöntemlerinin sınıfın geri kalanıyla nasıl uyumlu olmadığını hayal edebilirsiniz, ancak sadece sınıfın kendini serileştirmek için yeterli veriyi bileceği garanti edilebilir.

Bir sınıfın kendini nasıl serileştirip serileştireceğini bilmesi yaygın bir uygulama mıdır? Yoksa ortak bir paternim yok mu?

Yanıtlar:


16

Genellikle birkaç nedenden dolayı sınıfın kendini nasıl serileştireceğini bilmesini önlerim. İlk olarak, farklı bir formata / seriden serileştirmek istiyorsanız, şimdi modeli bu ekstra mantıkla kirletmeniz gerekir. Modele bir arayüz aracılığıyla erişilirse, sözleşmeyi de kirletirsiniz.

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

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

Peki bir PNG ve GIF'e / serisinden serileştirmek isterseniz ne olur? Şimdi sınıf

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

Bunun yerine, genellikle aşağıdakine benzer bir desen kullanmayı seviyorum:

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

Şimdi, bu noktada, bu tasarıma sahip uyarılardan biri, serileştiricilerin serileştirdiği identitynesneyi bilmeleri gerektiğidir . Bazıları, uygulamanın sınıfın dışına sızması nedeniyle bunun kötü bir tasarım olduğunu söyleyebilir. Bunun riski / ödülü gerçekten size kalmış, ancak böyle bir şey yapmak için sınıfları biraz değiştirebilirsiniz.

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

Bu daha genel bir örnektir, çünkü görüntüler genellikle onunla birlikte gelen meta verilere sahiptir; Sıkıştırma seviyesi, renk aralığı vb.


2
Ben soyut bir IOStream veya ikili biçim / metin serileştirmek tavsiye ederim (metin belirli bir ikili biçim türüdür). Bu şekilde bir dosyaya yazmakla sınırlı kalmazsınız. Verileri ağ üzerinden göndermek istemek önemli bir alternatif çıktı konumu olacaktır.
unholysampler

Çok iyi bir nokta. Bunu düşünüyordum, ama beyin osuruydu. Kodu güncelleyeceğim.
Zymus

Daha fazla serileştirme formatı desteklendiğinden (yani ImageSerializerarayüzün daha fazla uygulaması yazıldığından), ImageSerializerarayüzün de büyümesi gerekeceğini varsayıyorum . EX: Yeni bir format isteğe bağlı sıkıştırmayı destekler, öncekiler ImageSerializerarayüze sıkıştırma yapılandırması eklemez . Ancak daha sonra diğer biçimler kendileri için geçerli olmayan özelliklerle doludur. Ne kadar çok düşünürsem, kalıtımın o kadar az geçerli olduğunu düşünüyorum.
kdbanman

Nereden geldiğinizi anlasam da, bunun bir neden olmadığını hissediyorum, birkaç nedenden dolayı. Varolan bir görüntü biçimiyse, serileştiricinin sıkıştırma düzeyleriyle nasıl başa çıkacağını zaten biliyor olması ve yeniyse, yine de yazmanız gerekir. Bir çözüm, yöntemleri aşırı yüklemek, gibi bir şey void serialize(Image image, Stream outputStream, SerializerSettings settings);O zaman sadece mevcut sıkıştırma ve meta veri mantığını yeni yönteme bağlamak durumunda.
Zymus

3

Serileştirme iki bölümlü bir sorundur:

  1. Bir sınıf aka yapının nasıl örnekleneceği hakkında bilgi .
  2. Mekanik olarak adlandırılan bir sınıfı somutlaştırmak için gerekli olan bilginin nasıl sürdürüleceği / aktarılacağı hakkında bilgi .

Yapı mümkün olduğunca mekanikten ayrı tutulmalıdır . Bu, sisteminizin modülerliğini artırır. Sınıfınızdaki # 2 bilgisini gömerseniz, modülerliği bozarsınız çünkü artık sınıfınız yeni serileştirme yollarına ayak uydurmak için değiştirilmelidir (eğer gelirse).

Görüntü serileştirme bağlamında, serileştirme ile ilgili bilgileri sınıfın kendisinden ayrı tutacak ve daha ziyade serileştirmenin formatını belirleyebilecek algoritmalarda saklayacaksınız - bundan dolayı JPEG, PNG, BMP vb. İçin farklı sınıflar. serileştirme algoritması size sadece bu algoritmayı kodlar ve sınıf sözleşmeniz değişmeden kalır.

IPC bağlamında, sınıfınızı ayrı tutabilir ve sonra serileştirme için gerekli bilgileri (ek açıklamalara / niteliklere göre) seçerek bildirebilirsiniz. Ardından serileştirme algoritmanız serileştirme için JSON, Google Protokol Tamponları veya XML kullanıp kullanmayacağınıza karar verebilir. Jackson ayrıştırıcısını mı yoksa özel ayrıştırıcıyı mı kullanacağınıza bile karar verebilir - modüler bir şekilde tasarladığınızda kolayca elde edebileceğiniz birçok seçenek var!


1
Bana bu iki şeyin nasıl ayrılabileceğine dair bir örnek verebilir misiniz? Ayrımı anladığımdan emin değilim.
kdbanman
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.