BorlandTalk.com Forum Index BorlandTalk.com
Borland discussion newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Re: Is this class methods usage violating OOP principles?
Goto page 1, 2, 3, 4  Next
 
Post new topic   Reply to topic    BorlandTalk.com Forum Index -> Delphi OO design
View previous topic :: View next topic  
Author Message
Jens Gruschel
Guest





PostPosted: Tue Sep 20, 2005 12:55 am    Post subject: Re: Is this class methods usage violating OOP principles? Reply with quote



Quote:
I wrote this code to change my app's cursor to crHourGlass in lengthy
operations:

[...]

Quote:
It works, but I wonder if I'm not violating OOP principles by using a
global variable (Delphi doesn't allow class methods access to class
members).
My code seems a bit procedural...

That's what I think, too. And it also has a big flaw:

THourglass.Yes;
try
// Lengthy operations
THourglass.Yes;
try
// More lengthy operations
finally
THourglass.No;
end;
finally
THourglass.No;
end;

Since there is only one global variable the second Yes call would result
of a loss of the original value, so the second No call would restore the
wrong value. You could solve this by introducing a counter for the level
of depth.

But why don't you store the SavedCursor as a field and use normal
methods (well, or simply constructor and destructor)? What's wrong with
following code?

Hourglass := THourglass.Create;
try
// Lengthy operations
finally
Hourglass.Free;
end;

It's not too much more code. And reintrant. And less procedural.

Jens

--
Jens Gruschel
http://www.pegtop.net

Back to top
Jens Gruschel
Guest





PostPosted: Tue Sep 20, 2005 1:08 am    Post subject: Re: Is this class methods usage violating OOP principles? Reply with quote



Quote:
But why don't you store the SavedCursor as a field and use normal
methods (well, or simply constructor and destructor)? What's wrong with
following code?

Hourglass := THourglass.Create;
try
// Lengthy operations
finally
Hourglass.Free;
end;

It's not too much more code. And reintrant. And less procedural.

P.S. While I think that's better OOP than your original version, some
would call it a poltergeist class. Not that I don't like the idea of
this little class, but I've always used a local variable to be able to
restore the cursor and access Screen.Cursor directly. It's OOP, it's
nice, it's probably more platform independent, if you want to look at it
this way. But is it really neccessary? I don't know the answer, but I
think it's not the best example how useful OOP can be. Ok, there is at
least one advantage: you can change the behaviour globally later (maybe
replace the default hourglass cursor by your own one).

Jens

--
Jens Gruschel
http://www.pegtop.net

Back to top
Jarle Stabell
Guest





PostPosted: Tue Sep 20, 2005 1:09 am    Post subject: Re: Is this class methods usage violating OOP principles? Reply with quote



Jens Gruschel wrote:
Quote:

Hourglass := THourglass.Create;
try
// Lengthy operations
finally
Hourglass.Free;
end;

It's not too much more code. And reintrant. And less procedural.

You can get it down to a single line of code by using interfaces, because
then you do not need the explicit free nor the explicity try-finally block,
the compiler writes those lines for you with interfaces.

Cheers,
Jarle



Back to top
Jens Gruschel
Guest





PostPosted: Tue Sep 20, 2005 1:27 am    Post subject: Re: Is this class methods usage violating OOP principles? Reply with quote

Quote:
You can get it down to a single line of code by using interfaces, because
then you do not need the explicit free nor the explicity try-finally block,
the compiler writes those lines for you with interfaces.

Right, but you cannot specify any more when exactly to restore the
cursor, the interface variable has to run out of scope. I agree, that's
probably irrelevant, because either it runs out of scope very quickly,
or another lengthy operation does follow, where another hourglass is
needed. To summarize: your solution is better than mine ;-)

Jens

--
Jens Gruschel
http://www.pegtop.net

Back to top
Jens Gruschel
Guest





PostPosted: Tue Sep 20, 2005 2:17 am    Post subject: Re: Is this class methods usage violating OOP principles? Reply with quote

Quote:
I know it will fail in that situation, never intended to nest calls to
Yes!

Nothing wrong, just wanted to avoid declaring object instances,
specially when one intends to use this code everywhere.

If it really is used everywhere, you can easily be faced with nested
calls. Of course you probably won't write code the way I wrote my little
example, but maybe call some procedure, which calls another procedure,
which calls Yes again. Have you ever seen applications, which generate a
hourglass, that doesn't change back to normal again? I have. And it
might be because of such things (although it might also be because of a
missing try / finally block).


Quote:
I wanted something easier, just turn hourglass on and turn it off when
finished.

IMO it's easy enough.

Quote:
Perhaps I will end up coding

type TProc = procedure of object;

