Original in Russian: http://programmingmindstream.blogspot.ru/2014/09/raii.html
Based on - RAII
I’d like to write about “integral objects”.
I mean, the objects that create other objects internally.
Usually it is done in this way:
But sometimes it can be done like this:
What is “delicious”?
The instance of TSomeClass will not be created until the instances of TSomeClass1 and TSomeClass2 are created.
It means, the destructor will not be called.
Therefore, SomeInitCode won’t be called either.
Hence, there will be no problems with destructing of a partially created object.
There are questions about SomeDoneCode. It will be called (we look into documentation).
But!
If it depends only from the objects initialized above, there will be no problems either.
Besides, it will be called only if the lines:
- are executed.
And the lines:
- do not pass for some reason.
But in this area “the probability tends to zero”.
We can also write in this way:
Or even in this way (for paranoiacs like me):
- probably, it is more clear this way?
Finally, TSomeClass1.Create and TSomeClass2.Create can be created “inductively” if they are also integral.
We should not forget that if constructor throws an exception, the destructor is definitely called (it is also written in documentation), even on a “partially initialized object”.
I’ll repeat once more:
If constructor throws an exception, the destructor is called definitely - even on a “partially initialized object”.
It should be taken into account.
For this reason I had a post - http://programmingmindstream.blogspot.ru/2014/09/blog-post_9.html
What do I lead to?
The “smaller” our objects are, the “smaller” the probability to have destructor call on “partially initialized object” is.
I’ll repeat:
The “smaller” our objects are, the “smaller” the probability to have destructor call on “partially initialized object” is.
It means working less in the constructor instead of factory provides more “peace” for us, as well as decreases the probability of getting AccessViolation or/and unreleased resources.
I’ll be glad if my thoughts will be helpful for anybody.
I’ve used them myself. Several times. But, that is not a measure.
If you’re actually interested, I could try to follow using a real example.
P.S. What we got using this approach is that, on the one hand, we kind of encapsulate “inner logic” in class and, on the other hand, avoid creating a “partially initialized object instance”.
In general, I personally like factories, mixins and Assert’s for a while now. It seems to be serious and long-lasting.
Based on - RAII
I’d like to write about “integral objects”.
I mean, the objects that create other objects internally.
Usually it is done in this way:
type TSomeClass1 = class public constructor Create(aSomeData1 : TSomeType1); end;//TSomeClass1 TSomeClass = class private f_SomeClass1 : TSomeClass1; f_SomeClass2 : TSomeClass2; public constructor Create(aSomeData1 : TSomeType1; aSomeData2: TSomeType2); destructor Destroy; override; end;//TSomeClass ... constructor TSomeClass1.Create(aSomeData1 : TSomeType1); begin Assert(IsValid(aSomeData1)); inherited Create; ... end; ... constructor TSomeClass.Create(aSomeData1 : TSomeType1; aSomeData2: TSomeType2); begin inherited Create; f_SomeClass1 := TSomeClass1.Create(aSomeData1); SomeInitCode; f_SomeClass2 := TSomeClass2.Create(aSomeData2); end; destructor TSomeClass.Destroy; begin FreeAndNil(f_SomeClass2); SomeDoneCode; FreeAndNil(f_SomeClass1); inherited; end;
But sometimes it can be done like this:
type TSomeClass = class private f_SomeClass1 : TSomeClass1; f_SomeClass2 : TSomeClass2; protected constructor Make(aSomeClass1 : TSomeClass1; aSomeClass2: TSomeClass2); public class function Create(aSomeData1 : TSomeType1; aSomeData2: TSomeType2): TSomeClass; destructor Destroy; override; end;//TSomeClass constructor TSomeClass.Make(aSomeClass1 : TSomeClass1; aSomeClass2: TSomeClass2); begin inherited Create; f_SomeClass1 := aSomeClass1; SomeInitCode; f_SomeClass2 := aSomeClass2; end; class function TSomeClass.Create(aSomeData1 : TSomeType1; aSomeData2: TSomeType2): TSomeClass; var l_SomeClass1: TSomeClass1; l_SomeClass2: TSomeClass2; begin Assert(IsValid(aSomeData1)); Assert(IsValid(aSomeData2)); l_SomeClass1 := TSomeClass1.Create(aSomeData1); l_SomeClass2 := TSomeClass2.Create(aSomeData2); Result := Make(l_SomeClass1, l_SomeClass2); end; destructor TSomeClass.Destroy; begin FreeAndNil(f_SomeClass2); SomeDoneCode; FreeAndNil(f_SomeClass1); inherited; end;
What is “delicious”?
The instance of TSomeClass will not be created until the instances of TSomeClass1 and TSomeClass2 are created.
It means, the destructor will not be called.
Therefore, SomeInitCode won’t be called either.
Hence, there will be no problems with destructing of a partially created object.
There are questions about SomeDoneCode. It will be called (we look into documentation).
But!
If it depends only from the objects initialized above, there will be no problems either.
Besides, it will be called only if the lines:
... l_SomeClass1 := TSomeClass1.Create(aSomeData1); l_SomeClass2 := TSomeClass2.Create(aSomeData2); ...
- are executed.
And the lines:
... inherited Create; f_SomeClass1 := aSomeClass1; ...
- do not pass for some reason.
But in this area “the probability tends to zero”.
We can also write in this way:
type TSomeClass = class private f_SomeClass1 : TSomeClass1; f_SomeClass2 : TSomeClass2; protected constructor Make(aSomeClass1 : TSomeClass1; aSomeClass2: TSomeClass2); public class function Create(aSomeData1 : TSomeType1; aSomeData2: TSomeType2): TSomeClass; destructor Destroy; override; end;//TSomeClass constructor TSomeClass.Make(aSomeClass1 : TSomeClass1; aSomeClass2: TSomeClass2); begin inherited Create; f_SomeClass1 := aSomeClass1; SomeInitCode; f_SomeClass2 := aSomeClass2; end; class function TSomeClass.Create(aSomeData1 : TSomeType1; aSomeData2: TSomeType2): TSomeClass; var l_SomeClass1: TSomeClass1; l_SomeClass2: TSomeClass2; begin l_SomeClass1 := TSomeClass1.Create(aSomeData1); l_SomeClass2 := TSomeClass2.Create(aSomeData2); Result := Make(l_SomeClass1, l_SomeClass2); end; destructor TSomeClass.Destroy; begin FreeAndNil(f_SomeClass2); if (f_SomeClass1 <> nil) then // - we check if f_SomeClass1 is initialized // Why do we need to check? The answer is – “otherwise, why is this SomeDoneCode exactly HERE? SomeDoneCode; FreeAndNil(f_SomeClass1); inherited; end;
Or even in this way (for paranoiacs like me):
type TSomeClass = class private f_SomeClass1 : TSomeClass1; f_SomeClass2 : TSomeClass2; protected constructor Make(aSomeClass1 : TSomeClass1; aSomeClass2: TSomeClass2); public class function Create(aSomeData1 : TSomeType1; aSomeData2: TSomeType2): TSomeClass; destructor Destroy; override; end;//TSomeClass constructor TSomeClass.Make(aSomeClass1 : TSomeClass1; aSomeClass2: TSomeClass2); begin inherited Create; f_SomeClass1 := aSomeClass1; SomeInitCode(f_SomeClass1); f_SomeClass2 := aSomeClass2; end; class function TSomeClass.Create(aSomeData1 : TSomeType1; aSomeData2: TSomeType2): TSomeClass; var l_SomeClass1: TSomeClass1; l_SomeClass2: TSomeClass2; begin l_SomeClass1 := TSomeClass1.Create(aSomeData1); l_SomeClass2 := TSomeClass2.Create(aSomeData2); Result := Make(l_SomeClass1, l_SomeClass2); end; destructor TSomeClass.Destroy; begin FreeAndNil(f_SomeClass2); if (f_SomeClass1 <> nil) then // - we check if f_SomeClass1 is initialized // Why do we need to check? The answer is – “otherwise, why is this SomeDoneCode exactly HERE? SomeDoneCode(f_SomeClass1); FreeAndNil(f_SomeClass1); inherited; end;
- probably, it is more clear this way?
Finally, TSomeClass1.Create and TSomeClass2.Create can be created “inductively” if they are also integral.
We should not forget that if constructor throws an exception, the destructor is definitely called (it is also written in documentation), even on a “partially initialized object”.
I’ll repeat once more:
If constructor throws an exception, the destructor is called definitely - even on a “partially initialized object”.
It should be taken into account.
For this reason I had a post - http://programmingmindstream.blogspot.ru/2014/09/blog-post_9.html
What do I lead to?
The “smaller” our objects are, the “smaller” the probability to have destructor call on “partially initialized object” is.
I’ll repeat:
The “smaller” our objects are, the “smaller” the probability to have destructor call on “partially initialized object” is.
It means working less in the constructor instead of factory provides more “peace” for us, as well as decreases the probability of getting AccessViolation or/and unreleased resources.
I’ll be glad if my thoughts will be helpful for anybody.
I’ve used them myself. Several times. But, that is not a measure.
If you’re actually interested, I could try to follow using a real example.
P.S. What we got using this approach is that, on the one hand, we kind of encapsulate “inner logic” in class and, on the other hand, avoid creating a “partially initialized object instance”.
In general, I personally like factories, mixins and Assert’s for a while now. It seems to be serious and long-lasting.
Комментариев нет:
Отправить комментарий