2017-03-18 3 views
3

Ich habe eine Schnittstelle, die eine Klasse definiert Serialisierung zu einem Byte-Array.Schnittstellen und Objekt Deserialisierung

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

Der natürliche Partner ist dies ein deserialize Methode, die ich ein Objekt zurückgeben möchten, IByteSerializable implementiert.

Ich bin buggle mit, wie man eine solche Schnittstelle entwerfen.

scheint dies keinen Sinn zu machen:

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

Da die Implementierung von GetObject() nicht static sein kann und es sinnvoll, ein Dummy IByteSerializable Objekt zu verwenden, macht nicht nur die GetObject() Methode aufrufen zu deserialize das tatsächliche Objekt, das ich bin.

Es scheint auch keinen Sinn zu machen, dies zu tun:

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

Ein Factory-Klasse konnte das Problem lösen, aber das fühlt sich an wie es in der Klasse Explosion führen. Außerdem sind die Details, wie eine gegebene IByteSerializable Unterklasse serialisiert und dann deserialisiert wird, ko-abhängig, so dass es sinnvoll ist, sie am selben Ort und nicht in zwei verschiedenen Klassen zu halten. Offensichtlich hängt der exakte Prozess, der zum Deserialisieren eines gegebenen IByteSerializable-Objekts erforderlich ist, vollständig davon ab, wie die GetBytes()-Methode dieses Objekts geschrieben wurde.

Gibt es ein gemeinsames Design oder Muster, das ich verwenden kann, um dieses Problem zu lösen?

Antwort

2

viele unterschiedliche Meinungen über Schnittstellen Es gibt Klassen, und Muster, wenn es um Ihre Frage kommt. Meine persönliche Präferenz würde eine Schnittstelle mit einer byte [] -Eigenschaft und eine abstrakte Klasse mit einer virtuellen Methode () implementieren oder sogar die gesamte Schnittstelle verlieren, die für Sie möglicherweise keine Option ist und nicht gut mit DI und Einheit funktioniert Prüfung):

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

ich möchte betonen, dass VS abstrakte Klassen-Schnittstellen ist eine endlose Diskussion. Wenn Sie Dinge tun können, ohne Schnittstellen zu implementieren und nur abstrakte Klassen zu verwenden, würde ich Ihnen wärmstens empfehlen.

Update 3/18/17: um auf einen Kommentar zu antworten (Definieren von Verhalten ist der Zweck einer Schnittstelle) und erklären, wie ich sehe es die folgende Erklärung hinzufügen.

In diesem Szenario ist das "Verhalten", das wir definieren, "Ein Objekt sollte in ein Byte-Array konvertierbar sein. Das Konvertierungsergebnis sollte wieder in dasselbe Objekt konvertiert werden können." Wir definieren also das Verhalten für ein Objekt und für ein Byte-Array (weil nach der Deserialisierung eines Objekts nicht mehr dasselbe Objekt, sondern nur ein Byte-Array ist).

Aus meiner Sicht, das ist reine Fabrik Muster-Szenario.

// 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 
} 

Update 2 3/18/17: auf einen Kommentar unter einer anderen Antwort (generische Implementierung mit einfacher Verwendung Schnittstelle) zu antworten.

Leider wird es keinen sauberen Weg geben, es zu tun. Es gibt eine dreckige (meine persönliche Meinung: "BAD BAD BAD!") Weg, indem Sie einige Betrug, Definition von Klassen, die Serialisierungsmethoden definieren und Verwendung von Reflexion, um den richtigen Typ zurückzukehren. Das folgende Beispiel wird für die Verwendung von richtigen Feldern mit verschiedenen Typen eine Menge eigener Logik in der Serialisierungsmethode erfordern:

// 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

Danke Phil. Ich verstehe, dass Sie hier Ihre Meinung sagen, aber für mein eigenes Lernen - warum schlagen Sie vor, Schnittstellen zu vermeiden, wenn möglich? – khargoosh

+0

@khargoosh Einfach weil Schnittstellen heute überstrapaziert werden. Die Leute benutzen sie, ohne wirklich zu verstehen, wofür. Eine Schnittstelle ist ein Vertrag, wie eine Anfrage zu versprechen, dass bestimmte Dinge implementiert werden - es gibt keine Notwendigkeit (ich spreche nicht über Abhängigkeit Injektion jetzt), um einen Vertrag zu implementieren, wenn man nicht mehrere Implementierungen von verschiedenen Entwicklern erwartet - Schnittstellen macht das Zeug übermäßig kompliziert. Abstrakte Klassen geben jedoch die Möglichkeit, fast dasselbe zu tun und Standardimplementierungen (virtuelle Methoden) zu definieren. –

+0

Ich bekomme, was du sagst, Phil, und danke für deine Hilfe, es ist sehr geschätzt. Wenn Sie diesen speziellen Fall betrachten, glauben Sie nicht, dass eine "Schnittstelle" mehr Sinn macht als eine "abstrakte" Klasse? Wir definieren hier wirklich das Verhalten, anstatt * zu definieren, was ein Objekt ist *. Viele verschiedene Arten von Klassen könnten eine gültige Verwendung finden, die eine IByteSerializable-Schnittstelle implementiert, aber sie könnten auch besser als Subtypen von Basisklassen definiert sein, die sich stark unterscheiden. Ist das nicht der eigentliche Zweck einer Schnittstelle? – khargoosh

2

Verwenden Sie eine generische Schnittstelle und jede Implementierung kann die generische schließen und den geschlossenen Typ zurückgeben. Bis zur Implementierung muss entschieden werden, welcher Typ zurückgegeben werden soll.

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[]){} 
} 

Wenn Sie Klassen, die dann in einer gemeinsamen Weise Serialisierung tun:

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

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

Dank CodingYoshi - Ich mag die Idee von Generics mit einem Rückgabetyp auf dem 'Deserialize()' definieren Methode. Haben Sie irgendwelche Gedanken darüber, wie Sie diese Interface-Methode in einer tatsächlichen Klasse am besten implementieren können - auf eine sinnvolle Weise? Meine ursprüngliche Frage lautet immer noch: Eine Factory-Klasse würde zu einer Klassenexplosion führen; kann keine statische Schnittstellenmethode implementieren; und aus 'Something' abzuleiten, nur um * dieses Verhalten * oder Funktionalität zu implementieren, scheint keine gute Design-Praxis zu sein! In Ihrem letzten Beispiel würde ich eine Instanz von "SomethingConcrete" benötigen, um ein anderes Objekt zu deserialisieren. – khargoosh

Verwandte Themen