procedure DoLengthyProc(Proc: TProc):
var
Hourglass: THourglass;
begin
Hourglass := THourglass.Create;
try
Proc;
finally
Hourglass.Free;
end;
end;

Not too bad. But OOP offers more elegant solutions. You could simply
replace your TProc with a class or interface type, which declares a
(virtual) Proc method. And of course there is the template method design
pattern, which allows to write your DoLengthyProc method once, and
override your Proc method. Other related patterns are the strategy
design pattern and maybe the command pattern.

Jens

--
Jens Gruschel
http://www.pegtop.net

Back to top
krisztian pinter
Guest





PostPosted: Tue Sep 20, 2005 7:10 am    Post subject: Re: Is this class methods usage violating OOP principles? Reply with quote

On Tue, 20 Sep 2005 03:27:41 +0200, Jens Gruschel
<nospam (AT) thisurldoesnotexist (DOT) com> wrote:


Quote:
Right, but you cannot specify any more when exactly to restore the
cursor, the interface variable has to run out of scope.

I thing it is much worse than that. Compiler is free to optimize your
variables lifetime. As you wont use the interface at all, compiler
might release the interface right after creation. Moral of the story:
if you expect the compiler to do the freeing for you, you can't count
on how or when will this happen.

Back to top
krisztian pinter
Guest





PostPosted: Tue Sep 20, 2005 7:29 am    Post subject: Re: Is this class methods usage violating OOP principles? Reply with quote

On 19 Sep 2005 18:35:52 -0700, José González D'Amico
<jgonzalezdamico (AT) geemail (DOT) com> wrote:

Quote:
type TProc = procedure of object;

procedure DoLengthyProc(Proc: TProc):
var
Hourglass: THourglass;
begin
Hourglass := THourglass.Create;
try
Proc;
finally
Hourglass.Free;
end;
end;


This approach is valid, although here is a major drawback: you lose
context.

the calling context is problematic to pass to Proc. you cannot use the
caller functions local vars, other class methods, properties etc. you need
to somehow pack all the context data to some data package, and pass it as a
parameter or Proc. or create a context object, fill it in, and use it's
method
as Proc.

the first would look something like

type TProc = procedure (Param: pointer) of object;

procedure DoLengthyProc(Proc: TProc; Param: pointer):

....
Proc(Param);
....

and your calling side is, assuming a file name is the parameter:

type
TContext = class
public
FileName: string;
end;

....
Ctx := TContext.Create;
Ctx.FileName := sFileName;
DoLengthyProc(ProcessFile, Ctx);
Ctx.Free;
....

procedure TForm1.ProcessFile(Param: pointer);
begin
sFileName := TContext(Param).FileName;
end;


or the other approach is the "process and context" class

type
TFileProcessor = class
public
FileName: string;
procedure Process;
end;

and the caller side

....
FileProc := TFileProcessor.Create;
FileProc.FileName := sFileName;
DoLengthyProc(FileProc.Process);
FileProc.Free;
....

both ways, you need to do extra work to collect the required context,
and pass it to the executor side.

if you go by the parameter way, you will declare TProc to accept some
general
parameter like pointer or TObject, and only in implementation, you will
know
what it really is. you will need a typecast, and it is always dangerous.
Back to top
krisztian pinter
Guest





PostPosted: Tue Sep 20, 2005 7:42 am    Post subject: Re: Is this class methods usage violating OOP principles? Reply with quote

On Tue, 20 Sep 2005 02:55:39 +0200, Jens Gruschel
<nospam (AT) thisurldoesnotexist (DOT) com> wrote:

Quote:
Hourglass := THourglass.Create;
try
// Lengthy operations
finally
Hourglass.Free;
end;

I think it is even better as you think more about it. We can go one step
forward, and do an abstraction: we actually don't want to cursor to become
an hourglass, but instead, we want to notify the user that something
is happening. What about:


type
TProgressOption = (proDisableInterface, proChangeAppCaption,
proShowWindow, proAbortable, ...)
TProgressOptions = set of TProgressOption;

TProgress = class
...
public
property Options: TProgressOptions read ...
property TaskDescription: string ...

constructor Create(AOptions: TProgressOptions);
end;


Progress := TProgress.Create([proDisableInterface, proChangeAppCaption]);
try
Progress.TaskDescription := 'opening file';
// do the work
Progress.TaskDescription := 'reading content';
// do more work
Progress.TaskDescription := 'processing';
...

finally
Progress.Free;
end;

Back to top
Maxim Shiryaev
Guest





PostPosted: Tue Sep 20, 2005 7:43 am    Post subject: Re: Is this class methods usage violating OOP principles? Reply with quote

Why not Singleton pattern?

