2017-03-18 70 views
3

我有一個接口,它定義了一個類可以序列化爲一個字節數組。接口和對象反序列化

public interface IByteSerializable 
{ 
    byte[] GetBytes(); 
} 

天然的合作伙伴,這是一個反序列化的方法,我想返回實現IByteSerializable的對象。

我在如何設計這樣一個接口。

這似乎沒有任何意義:

public interface IByteSerializable 
{ 
    byte[] GetBytes(); 
    IByteSerializable GetObject(byte[] bytes); 
} 

由於GetObject()執行不能static,它並沒有什麼意義使用虛擬IByteSerializable對象只是調用GetObject()方法反序列化實際的對象。

它也似乎沒有意義的,這樣做:

public interface IByteSerializableFactory 
{ 
    IByteSerializable GetObject(byte[] bytes); 
} 

一個工廠類可以解決這個問題,但這種感覺就像它會導致類爆炸。此外,給定IByteSerializable子類如何序列化然後反序列化的細節是相互依賴的,所以將它們保持在相同的地方而不是兩個不同的類中是有意義的。顯然,反序列化給定的IByteSerializable對象所需的確切過程完全取決於該對象的GetBytes()方法是如何寫入的。

有沒有可用於解決此問題的常見設計或模式?

回答

2

,當涉及到你的問題有很多的接口,類和模式不同意見。我的個人首選項將實現與byte []屬性和虛擬方法的抽象類的接口(或完全丟失接口,這可能不是一個選項,並與DI和單位測試):

public interface IByteSerializable 
{ 
    byte[] SerializableByteObject { get; } 
} 

public abstract class ByteSerializable : IByteSerializable 
{ 
    public byte[] SerializableByteObject { get; } 
    protected virtual byte[] GetBytes() { 
     return SerializableByteObject; 
    } 
    public abstract IByteSerializable GetObject(); 
    //{ // You can make this method virtual and use Impl method: 
      // GetObjectImpl(SerializableByteObject); 
    //} 
    protected internal IByteSerializable GetObjectImpl(byte[] bytes) { 
     // If you need a default implementation (GetObject() should be protected virtual then) 
     // return IByteSerializable...; 
    } 
} 

我想強調的是接口VS抽象類是一個永無止境的討論。如果你可以做的東西沒有實現接口,只使用抽象類 - 我強烈建議這樣做。

更新3/18/17:回覆評論(定義行爲是接口的目的)並解釋我如何看到它添加下面的解釋。

在這種情況下,我們定義的「行爲」是「一個對象應該可以轉換爲一個字節數組,轉換結果應該可以轉換回同一個對象。所以我們實際上是爲一個對象和一個字節數組定義行爲(因爲在一個對象被反序列化之後 - 它不再是同一個對象,它只是一個字節數組)。

從我的角度來看,這是純粹的工廠模式場景。

// Let's define an interface for our serializable type of objects factory 
public interface IByteSerializableFactory<T> 
{ 
    T CreateFromBytes(byte[] objectDataToUse); 
    byte[] CovertToBytes(T objectToConvert); 
} 

// Interface for any class that needs a serialization factory 
// This is not even necessary, but I like it to enforce people to implement simple methods that reference the factory. 

public interface IByteSerializable<T> 
{ 
    IByteSerializableFactory<T> GetFactory(); 
} 

// Now a moment comes for us to have this kind of class. We need to build a factory first (because our interface requires a GetFactory() implementation. We can lose the IByteSerializable interface altogether, but then we lose a way to let people know which factory should be used. 

public class SomeBaseClassSerializationFactory : IByteSerializableFactory<SomeBaseClass> 
{ 
    public SomeBaseClass CreateFromBytes(byte[] objectDataToUse) { //... 
     return new SomeClass(); 
    } 
    public byte[] CovertToBytes(SomeBaseClass objectToConvert) { //... 
     return new byte[1]; 
    } 
} 

// We have a factory, let's implement a class. 

public abstract class SomeBaseClass : IByteSerializable<SomeBaseClass> 
{ 
    public virtual IByteSerializableFactory<SomeBaseClass> GetFactory() { 
     return new SomeBaseClassSerializationFactory();            
    } 
} 

public class SomeClass : SomeBaseClass { 
    // Now we're independent. Our derived classes do not need to implement anything. 
    // If the way the derived class is serialized is different - we simply override the method 
} 

更新2 17年3月18日:回覆評論下了不同的答案(通用實現簡單的使用界面)。

不幸的是,將沒有乾淨的方式來做到這一點。有一個骯髒的(我個人的意見:「壞的壞!」)的方式通過使用一些作弊,定義類定義序列化方法和使用反射返回正確的類型。下面的例子將需要在序列化方法有很多自定義邏輯的使用正確的字段有不同的類型:

// You define an enum with action and a dictionary with a collection of serialization methods. 
public enum SerializationAction { 
    ToBytes, 
    ToObject  
} 

// It can also be an enum, but it's easier to test with a collection of strings. 
public static readonly string[] SerializationKindList = new string[] { 
    "FirstKind", 
    "SecondKind" 
}; 

