Я вообще избегаю, чтобы класс знал, как сериализовать себя, по нескольким причинам. Во-первых, если вы хотите (де) сериализовать в / из другого формата, теперь вам нужно загрязнить модель этой дополнительной логикой. Если модель доступна через интерфейс, вы также загрязняете контракт.
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) { ... }
}
Теперь, на этом этапе, одно из предостережений с этим дизайном заключается в том, что сериализаторам необходимо знать 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);
}
}
Это более общий пример, поскольку изображения обычно содержат метаданные, которые сопровождают его; такие вещи, как уровень сжатия, цветовое пространство и т. д., которые могут усложнить процесс.
ImageSerializer
написано больше реализаций интерфейса),ImageSerializer
интерфейс также должен будет расти. Пример: новый формат поддерживает необязательное сжатие, предыдущие не сделали -> добавили возможность настройки сжатия вImageSerializer
интерфейс. Но тогда другие форматы забиты функциями, которые к ним не относятся. Чем больше я думаю об этом, тем меньше я думаю, что наследование применяется здесь.void serialize(Image image, Stream outputStream, SerializerSettings settings);
Тогда» - это всего лишь случай подключения существующей логики сжатия и метаданных к новому методу.Сериализация состоит из двух частей:
Насколько это возможно, структура должна быть отделена от механики . Это увеличивает модульность вашей системы. Если вы похороните информацию о # 2 в вашем классе, то вы нарушите модульность, потому что теперь ваш класс должен быть изменен, чтобы идти в ногу с новыми способами сериализации (если они появятся).
В контексте сериализации изображений вы должны хранить информацию о сериализации отдельно от самого класса и хранить ее скорее в алгоритмах, которые могут определять формат сериализации - поэтому разные классы для JPEG, PNG, BMP и т. Д. Если завтра новая Алгоритм сериализации приходит вместе с тем, что вы просто кодируете этот алгоритм и ваш классовый контракт остается неизменным.
В контексте IPC вы можете хранить свой класс отдельно и затем выборочно объявлять информацию, необходимую для сериализации (посредством аннотаций / атрибутов). Тогда ваш алгоритм сериализации может решить, использовать ли JSON, буфер протокола Google или XML для сериализации. Он даже может решить, использовать ли парсер Джексона или свой собственный парсер - есть много опций, которые вы легко получите, когда будете разрабатывать модульно!
источник