| View previous topic :: View next topic |
| Author |
Message |
Chris Trueman Guest
|
Posted: Tue Jan 04, 2005 9:54 pm Post subject: Component Design |
|
|
I've built a component that essentially manages a list of objects. In this
case I chose to return a new object each time an item in the list was
retrieved as in:
property Item[ix: Integer]: TService read GetItem;
function TServiceList.GetItem(ix: Integer): TService;
var
s: PEnumServiceStatus;
begin
if (ix < 0) or (ix >= FCount) then
Result := nil
else
begin
{ FItems is just a block of memory containing records }
s := FItems;
while ix > 0 do
begin
Inc(s);
Dec(ix);
end;
{ Calls a private constructor that picks apart the record }
Result := TService.Create(s);
end;
end;
This feels like a design that runs contrary to most list management
components I've seen. Thoughts?
Chris.
|
|
| Back to top |
|
 |
Peter Morris [Air Softwar Guest
|
Posted: Tue Jan 04, 2005 10:12 pm Post subject: Re: Component Design |
|
|
| Quote: | This feels like a design that runs contrary to most list management
components I've seen. Thoughts?
|
If you were lazy-creating the item and keeping the reference in a list to
pass back the next time it was requested then I wouldn't have an objection,
but that is not what you are doing.
One of my golden-never-break rules is to *never* return a new object from a
function, if you do you are at high risk of introducing memory leaks, for
example.
if list.item[0] <> nil then list.item[0].Caption := 'Hello';
You have just created two different objects, neither of which have been
freed.
--
Pete
====
Audio compression components, DIB graphics controls, FastStrings
http://www.droopyeyes.com
Read or write articles on just about anything
http://www.HowToDoThings.com
My blog
http://blogs.slcdug.org/petermorris/
|
|
| Back to top |
|
 |
Chris Trueman Guest
|
Posted: Tue Jan 04, 2005 10:21 pm Post subject: Re: Component Design |
|
|
| Quote: | if list.item[0] <> nil then list.item[0].Caption := 'Hello';
|
This is a good observation. Since the TServicesManager.Services also
returns a new object each time - code like:
for iii := 0 to svrMgr.Services.Count - 1 do
if svrMgr.Services[iii].ServiceName = 'blah blah'
presumbely creates the list object Count + 1 times.
Thanks for the comments.
Chris.
|
|
| Back to top |
|
 |
Bryan Crotaz Guest
|
Posted: Wed Jan 05, 2005 12:47 pm Post subject: Re: Component Design |
|
|
| Quote: | One of my golden-never-break rules is to *never* return a new object from
a
function, if you do you are at high risk of introducing memory leaks, for
example.
|
Actually it's fine, as long as your method is named well
Eg CreateItem: TItem or NewItem: TItem.
Bryan
|
|
| Back to top |
|
 |
Jim Cooper Guest
|
Posted: Wed Jan 05, 2005 2:56 pm Post subject: Re: Component Design |
|
|
| Quote: | One of my golden-never-break rules is to *never* return a new object from a
function
|
How do you do factory patterns then? :-)
Cheers,
Jim Cooper
_______________________________________________
Jim Cooper [email]jim (AT) falafelsoft (DOT) com[/email]
Falafel Software http://www.falafelsoft.com
_______________________________________________
|
|
| Back to top |
|
 |
Bob Dawson Guest
|
Posted: Wed Jan 05, 2005 3:07 pm Post subject: Re: Component Design |
|
|
"Jim Cooper" wrote
| Quote: |
How do you do factory patterns then?
|
I'm also a bit more relaxed about this, and try to mitigate things via
coding conventions: factory classes are always called T<~>Factory, and other
classes the have object-returning functions tend to name those functions
Create<~> or Provide<~> rather than just Get<~>. This tells the programmer
that the function assumes no responsibily for the provided object, whereas
the result of an Item[x] or GetItem(x) call can safely be walked away from.
bobD
|
|
| Back to top |
|
 |
Derek Davidson Guest
|
Posted: Wed Jan 05, 2005 3:27 pm Post subject: Re: Component Design |
|
|
Peter Morris [Air Software Ltd] wrote:
| Quote: | One of my golden-never-break rules is to never return a new object
from a function
|
Whoa! That's a severe, self-imposed limitation there.
If everyone followed the rule, here are a few places where significant
re-writes would have to occur:
1. The VCL
2. The FCL
3. Refactoring - Improving the Design of Existing Code
4. Design Patterns
I think, on balance, I'll keep the option open <g>
--
Derek Davidson
http://www.ebsms.com
Send SMS Text messages from your PC. For FREE!
|
|
| Back to top |
|
 |
Charles McAllister Guest
|
Posted: Wed Jan 05, 2005 3:34 pm Post subject: Re: Component Design |
|
|
Peter Morris [Air Software Ltd] wrote:
| Quote: | One of my golden-never-break rules is to *never* return a new object from a
function, if you do you are at high risk of introducing memory leaks, for
example.
if list.item[0] <> nil then list.item[0].Caption := 'Hello';
You have just created two different objects, neither of which have been
freed.
How about just following a naming convention, e.g. CreateItem? or DoCreateItem (looks less like a |
constructor call).
if i saw...
if list.CreateItem[0] <> nil then
list.CreateItem[0].Caption := 'Hello';
....be easier to see the red flag
i also follow a naming convention of TryCreateXXX for cases where a function might return nil if
object could not be created.
|
|
| Back to top |
|
 |
