2017-03-18 21 views
3

バイト配列にシリアル化可能なクラスを定義するインターフェイスがあります。インターフェイスとオブジェクトの逆シリアル化

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

これに自然なパートナーは、私がIByteSerializableを実装するオブジェクトを返したいデシリアライズする方法、です。

私はこのようなインターフェイスをどのように設計するかについて悩んでいます。

これが意味をなすようには見えません。

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

GetObject()の実装はstaticすることはできませんし、それだけにGetObject()メソッドを呼び出すためにダミーIByteSerializableオブジェクトを使用しても意味がありませんのでデシリアライズする実際にはオブジェクトです。

また、これを行うには意味をなすようには見えません。

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

ファクトリクラスは、問題を解決することができ、それは、クラスの爆発になりますように、これは感じています。また、指定されたIByteSerializableサブクラスが直列化されてデシリアライズされる方法の詳細は、共依存であるため、2つの異なるクラスではなく、同じ場所にそれらを保持することは理にかなっています。明らかに、特定のIByteSerializableオブジェクトを逆シリアル化するために必要な正確なプロセスは、そのオブジェクトのGetBytes()メソッドがどのように記述されたかによってまったく異なります。

この問題を解決するために使用できる共通のデザインやパターンはありますか?

答えて

2

を、それはあなたの質問に来るときインタフェース、クラス、およびパターンの異なる意見がたくさんあります。私のパーソナルの設定では、byte []プロパティと仮想メソッド()を持つ抽象クラスを実装しているか、またはインタフェースを完全に失っていても、オプションではない可能性があります。テスト):

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 3/18/17:別の答え(インターフェースの簡単な使用と一般的な実装)の下でのコメントに返信します。

残念ながら、それを行うためのクリーンな方法はありません。私の個人的意見:"BAD BAD BAD!")は、シリアライズメソッドを定義するクラスを定義し、リフレクションを使って正しいタイプを返す方法を使用しています。以下の例は、さまざまな種類の正しいフィールドを使用するためのシリアル化の方法で、カスタム・ロジックの多くを必要とします:

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

ありがとうPhil。私はあなたがここに意見を述べていることを理解していますが、私自身の学習のために、なぜ可能な場合にインターフェイスを避けることを提案していますか? – khargoosh

+0

@khargoosh今日はインターフェイスが過度に使用されているためです。人々は何を本当に理解することなくそれらを使用しています。インタフェースは契約であり、特定のものが実装されることを約束する要求のように - 異なる開発者が複数の実装を期待していない場合には、契約を実装する必要はありません(今は依存性注入については言及していません) - インタフェース物事は過度に複雑になります。しかし、クラスはほとんど同じことを行い、デフォルトの実装(仮想メソッド)を定義する能力を与えます。 –

+0

私はあなたがPhilと言っているものを手に入れました。あなたの助けに感謝します。この特定のケースを見ると、 '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 - 私は '逆シリアル化(上の戻り値の型を定義するためにジェネリックを使用してのアイデアが好きです)、'方法。実際のクラスでそのインターフェースメソッドをどのように実装するのが最善の方法であるかは、理にかなっていますか?私の元々の質問は次の通りです。ファクトリクラスはクラスの爆発を引き起こします。静的インタフェースメソッドを実装することはできません。 *この振る舞い*を実装するためだけに「何か」から派生するか、または機能性は良い設計の習慣ではないようです!上記の最後の例では、別のオブジェクトを逆シリアル化するために 'SomethingConcrete'のインスタンスが必要です。 – khargoosh

関連する問題