It's considered to be truely OOP - without global variables, etc.
And a comment about nested calls is actually important.
In my system I use a bit more complicated class which not only change a
cursor but also shows a top-most window with a message. It allows to supply
a string parameter to Yes() method and so I use a stack of these strings.
When Yes() is called the string is pushed to stack and when No() it's poped
back. So a user can see what process is going on even if they are nested.

Maxim Shiryaev.

"José González D'Amico" <jgonzalezdamico (AT) geemail (DOT) com> wrote

Quote:
Hi OO-people,

I wrote this code to change my app's cursor to crHourGlass in lengthy
operations:

type
THourglass = class
public
class procedure Yes;
class procedure No;
end;

// Implementation
var
SavedCursor : TCursor;

{ THourglass }

class procedure THourglass.Yes;
begin
SavedCursor := Screen.Cursor;
Screen.Cursor := crHourGlass;
end;

class procedure THourglass.No;
begin
Screen.Cursor := SavedCursor;
end;

// Usage
THourglass.Yes;
try
// Lengthy operations
finally
THourglass.No;
end;

It works, but I wonder if I'm not violating OOP principles by using a
global variable (Delphi doesn't allow class methods access to class
members).
My code seems a bit procedural...

--
José



Back to top
Jens Gruschel
Guest





PostPosted: Tue Sep 20, 2005 11:59 am    Post subject: Re: Is this class methods usage violating OOP principles? Reply with quote

Quote:
I think it is even better as you think more about it. We can go one step
forward, and do an abstraction: we actually don't want to cursor to become
an hourglass, but instead, we want to notify the user that something
is happening. What about:

[...]

Quote:
Progress := TProgress.Create([proDisableInterface, proChangeAppCaption]);
try
Progress.TaskDescription := 'opening file';
// do the work
Progress.TaskDescription := 'reading content';
// do more work
Progress.TaskDescription := 'processing';
...

finally
Progress.Free;
end;

I like it. You can even extend it by some progress bar, if you introduce
another property for TProgress. The only thing missing is some relation
between the progress class and the GUI. THourglass was using the Screen
object directly. Of course TProgress can do something similar. But maybe
the progress object can also be created with a factory method of the
GUI. Or the progress object might be parameterized with some GUI
interaction object. A singleten class might be a more simple solution,
but IMO it's too close to using Screen directly, even if you can
configure it better. I have to think about it again. Looks like it might
be useful (maybe with some modifications) for my applications as well...

Jens

--
Jens Gruschel
http://www.pegtop.net

Back to top
krisztian pinter
Guest





PostPosted: Tue Sep 20, 2005 12:17 pm    Post subject: Re: Is this class methods usage violating OOP principles? Reply with quote

On Tue, 20 Sep 2005 13:59:57 +0200, Jens Gruschel
<nospam (AT) thisurldoesnotexist (DOT) com> wrote:


Quote:
Progress := TProgress.Create([proDisableInterface,
proChangeAppCaption]);

I like it. You can even extend it by some progress bar, if you introduce
another property for TProgress. The only thing missing is some relation
between the progress class and the GUI. THourglass was using the Screen
object directly. Of course TProgress can do something similar. But maybe
the progress object can also be created with a factory method of the
GUI. Or the progress object might be parameterized with some GUI
interaction object. A singleten class might be a more simple solution,
but IMO it's too close to using Screen directly, even if you can
configure it better. I have to think about it again. Looks like it might
be useful (maybe with some modifications) for my applications as well....

In my real implementation, TProgress was indeed a TProgressSession, and it
represented a single task. TProgressSession instances used a hidden
singleton
that actually managed things. It had a list of all existing sessions, and
their nesting. Sessions had properties MainTask, SubTask, plus any number
of
progress counters, each with a name, a maximum and a current value. Alsoit
had ProcMsgs that was a way to relinquish control to user without actually
using Application object. Plus it had an Aborted boolean flag. If the
session was parametrized to show a progress window, and include an Abort
button, Aborted was set to true if pressed. Or, optionally, Abort button
raised an EAbort exception.

The only drawback of my design was that it was tightly bound to VCL. The
background singleton used Application, used forms and all. One more level
of abstraction is necessary, but i decided not to spend more time on that
one, as the current implementation covers our needs well.

Back to top
Bob Dawson
Guest





PostPosted: Tue Sep 20, 2005 1:54 pm    Post subject: Re: Is this class methods usage violating OOP principles? Reply with quote

"José González D'Amico" wrote

Quote:
It works, but I wonder if I'm not violating OOP principles by
using a global variable

Since SavedCursor is in the implementation secton and only accessible to the
THourglass object, doesn't bother me. Like you said, we don't have class
vars yet.

Quote:
My code seems a bit procedural...