Peter Morris [Air Softwar Guest
|
Posted: Wed Jan 05, 2005 5:37 pm Post subject: Re: Component Design |
|
|
| Quote: | Actually it's fine, as long as your method is named well
Eg CreateItem: TItem or NewItem: TItem.
|
I'd prefer
procedure TMyClass.CreateItem(var NewItem: TItem);
That way it is 100% impossible to cause this problem.
|
|
| Back to top |
|
 |
Peter Morris [Air Softwar Guest
|
Posted: Wed Jan 05, 2005 5:44 pm Post subject: Re: Component Design |
|
|
| Quote: | How do you do factory patterns then?
|
I always create a procedure with a "var" parameter, like so
............CreateWhatever(var Result: TWhatever)
This way it is enforced that there must be a variable to hold the instance
reference. The only time I would have a function returning a new object is
when there is a parent object which holds a reference to it automatically,
such as
Person.Children.AddNew
--
Pete
====
Audio compression components, DIB graphics controls, FastStrings
http://www.droopyeyes.com
Read or write articles on just about anything
http://www.HowToDoThings.com
My blog
http://blogs.slcdug.org/petermorris/
|
|
| Back to top |
|
 |
Joanna Carter (TeamB) Guest
|
Posted: Wed Jan 05, 2005 5:54 pm Post subject: Re: Component Design |
|
|
"Peter Morris [Air Software Ltd]"
<peter.morris (AT) remove (DOT) this.airsoftware.co.uk> a écrit dans le message de
news: 41dc2786$1 (AT) newsgroups (DOT) borland.com...
| Quote: | I always create a procedure with a "var" parameter, like so
...........CreateWhatever(var Result: TWhatever)
This way it is enforced that there must be a variable to hold the instance
reference. The only time I would have a function returning a new object
is
when there is a parent object which holds a reference to it automatically,
such as
|
In that case, you might like to consider using the 'out' modifier as this
guarantees that you get nil if it fails, rather than accidentally passing in
a valid object and getting the same object back instead of a replacement ??
Joanna
--
Joanna Carter (TeamB)
Consultant Software Engineer
TeamBUG support for UK-BUG
TeamMM support for ModelMaker
|
|
| Back to top |
|
 |
Bryan Crotaz Guest
|
Posted: Wed Jan 05, 2005 5:55 pm Post subject: Re: Component Design |
|
|
"Peter Morris [Air Software Ltd]"
<peter.morris (AT) remove (DOT) this.airsoftware.co.uk> wrote
| Quote: | Actually it's fine, as long as your method is named well
Eg CreateItem: TItem or NewItem: TItem.
I'd prefer
procedure TMyClass.CreateItem(var NewItem: TItem);
That way it is 100% impossible to cause this problem.
|
That's horrible to use though.
I do a lot of this:
ProcessHandler.Create(MyObject.CreateClone);
or MyProcess.DataObject := MyObject.CreateClone;
The Process owns the object, so all is hunkydory.
Bryan
|
|
| Back to top |
|
 |
Derek Davidson Guest
|
Posted: Wed Jan 05, 2005 6:13 pm Post subject: Re: Component Design |
|
|
Chris Trueman wrote:
| Quote: | In this
case I chose to return a new object each time an item in the list was
retrieved as in:
|
In the first case, I'm assuming that you're using Delphi (Win32) as
this is a 'freeing the memory' issue and with .NET you get GC (and
hence no issue)
If you have control over the objects that you return how about
returning an object that implements interfaces and thus get the objects
reference counted and freed automatically?
--
Derek Davidson
http://www.ebsms.com
Send SMS Text messages from your PC. For FREE!
|
|
| Back to top |
|
 |
Bob Dawson Guest
|
Posted: Wed Jan 05, 2005 6:17 pm Post subject: Re: Component Design |
|
|
"Bryan Crotaz" wrote
| Quote: |
ProcessHandler.Create(MyObject.CreateClone);
|
hmmm--rather the opposite of Peter's original case (you're passing an
unreferenced new object to a function rather than out of one). If
ProcessHandler.Create fails, what happens to MyObject?
Rather than
IntendedOwner.AddItem(TSomeObject.Create);
-- what happens if the AddItem fails?
I tend to do things like
var
sobj : TSomeObject;
begin
sobj := TSomeObject.Create;
try
IntendedOwner.AddItem(sobj);
except
sobj.Free;
raise;
end;
bobD
|
|
| Back to top |
|
 |
Jim Cooper Guest
|
Posted: Wed Jan 05, 2005 6:32 pm Post subject: Re: Component Design |
|
|
| Quote: | I always create a procedure with a "var" parameter, like so
|
I find that a bit of an issue with factories, since you cannot have
allocated Result yet. Dealing with failure gets a bit ugly. (I'm also
not keen on Result as a parameter name , but that's a spearate problem)
Cheers,
Jim Cooper
_______________________________________________
Jim Cooper [email]jim (AT) falafelsoft (DOT) com[/email]
Falafel Software http://www.falafelsoft.com
_______________________________________________
|
|
| Back to top |
|
 |
|