// This generic class can have an implementation of all the handlers. Additional switching can be done by type, or reflection can be used to find properties for different classes and construct different classes. 
public class SerializationMethod { 
    public object ProcessByKind (string kindToUse, SerializationAction action, object objectToProcess) { 
     if (kindToUse == "FirstKind") { 
      if (action == SerializationAction.ToBytes) { 
       return new byte[1]; 
      } 

      return new SomeClass(); // These would need to be your hard implementations. Not clean. 
     } else { 
      throw new NotImplementedException();  
     } 
    } 
} 

// This struct type defines the serialization method and is required for the interface implementation 
public struct ByteSerialization 
{ 
    public string SerializationTypeName { get; private set; } 
    public ByteSerialization(string kindToUse) { 
     if (!SerializationKindList.Contains(kindToUse)) { 
      throw new ArgumentException(); 
     } 

     SerializationTypeName = kindToUse; 
    } 
    public byte[] Deserialize(object objectToProcess) { 
     var serializationMethod = new SerializationMethod(); 
     return (byte[])serializationMethod.ProcessByKind(this.SerializationTypeName, SerializationAction.ToBytes, objectToProcess); 
    } 
    public object Serialize(byte[] byteArrayToProcess) { 
     var serializationMethod = new SerializationMethod(); 
     return serializationMethod.ProcessByKind(this.SerializationTypeName, SerializationAction.ToObject, byteArrayToProcess); 
    } 
} 


// Interface for any class that needs to use generic serialization 
public interface IByteSerializable 
{ 
    ByteSerialization serializationType { get; } 
} 

// Creating extension methods for the interface to make the life easier 
public static class IByteSerializableExtensions { 
    public static byte[] DeserializeObjectIntoBytes(this IByteSerializable objectToProcess) { 
     return objectToProcess.serializationType.Deserialize(objectToProcess); 
    } 
    public static void SerializeObjectFromBytes(this IByteSerializable objectToProcess, byte[] fromBytes) { 
     var someObjectData = objectToProcess.serializationType.Serialize(fromBytes); 
    } 
} 


// Abstract base class implementation with static readonly field. 
// Only downside - there is no way to enforce the config of this field in the constructor from the interface. 
// There also no way to make sure this field always gets set for other implementations of IByteSerializable 
public abstract class SomeBaseClass : IByteSerializable 
{ 
    private static readonly ByteSerialization _serializationType = new ByteSerialization("FirstKind"); 

    public ByteSerialization serializationType { get { return _serializationType; } } 
} 

public class SomeClass : SomeBaseClass { 

} 


// And here's how one would use it. You will need to create a new object of the class before serializing from bytes. 
var someClass = new SomeClass(); 
var bytes = someClass.DeserializeObjectIntoBytes(); 
var someClass2 = new SomeClass(); 
var byteArray = new byte[1]; 
someClass2.SerializeObjectFromBytes(byteArray); 
+0

感謝菲爾。我知道你在這裏陳述你的觀點,但對於我自己的學習 - 爲什麼你建議儘可能避免接口? – khargoosh

+0

@khargoosh僅僅因爲界面今天被過度使用。人們正在使用它們而沒有真正理解什麼。一個接口是一個契約,就像是要求承諾某些事情將被實現 - 如果不期望多個開發者實現多個實現,就不需要(我現在不談論依賴關係注入)來實現契約 - 接口會使東西過於複雜。 抽象類雖然給人一種能力做幾乎相同的事情和定義默認實現(虛擬方法)。 –

+0

我得到你說的菲爾,並感謝您的幫助,非常感謝。看看這個具體的例子,你不覺得''interface'比'abstract'類更有意義嗎?我們真的在這裏定義行爲,而不是定義*一個對象是什麼*。許多不同類型的類可以找到實現「IByteSerializable」接口的有效用途 - 但它們也可能更好地定義爲基本類的子類型,它們的區別很大。這不是接口的目的嗎? – khargoosh

2

使用通用接口,每個實現可以關閉通用並返回封閉類型。直到實施決定什麼類型返回。

public interface ICustomSerializable<T> where T : class 
{ 
    byte[] GetBytes(); 
    T Deserialize(byte[]); 
} 

public class Foo : ICustomSerializable<Foo> 
{ 
    public byte[] GetBytes() {} 
    public Foo Deserialize(byte[]){} 
} 

public class Bar : ICustomSerializable<Bar> 
{ 
    public byte[] GetBytes() {} 
    public Bar Deserialize(byte[]){} 
} 

如果您有做系列化然後在一種常見的方式類:

public abstract class Something 
{ 
    public byte[] GetBytes() { //common code } 
} 

public class SomethingConcrete : Something, ICustomSerializable<SomethingConcrete> 
{ 
    public SomethingConcrete Deserialize(byte[]){} 
} 
+0

感謝CodingYoshi - 我喜歡使用泛型定義的'反序列化的返回類型(的想法)'方法。你有沒有想過如何在實際的課堂中最好地實現這種接口方法 - 以一種合理的方式?我的原始問題仍然是:工廠班會導致課堂爆炸;不能實現靜態接口方法;並從'Something'派生來實現*這種行爲*或功能似乎不是一個好的設計實踐!在上面的最後一個例子中,我需要一個'SomethingConcrete'的實例來反序列化一個不同的對象。 – khargoosh