Original in Russian: http://programmingmindstream.blogspot.ru/2014/09/blog-post_3.html
In a way, based on:
Briefly. About factories and http://programmingmindstream.blogspot.ru/2014/08/istorage-tdd.html
What do I want to talk about?
We have our own implementation of IStorage (and, therefore, IStream).
It is good. In a sense. It is good at least because it works stable for already 15 years.
But there are “some problems”.
I examine these problems scrupulously now.
I do not speak about the “problems” of complex implementation in heterogeneous network environment.
There are “local problems”, too.
For example, the fact that everything is built there on "binary serialisation" .
I.e. something like this:
You can look at the “real code” here.
What’s the problem? The problem is the format of TStoreHeader cannot be changed “for no reason” – everything will “get displaced”.
What should we do?
To begin with, we do something like this:
What’s next?
Something like this:
What have we done here?
We’ve introduced a base abstract class - TStoreHeaderAbstract and a factory - TStoreHeaderFactory.
How can we change data format?
In this way:
Moreover, we can also write in this way:
What have we done here?
In the first place, we’ve divided “binary serialization” of a record into a few “binary serializations” of separate fields.
In the second place, we’ve reversed some of the fields in order to demonstrate “the heart of the approach”.
Lots of questions are left beyond the scope.
For example – “where do we get GetVersionGUID?”
Or – “what should we do if writing of version is not supported initially”?
These are important questions. But they “do not fit in the framework” of the post. Generally speaking, these are important, but more “technical questions”. If you are interested I will analyze them in detail.
For now I’ll leave them “beyond the scope”.
What is the result?
In sum, in my opinion, it has been shown that factories are a weighty supplement to encapsulation and polymorphism.
First we’ve used polymorphism - by introducing the type TStoreHeaderAbstract.
Then we’ve used encapsulation – by dividing TStoreHeader.Data and TStoreHeaderNew.Data.
Through polymorphism and encapsulation we’ve, in a way, avoided the “binary serialization”.
Why?
Because the next step could be the following:
- i.e. here we no more write/read “binarily”, but as it is written in SomeOtherDataType.Load/SomeOtherDataType.Save.
So.
What I wanted to show?
I’ll repeat.
I wanted to show that factories are a weighty supplement to encapsulation and polymorphism.
(Let’s say, factories are “twice the polymorphism”. That is because polymorphism “starts to have effect” even before object instance is created. Then, factory polymorphism works. Should I write about polymorphic factories?)
You are to judge how much I succeeded in achieving my goal.
I guess I did not “reinvented the wheel”, but I hope I’ve written something of use.
In a way, based on:
Briefly. About factories and http://programmingmindstream.blogspot.ru/2014/08/istorage-tdd.html
What do I want to talk about?
We have our own implementation of IStorage (and, therefore, IStream).
It is good. In a sense. It is good at least because it works stable for already 15 years.
But there are “some problems”.
I examine these problems scrupulously now.
I do not speak about the “problems” of complex implementation in heterogeneous network environment.
There are “local problems”, too.
For example, the fact that everything is built there on "binary serialisation" .
I.e. something like this:
type TStoreHeader = record rNextPosition : Int64; rRealSize : Int64; ... end;//TStoreHeader ... procedure SomeReadCode; var l_H : TStoreHeader; begin ... Stream.Read(l_H, SizeOf(l_H); ... end; ... procedure SomeWriteCode; var l_H : TStoreHeader; begin ... Stream.Write(l_H, SizeOf(l_H); ... end;
You can look at the “real code” here.
What’s the problem? The problem is the format of TStoreHeader cannot be changed “for no reason” – everything will “get displaced”.
What should we do?
To begin with, we do something like this:
type TStoreHeaderRec = record rNextPosition : Int64; rRealSize : Int64; ... end;//TStoreHeaderRec TStoreHeader = class private Data : TStoreHeaderRec; public procedure Load(aStream: TStream); procedure Save(aStream: TStream); end;//TStoreHeader ... procedure TStoreHeader.Load(aStream: TStream); begin aStream.ReadBuffer(Data, SizeOf(Data); end; procedure TStoreHeader.Save(aStream: TStream); begin aStream.WriteBuffer(Data, SizeOf(Data); end; ... procedure SomeReadCode; var l_H : TStoreHeader; begin ... l_H := TStoreHeader.Create; ... l_H.Load(Stream); ... end; ... procedure SomeWriteCode; var l_H : TStoreHeader; begin ... l_H := TStoreHeader.Create; ... l_H.Save(Stream); ... end;
What’s next?
Something like this:
type TStoreHeaderAbstract = class public procedure Load(aStream: TStream); virtual; abstract; procedure Save(aStream: TStream); virtual; abstract; end;// TStoreHeaderAbstract ... TStoreHeaderRec = record rNextPosition : Int64; rRealSize : Int64; ... end;//TStoreHeaderRec TStoreHeader = class(TStoreHeaderAbstract) private Data : TStoreHeaderRec; public procedure Load(aStream: TStream); override; procedure Save(aStream: TStream); override; end;//TStoreHeader TStoreHeaderFactory = class public class function Make: TStoreHeaderAbstract; end;//TStoreHeaderFactory ... class function TStoreHeaderFactory.Make: TStoreHeaderAbstract; begin Result := TStoreHeader.Create; end; procedure TStoreHeader.Load(aStream: TStream); begin aStream.ReadBuffer(Data, SizeOf(Data); end; procedure TStoreHeader.Save(aStream: TStream); begin aStream.WriteBuffer(Data, SizeOf(Data); end; ... procedure SomeReadCode; var l_H : TStoreHeaderAbstract; begin ... l_H := TStoreHeaderFactory.Make; ... l_H.Load(Stream); ... end; ... procedure SomeWriteCode; var l_H : TStoreHeaderAbstract; begin ... l_H := TStoreHeaderFactory.Make; ... l_H.Save(Stream); ... end;
What have we done here?
We’ve introduced a base abstract class - TStoreHeaderAbstract and a factory - TStoreHeaderFactory.
How can we change data format?
In this way:
type TStoreHeaderAbstract = class public procedure Load(aStream: TStream); virtual; abstract; procedure Save(aStream: TStream); virtual; abstract; end;// TStoreHeaderAbstract ... TStoreHeaderRec = record rNextPosition : Int64; rRealSize : Int64; ... end;//TStoreHeaderRec TStoreHeader = class(TStoreHeaderAbstract) private Data : TStoreHeaderRec; public procedure Load(aStream: TStream); override; procedure Save(aStream: TStream); override; end;//TStoreHeader TStoreHeaderRecNew = record rNextPosition : Int64; rRealSize : Int64; rSomeOtherData : SomeOtherType; ... end;//TStoreHeaderRecNew TStoreHeaderNew = class(TStoreHeaderAbstract) private Data : TStoreHeaderRecNew; public procedure Load(aStream: TStream); override; procedure Save(aStream: TStream); override; end;//TStoreHeaderNew TStoreHeaderFactory = class public class function Make(aVersion : TGUID): TStoreHeaderAbstract; end;//TStoreHeaderFactory ... class function TStoreHeaderFactory.Make(aVersion : TGUID): TStoreHeaderAbstract; begin if EqualGUID(aVersion, OldFormatGUID) then Result := TStoreHeader.Create else if EqualGUID(aVersion, NewFormatGUID) then Result := TStoreHeaderNew.Create else Assert(false, 'Incorrect header'); end; procedure TStoreHeader.Load(aStream: TStream); begin aStream.ReadBuffer(Data, SizeOf(Data); end; procedure TStoreHeader.Save(aStream: TStream); begin aStream.WriteBuffer(Data, SizeOf(Data); end; ... procedure TStoreHeaderNew.Load(aStream: TStream); begin aStream.ReadBuffer(Data, SizeOf(Data); end; procedure TStoreHeaderNew.Save(aStream: TStream); begin aStream.WriteBuffer(Data, SizeOf(Data); end; ... procedure SomeReadCode; var l_H : TStoreHeaderAbstract; begin ... l_H := TStoreHeaderFactory.Make(GetVersionGUID); ... l_H.Load(Stream); ... end; ... procedure SomeWriteCode; var l_H : TStoreHeaderAbstract; begin ... l_H := TStoreHeaderFactory.Make(GetVersionGUID); ... l_H.Save(Stream); ... end;
Moreover, we can also write in this way:
... procedure TStoreHeaderNew.Load(aStream: TStream); begin aStream.ReadBuffer(Data.rRealSize, SizeOf(Data.rRealSize); aStream.ReadBuffer(Data.rNextPosition, SizeOf(Data.rNextPosition); aStream.ReadBuffer(Data.rSomeOtherData, SizeOf(Data.rSomeOtherData); end; procedure TStoreHeaderNew.Save(aStream: TStream); begin aStream.WriteBuffer(Data.rRealSize, SizeOf(Data.rRealSize); aStream.WriteBuffer(Data.rNextPosition, SizeOf(Data.rNextPosition); aStream.WriteBuffer(Data.rSomeOtherData, SizeOf(Data.rSomeOtherData); end;
What have we done here?
In the first place, we’ve divided “binary serialization” of a record into a few “binary serializations” of separate fields.
In the second place, we’ve reversed some of the fields in order to demonstrate “the heart of the approach”.
Lots of questions are left beyond the scope.
For example – “where do we get GetVersionGUID?”
Or – “what should we do if writing of version is not supported initially”?
These are important questions. But they “do not fit in the framework” of the post. Generally speaking, these are important, but more “technical questions”. If you are interested I will analyze them in detail.
For now I’ll leave them “beyond the scope”.
What is the result?
In sum, in my opinion, it has been shown that factories are a weighty supplement to encapsulation and polymorphism.
First we’ve used polymorphism - by introducing the type TStoreHeaderAbstract.
Then we’ve used encapsulation – by dividing TStoreHeader.Data and TStoreHeaderNew.Data.
Through polymorphism and encapsulation we’ve, in a way, avoided the “binary serialization”.
Why?
Because the next step could be the following:
... procedure TStoreHeaderNew.Load(aStream: TStream); begin aStream.ReadBuffer(Data.rRealSize, SizeOf(Data.rRealSize); aStream.ReadBuffer(Data.rNextPosition, SizeOf(Data.rNextPosition); Data.rSomeOtherData.Load(aStream); end; procedure TStoreHeaderNew.Save(aStream: TStream); begin aStream.WriteBuffer(Data.rRealSize, SizeOf(Data.rRealSize); aStream.WriteBuffer(Data.rNextPosition, SizeOf(Data.rNextPosition); Data.rSomeOtherData.Save(aStream); end;
- i.e. here we no more write/read “binarily”, but as it is written in SomeOtherDataType.Load/SomeOtherDataType.Save.
So.
What I wanted to show?
I’ll repeat.
I wanted to show that factories are a weighty supplement to encapsulation and polymorphism.
(Let’s say, factories are “twice the polymorphism”. That is because polymorphism “starts to have effect” even before object instance is created. Then, factory polymorphism works. Should I write about polymorphic factories?)
You are to judge how much I succeeded in achieving my goal.
I guess I did not “reinvented the wheel”, but I hope I’ve written something of use.
Комментариев нет:
Отправить комментарий