Again, doesn't bother me. I don't like the Yes/No methods, though--seems
obsure to me. I'd probably prefer On/Off, or Active/Inactive--something like
that.

General design. I don't like the whole THourglass concept--what about
sqlWait, etc? Seems silly to make these all separate classes. Let's make a
class that handles any TCursor, and while we're at it handle nesting, and
then hide the oo details behind a singleton class access.

Think I'd approach the problem this way:

Create a class that would act as a stack for cursor state.

Create a class to manipulate the cursor, incorporating the stack internally.
Methods would look something like this:

//protected
procedure TScreenCursorController.InternalSetCursor(const aCursor :
TCursor);
begin
if aCursor <> Screen.Cursor then
Screen.Cursor := aCursor;
end;

//public
procedure TScreenCursorController.SetCursor(const aCursor : TCursor);
begin
FCursorStack.Push(aCursor);
InternalSetCursor(aCursor);
end;

procedure TScreenCursorController.Reset;
begin
InternalSetCursor(FCursorStack.Pop);
end;

Then finally create a singleton accessor for the TScreenCursorController.
This would reduce calls to class methods:

TScreenCursor = class(TObject)
public
class procedure SetCursor(const aCursor : TCursor);
class procedure Reset;
end;

implemented something like

implementation

var
uFController : TScreenCursorController;

class procedure TScreenCursor.SetCursor(const aCursor : TCursor);
begin
uFController.SetCursor;
end;

class procedure TScreenCursor.Reset;
begin
uFController.Reset;
end;

initialization
uFController := TScreenCursorController.Create;

finalization
uFController.Free;

end.




Back to top
Jens Gruschel
Guest





PostPosted: Tue Sep 20, 2005 2:21 pm    Post subject: Re: Is this class methods usage violating OOP principles? Reply with quote

Quote:
After reading all the answers, I realize that much of my code is more
procedural than I guessed

That damn RAD thing with which one starts learning Delphi!

Oh, not only beginners fall into this trap. Especially when I'm in a
hurry, such things happen to me, too.

Quote:
Thank you to all who responded with these useful tips

I think Krisztian's solution is quite good. Or use a abstract base class
and the template method design pattern. You can also combine both (find
better names)...

type
TWork = class
protected
procedure DoExecute(Progress: TProgress); virtual; abstract;
public
procedure Execute;
end;

procedure TWork.Execute;
var
Progress: TProgress;
begin
Progress := TProgress.Create(...);
try
DoExecute(Progress);
finally
Progress.Free;
end;
end;

But IMO that's only an advantage if you have to execute some similar
tasks and maybe want to enhance your logic later (maybe add some
scheduling list with different work items).

Jens

--
Jens Gruschel
http://www.pegtop.net

Back to top
Bob Dawson
Guest





PostPosted: Tue Sep 20, 2005 2:56 pm    Post subject: Re: Is this class methods usage violating OOP principles? Reply with quote

"Bob Dawson" wrote

oops--on review, procedure

Quote:
//public
procedure TScreenCursorController.SetCursor(const aCursor : TCursor);
begin
FCursorStack.Push(aCursor);
InternalSetCursor(aCursor);
end;

Should obviously have been

procedure TScreenCursorController.SetCursor(const aCursor : TCursor);
begin
FCursorStack.Push(Screen.Cursor);
InternalSetCursor(aCursor);
end;

bobD



Back to top
Scott Roberts
Guest





PostPosted: Tue Sep 20, 2005 5:52 pm    Post subject: Re: Is this class methods usage violating OOP principles? Reply with quote


"Jens Gruschel" <nospam (AT) thisurldoesnotexist (DOT) com> wrote


Quote:
Progress := TProgress.Create([proDisableInterface,
proChangeAppCaption]);
try
Progress.TaskDescription := 'opening file';
// do the work
Progress.TaskDescription := 'reading content';
// do more work
Progress.TaskDescription := 'processing';
...

finally
Progress.Free;
end;

I like it. You can even extend it by some progress bar, if you introduce
another property for TProgress. The only thing missing is some relation
between the progress class and the GUI.

The progress class could publish events which will be handled by the GUI.
Actually, we skipped the TProgress class and just had the "lengthy
opertaion" classes publish "Progress" events which can be handled by any
object(s). The event args have a flag indicating whether the process has
"finished" or not (i.e. there is always a single event at the end of the
process that is a "finish" event).

The only "gotcha" is that you have/need to handle any exceptions thrown by
those external event handlers. It's no good letting some poorly written GUI
element bomb your process.



Back to top
Display posts from previous:   
Post new topic   Reply to topic    BorlandTalk.com Forum Index -> Delphi OO design All times are GMT
Goto page 1, 2, 3, 4  Next
Page 1 of 4

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.