직렬화 및 직렬화 해제는 직렬화되는 클래스의 책임이어야합니까?


16

현재 C # .NET 응용 프로그램의 여러 모델 클래스의 (재) 디자인 단계에 있습니다. (MVC의 M과 같은 모델). 모델 클래스에는 이미 잘 디자인 된 많은 데이터, 동작 및 상호 관계가 있습니다. 파이썬에서 C #으로 모델을 다시 작성하고 있습니다.

오래된 파이썬 모델에서는 사마귀가 보인다고 생각합니다. 각 모델은 직렬화 방법을 알고 있으며 직렬화 논리는 클래스의 나머지 동작과 는 아무런 관련없습니다 . 예를 들어,

  • ImageA의 클래스 .toJPG(String filePath) .fromJPG(String filePath)방법
  • ImageMetaData.toString().fromString(String serialized)메소드 가있는 클래스 .

이러한 직렬화 방법이 나머지 클래스와 어떻게 통합되지 않는지 상상할 수 있지만 클래스 만 직렬화하기에 충분한 데이터를 알고 있다고 보장 할 수 있습니다.

클래스가 자신을 직렬화하고 역 직렬화하는 방법을 아는 것이 일반적인 관행입니까? 아니면 일반적인 패턴이 누락 되었습니까?

답변:


16

나는 일반적으로 클래스가 몇 가지 이유로 직렬화하는 방법을 알지 못하게합니다. 먼저 다른 형식으로 (직렬화) 직렬화하려는 경우 이제 해당 추가 논리로 모델을 오염시켜야합니다. 인터페이스를 통해 모델에 액세스하면 계약도 오염시킵니다.

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

이미지에는 일반적으로 메타 데이터가 포함되어 있으므로 일반적인 예입니다. 압축 수준, 색 공간 등이 프로세스를 복잡하게 만들 수 있습니다.


2
추상 IOStream 또는 이진 형식 (텍스트는 특정 종류의 이진 형식 임)으로 /에서 직렬화하는 것이 좋습니다. 이런 식으로 파일에 쓰도록 제한되지 않습니다. 네트워크를 통해 데이터를 보내려는 것은 중요한 대체 출력 위치입니다.
unholysampler

아주 좋은 지적입니다. 나는 그것에 대해 생각하고 있었지만 뇌 방귀가 있었다. 코드를 업데이트하겠습니다.
Zymus

더 많은 직렬화 형식이 지원됨에 따라 (즉, 더 많은 ImageSerializer인터페이스 구현 이 작성 됨) ImageSerializer인터페이스도 커져야 한다고 가정합니다 . EX : 새로운 형식은 압축 옵션을 지원하지만 이전 형식은 압축 구성 기능을 ImageSerializer인터페이스에 추가하지 않았습니다 . 그러나 다른 형식은 적용되지 않는 기능으로 복잡합니다. 내가 그것에 대해 더 많이 생각할수록, 상속이 여기에 덜 적용된다고 생각합니다.
kdbanman

어디에서 왔는지 이해하지만 몇 가지 이유로 문제가되지 않습니다. 기존 이미지 형식 인 경우 serializer가 압축 수준을 처리하는 방법을 이미 알고있을 가능성이 높으며 새로운 수준 인 경우 어쨌든 작성해야합니다. 한 가지 해결책은 void serialize(Image image, Stream outputStream, SerializerSettings settings);다음 과 같은 방법으로 메소드를 오버로드하는 것입니다. 그런 다음 기존 압축 및 메타 데이터 논리를 새 메소드에 연결하는 경우입니다.
Zymus

3

직렬화는 두 부분으로 인한 문제입니다.

  1. 클래스 일명 구조 를 인스턴스화하는 방법에 대한 지식 .
  2. 클래스 일명 역학 을 인스턴스화하는 데 필요한 정보를 유지 / 전송하는 방법에 대한 지식 .

가능한 한 구조기계 와 분리되어 있어야합니다 . 이것은 시스템의 모듈성을 증가시킵니다. 클래스 2에 정보를 묻 으면 모듈화가 중단됩니다. 이제 클래스가 새로운 직렬화 방법에 맞게 수정되어야합니다.

이미지 직렬화의 맥락에서 직렬화에 대한 정보를 클래스 자체와 별도로 유지하고 직렬화 형식을 결정할 수있는 알고리즘 (따라서 JPEG, PNG, BMP 등의 다른 클래스)을 유지하는 알고리즘으로 유지합니다. 직렬화 알고리즘은 단순히 해당 알고리즘을 코딩하고 클래스 계약은 변경되지 않습니다.

IPC와 관련하여 클래스를 별도로 유지 한 다음 직렬화에 필요한 정보 (주석 / 속성 별)를 선택적으로 선언 할 수 있습니다. 그런 다음 직렬화 알고리즘이 직렬화에 JSON, Google 프로토콜 버퍼 또는 XML을 사용할지 여부를 결정할 수 있습니다. Jackson 파서 또는 사용자 정의 파서를 사용할지 여부를 결정할 수도 있습니다. 모듈 방식으로 디자인 할 때 쉽게 얻을 수있는 많은 옵션이 있습니다!


1
이 두 가지를 어떻게 분리 할 수 ​​있는지 예를 들어 주시겠습니까? 나는 그 차이점을 이해하지 못한다.
kdbanman
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.