Có nên tuần tự hóa và giải tuần tự hóa là trách nhiệm của lớp được tuần tự hóa?


16

Tôi hiện đang trong giai đoạn thiết kế lại một số lớp mô hình của ứng dụng C # .NET. (Mô hình như trong M của MVC). Các lớp mô hình đã có nhiều dữ liệu, hành vi và mối liên hệ được thiết kế tốt. Tôi đang viết lại mô hình từ Python sang C #.

Trong mô hình Python cũ, tôi nghĩ rằng tôi thấy một mụn cóc. Mỗi mô hình biết cách tự tuần tự hóa và logic tuần tự hóa không liên quan gì đến phần còn lại của hành vi của bất kỳ lớp nào. Ví dụ: hãy tưởng tượng:

  • Imagelớp với một .toJPG(String filePath) .fromJPG(String filePath)phương thức
  • ImageMetaDatalớp với một .toString().fromString(String serialized)phương thức.

Bạn có thể tưởng tượng làm thế nào các phương thức tuần tự hóa này không gắn kết với phần còn lại của lớp, nhưng chỉ có lớp có thể được đảm bảo để biết đủ dữ liệu để tự tuần tự hóa.

Đây có phải là một thực tế phổ biến cho một lớp để biết làm thế nào để tuần tự hóa và giải tuần tự hóa nó? Hay tôi đang thiếu một mô hình chung?

Câu trả lời:


16

Tôi thường tránh cho lớp biết cách tự xê-ri hóa, vì một vài lý do. Đầu tiên, nếu bạn muốn (de) tuần tự hóa sang / từ một định dạng khác, bây giờ bạn cần làm ô nhiễm mô hình với logic bổ sung đó. Nếu mô hình được truy cập thông qua một giao diện, thì bạn cũng làm ô nhiễm hợp đồng.

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

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

Nhưng nếu bạn muốn tuần tự hóa nó thành / từ PNG và GIF thì sao? Bây giờ lớp học trở thành

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

Thay vào đó, tôi thường thích sử dụng một mẫu tương tự như sau:

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

Bây giờ, tại thời điểm này, một trong những lưu ý với thiết kế này là các bộ nối tiếp cần phải biết identityđối tượng mà nó được tuần tự hóa. Một số người sẽ nói rằng đây là thiết kế tồi, vì việc triển khai rò rỉ bên ngoài lớp học. Rủi ro / phần thưởng của việc này thực sự tùy thuộc vào bạn, nhưng bạn có thể điều chỉnh một chút các lớp để làm một cái gì đó như

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

Đây là một ví dụ chung, vì hình ảnh thường có siêu dữ liệu đi cùng với nó; những thứ như mức độ nén, không gian màu, vv có thể làm phức tạp quá trình.


2
Tôi khuyên bạn nên tuần tự hóa đến / từ một IOStream trừu tượng hoặc định dạng nhị phân (văn bản là một loại định dạng nhị phân cụ thể). Bằng cách này, bạn không bị hạn chế ghi vào một tập tin. Muốn gửi dữ liệu qua mạng sẽ là một vị trí đầu ra thay thế quan trọng.
unolysampler 04/07/2015

Điểm rất tốt. Tôi đã suy nghĩ về điều đó, nhưng đã bị rắm não. Tôi sẽ cập nhật mã.
Zymus 04/07/2015

Tôi giả định rằng khi nhiều định dạng tuần tự hóa được hỗ trợ (nghĩa là có nhiều triển khai ImageSerializergiao diện hơn được viết), ImageSerializergiao diện cũng sẽ cần phát triển. EX: Một định dạng mới hỗ trợ nén tùy chọn, các định dạng trước không -> thêm cấu hình nén vào ImageSerializergiao diện. Nhưng sau đó, các định dạng khác bị lộn xộn với các tính năng không áp dụng cho chúng. Tôi càng nghĩ về nó, tôi càng ít nghĩ đến việc thừa kế áp dụng ở đây.
kdbanman

Trong khi tôi hiểu bạn đến từ đâu, tôi cảm thấy đó không phải là vấn đề, vì một vài lý do. Nếu đó là định dạng hình ảnh hiện có, có khả năng bộ xê-ri hóa đã biết cách xử lý các mức nén và nếu đó là định dạng mới, bạn sẽ phải viết nó. Một giải pháp, là làm quá tải các phương thức, đại loại như vậy void serialize(Image image, Stream outputStream, SerializerSettings settings);, đó chỉ là một trường hợp kết nối logic nén và siêu dữ liệu hiện có sang phương thức mới.
Zymus 6/07/2015

3

Tuần tự hóa là một vấn đề gồm hai phần:

  1. Kiến thức về cách khởi tạo một lớp aka cấu trúc .
  2. Kiến thức về cách duy trì / chuyển thông tin cần thiết để khởi tạo một lớp hay còn gọi là cơ học .

Càng xa càng tốt, cấu trúc nên được tách biệt khỏi cơ học . Điều này làm tăng tính mô-đun của hệ thống của bạn. Nếu bạn chôn thông tin về số 2 trong lớp thì bạn sẽ phá vỡ tính mô đun bởi vì bây giờ lớp của bạn phải được sửa đổi để theo kịp các cách thức tuần tự hóa mới (nếu chúng xuất hiện).

Trong ngữ cảnh xê-ri hóa hình ảnh, bạn sẽ giữ thông tin về xê-ri hóa tách biệt với chính lớp đó và giữ nó thay vì trong các thuật toán có thể xác định định dạng của xê-ri hóa - trước đó, các lớp khác nhau cho JPEG, PNG, BMP, v.v ... Nếu ngày mai mới Thuật toán tuần tự hóa đi kèm với bạn chỉ đơn giản là mã thuật toán đó và hợp đồng lớp của bạn vẫn không thay đổi.

Trong ngữ cảnh của IPC, bạn có thể tách lớp của mình và sau đó khai báo có chọn lọc thông tin cần thiết để tuần tự hóa (bằng chú thích / thuộc tính). Sau đó, thuật toán tuần tự hóa của bạn có thể quyết định nên sử dụng JSON, Bộ đệm giao thức Google hay XML để tuần tự hóa. Nó thậm chí có thể quyết định nên sử dụng trình phân tích cú pháp Jackson hoặc trình phân tích cú pháp tùy chỉnh của bạn - có nhiều tùy chọn bạn có thể dễ dàng nhận được khi thiết kế theo kiểu mô-đun!


1
Bạn có thể cho tôi một ví dụ về làm thế nào hai điều đó có thể được tách rời? Tôi không chắc là tôi hiểu sự khác biệt.
kdbanman 6/07/2015
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.