2017-03-18 51 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