Pass object or Raise Event?
Author |
Message |
MP #1 / 25
|
 Pass object or Raise Event?
Hi all, assume some classes working together to some end (which, composed of different tasks, takes some time) main creates controller class and asks for service controller class creates worker class worker class creates and uses some helper classes objective, show progress to user as visual cue of activity one possible implementation : a progress bar on a form each task would set the progbar.max and increment based on workload and eg loop progress since there are multiple classes working at different times and one "progressDisplayingObject" my question is: Is it better to ... A. pass the "progressDisplayingObject" around to each class that needs to show progress Private moProgObj as IProgress 'interface Set moProgObj = New ConcreteProgress 'Implementation - or - B. Each class raises events relating to it's own workload and progress and the Controller (top level class) receives the events and talks to the "progressDisplayingObject" itself - or - C. ? for example A 'in main Set moController = CreateController Set moProgObj = CreateProgressObject Set moController.Progress = moProgObj 'in controller Set moWorker = CreateWorker Set moWorker.Progress = Me.Progress 'in worker worker class creates and uses some helper classes Set moDocumentInspector = CreateDocumentInspector Set moDocumentInspector.Progress = Me.Progress Set moDatabaseController = CreateDatabaseController Set moDatabaseController.Progress = Me.Progress For example Worker class has a list of documents to perform a task with (and display progress while doing so) With Worker.Progress .Max = totalNumDocumentsToProcess' load progress bar max for overall project .Notice = "Inspecting documents" 'fill out caption or label or whatever For Each Document in Worker.Documents .Increment 'increment progress bar DocumentInspector.InspectDocument(Document)...and display progress during inspection With DocumentInspector.Progress .Max = Document.ObjectCount 'load progress bar intermediate process For Each object in DocumentInspector.DocumentObjectCollection .Increment 'display progress intermediate level .Notice = "Inspecting " & Document.Name ... Next object End with With DatabaseControllerClass.Progress .Max = NumRecordsToReadOrWrite For Each Record .Increment ... Next Record End With Next Document end with Option B Each "worker / helper Class" raises events like _ProgressMax and _ProgressIncrement in locations above where they are calling the respective object properties Upper level class receives events and calls it's own ProgressObject's properties etc.... does any of that make any sense? Thanks for any suggextions <g> (like ... can't you make your posts more succinct? <vbg> ..sorry :-| ) mark
|
Wed, 15 Sep 2010 11:54:10 GMT |
|
 |
Kevin Provanc #2 / 25
|
 Pass object or Raise Event?
Me personally...I'd use the Event - Kev
| Hi all, | assume some classes working together to some end (which, composed of | different tasks, takes some time) | main creates controller class and asks for service | controller class creates worker class | worker class creates and uses some helper classes | | objective, show progress to user as visual cue of activity | | one possible implementation : a progress bar on a form | each task would set the progbar.max and increment based on workload and | eg loop progress | since there are multiple classes working at different times and one | "progressDisplayingObject" my question is: | | Is it better to ... | A. pass the "progressDisplayingObject" around to each class that needs | to show progress | Private moProgObj as IProgress 'interface | Set moProgObj = New ConcreteProgress 'Implementation | - or - | B. Each class raises events relating to it's own workload and progress | and the Controller (top level class) receives the events and talks | to the "progressDisplayingObject" itself | - or - | C. ? | | for example A | 'in main | Set moController = CreateController | Set moProgObj = CreateProgressObject | Set moController.Progress = moProgObj | 'in controller | Set moWorker = CreateWorker | Set moWorker.Progress = Me.Progress | 'in worker | worker class creates and uses some helper classes | Set moDocumentInspector = CreateDocumentInspector | Set moDocumentInspector.Progress = Me.Progress | | Set moDatabaseController = CreateDatabaseController | Set moDatabaseController.Progress = Me.Progress | | For example Worker class has a list of documents to perform a task with | (and display progress while doing so) | | With Worker.Progress | .Max = totalNumDocumentsToProcess' load progress bar max for overall | project | .Notice = "Inspecting documents" 'fill out caption or label or | whatever | | For Each Document in Worker.Documents | | .Increment 'increment progress bar | | DocumentInspector.InspectDocument(Document)...and display | progress during inspection | With DocumentInspector.Progress | .Max = Document.ObjectCount 'load progress bar intermediate | process | | For Each object in | DocumentInspector.DocumentObjectCollection | .Increment 'display progress intermediate level | .Notice = "Inspecting " & Document.Name | ... | Next object | End with | | With DatabaseControllerClass.Progress | .Max = NumRecordsToReadOrWrite | For Each Record | .Increment | ... | Next Record | End With | Next Document | end with | | | Option B | Each "worker / helper Class" raises events like _ProgressMax and | _ProgressIncrement in locations above where they are calling the respective | object properties | Upper level class receives events and calls it's own ProgressObject's | properties | | etc.... | | does any of that make any sense? | Thanks for any suggextions <g> (like ... can't you make your posts more | succinct? <vbg> ..sorry :-| ) | mark | |
|
Wed, 15 Sep 2010 13:01:46 GMT |
|
 |
Ralp #3 / 25
|
 Pass object or Raise Event?
Quote: > Hi all, > assume some classes working together to some end (which, composed of > different tasks, takes some time) > main creates controller class and asks for service > controller class creates worker class > worker class creates and uses some helper classes > objective, show progress to user as visual cue of activity > one possible implementation : a progress bar on a form > each task would set the progbar.max and increment based on workload and > eg loop progress > since there are multiple classes working at different times and one > "progressDisplayingObject" my question is:
Since there is only one "Progress Displaying Object" I would tend to go along with Plan A. One small addition: When I have certain attributes that must be set upfront before an object can do its job I prefer to use a 'constructor' instead of setting a 'Property'. Since VB doesn't support constructors a certain amount of discipline is required in either case, but you're less likely to forget a 'constructor' parameter. Public WithEvents evt As CEvent: Set evt = New CEvent .... Dim cls As CClass1: Set evt = New CClass1 cls.Init( evt, ... ) However, the real issues that you are likely to run into isn't with the callbacks from Grandchildren to Parent, it with be with receiving messages from items in Collections and Arrays. So you might find yourself dealing with more than one strategy. These might help: The venerable Karl Peterson code: http://vb.mvps.org/samples/project.asp?id=objarrays and Bryan's Collections: http://www.mvps.org/vbvision/collections.htm -ralph
|
Wed, 15 Sep 2010 13:08:31 GMT |
|
 |
Larry Serflate #4 / 25
|
 Pass object or Raise Event?
Quote: > objective, show progress to user as visual cue of activity
I'm no puritian, if I needed a progress bar on the UI, available to many different functions, I'd use the form itself to handle the display of the progress bar. Give it a property (Progress) and a timer so that the bar can appear to move, even while the work is being done. Then to indicate progress, you'd set the form's Progress property to the amount of the work you've just finished. If you did half the work, Progress = 50, if you then did 10 more percent, Progress = 10 and so on. The timer would smooth it out so it doesn't appear to be so jerky. Its just a rough estimate anyway, so I see no need to be exact, and no desire to make the whole thing an OO monster... <g> LFS
|
Wed, 15 Sep 2010 20:26:13 GMT |
|
 |
Ralp #5 / 25
|
 Pass object or Raise Event?
Quote:
> > objective, show progress to user as visual cue of activity > I'm no puritian, if I needed a progress bar on the UI, available to > many different functions, I'd use the form itself to handle the > display of the progress bar. Give it a property (Progress) and > a timer so that the bar can appear to move, even while the work > is being done. > Then to indicate progress, you'd set the form's Progress property > to the amount of the work you've just finished. If you did half the > work, Progress = 50, if you then did 10 more percent, Progress = 10 > and so on. The timer would smooth it out so it doesn't appear to > be so jerky. Its just a rough estimate anyway, so I see no need to > be exact, and no desire to make the whole thing an OO monster... > <g> > LFS
I agree. Objects are such messy, scary critters ... So just how do the grandchild, ... excuse me, make that Sub-SubRoutines go about letting the Form 'know' they are about 10%, 20%, ... or about "half done"? -ralph <g>
|
Wed, 15 Sep 2010 21:44:47 GMT |
|
 |
Steve Gerrar #6 / 25
|
 Pass object or Raise Event?
Quote:
> Is it better to ... > A. pass the "progressDisplayingObject" around to each class that > needs to show progress > Private moProgObj as IProgress 'interface > Set moProgObj = New ConcreteProgress 'Implementation > - or - > B. Each class raises events relating to it's own workload and > progress and the Controller (top level class) receives the > events and talks to the "progressDisplayingObject" itself > - or - > C. ?
Advantages of using events: 1. The process can be run without a visual when desired, as in batch mode, macros, etc. 2. The process can be run with two or more visuals when desired - progress bar plus caption update if minimized, etc. Like Larry, I would have the event picked up by the form itself, not a "progress display controller", though it could be relayed up a chain. I would avoid tying your processing classes to any particular GUI, or assuming there even is one.
|
Wed, 15 Sep 2010 23:42:19 GMT |
|
 |
Larry Serflate #7 / 25
|
 Pass object or Raise Event?
Quote: >> If you did half the > > work, Progress = 50, if you then did 10 more percent, Progress = 10 > > and so on. The timer would smooth it out so it doesn't appear to > > be so jerky. > I agree. Objects are such messy, scary critters ... > So just how do the grandchild, ... excuse me, make that Sub-SubRoutines go > about letting the Form 'know' they are about 10%, 20%, ... or about "half > done"?
Forms are globally scoped by default. Any routine in the project can access properties of such a form. If a person is of a mind to use a more limited scoped variable, then they get forced to pass around a reference to the form or devise some other means to access the form. But perhaps your question was aimed more at the value of completion, rather than access; Its just a slightly different perspective. How would some task know that is it 10% done, or 50% done? Somewhere it either has to make a guess, (1 of 3 tasks complete might be 20% of the entire job) or provide a fraction (200th loop in 1000, or whatever). Taking that 1 to 1000 loop as an example, the actual percentage would be the iterator divided by 1000, so an event raising approach might use every 100th loop (to avoid excessive delays) to raise a progress event passing the current fraction. Every 100th loop out of 1000 is 10%, in the Progress method every 100th loop would set Progress equal to 10. That 10 would be added to the values already sent to calculate the actual progress. That need not be what is displayed.... In the event method, whatever value used might (possibly) be applied to the progress bar display, which would make the display jump to the different values. What if one of the actions involves hitting the web? In that case the progress bar remains still while the request is serviced. Having the form use a timer would smooth out the display. When Progress is set to 10, that 10 is added to the other values passed in to provide a target value. The timer could be adjusted so that if the target value is greater than what is displayed (true at the first call where displayed is 0 and the target might be 10) the timer interval might be adjusted to run a little quicker to catch up. But after the displayed value increases above the target value, the timer interval could be increased to slow the indicator down, without actually remaining still. The point being the indicator would show motion (its still 'alive' and not hung) between calls to set the Progress value. If the task proceeds quickly, the bar moves quickly, and if the task goes slow, the bar moves slow. Compare that with the progress indicator for Internet Explorer for example. While waiting for the response from some server, the progress bar continues to move, fast at first, and if the delay is prolonged, the bar actually slows down, but does not stop entirely until the page either loads, or times out. Once more, I see a progress indicator as more of a '"I'm still busy" indicator rather than actually being representative of the amount of work completed. If every task was instantanious, there would be no need for such an indicator, but because it may appear to be froze while crunching some numbers, the progress indicator let's the user know that the program is not hung, its just taking a while to do the work. FWIW I don't mind using objects, but OTOH, I don't want to be adding more work for myself either. LFS
|
Thu, 16 Sep 2010 01:45:18 GMT |
|
 |
Ralp #8 / 25
|
 Pass object or Raise Event?
Quote:
> >> If you did half the > > > work, Progress = 50, if you then did 10 more percent, Progress = 10 > > > and so on. The timer would smooth it out so it doesn't appear to > > > be so jerky. > > I agree. Objects are such messy, scary critters ... > > So just how do the grandchild, ... excuse me, make that Sub-SubRoutines go > > about letting the Form 'know' they are about 10%, 20%, ... or about "half > > done"? > Forms are globally scoped by default. Any routine in the project can access > properties of such a form. If a person is of a mind to use a more limited > scoped variable, then they get forced to pass around a reference to the > form or devise some other means to access the form. But perhaps your > question was aimed more at the value of completion, rather than access; > Its just a slightly different perspective. How would some task know that > is it 10% done, or 50% done? Somewhere it either has to make a guess, > (1 of 3 tasks complete might be 20% of the entire job) or provide a > fraction (200th loop in 1000, or whatever). > Taking that 1 to 1000 loop as an example, the actual percentage would > be the iterator divided by 1000, so an event raising approach might use > every 100th loop (to avoid excessive delays) to raise a progress event > passing the current fraction. > Every 100th loop out of 1000 is 10%, in the Progress method every > 100th loop would set Progress equal to 10. That 10 would be added to > the values already sent to calculate the actual progress. That need not > be what is displayed.... > In the event method, whatever value used might (possibly) be applied > to the progress bar display, which would make the display jump to > the different values. What if one of the actions involves hitting the web? > In that case the progress bar remains still while the request is serviced. > Having the form use a timer would smooth out the display. When > Progress is set to 10, that 10 is added to the other values passed in > to provide a target value. The timer could be adjusted so that if the > target value is greater than what is displayed (true at the first call where > displayed is 0 and the target might be 10) the timer interval might be > adjusted to run a little quicker to catch up. But after the displayed > value increases above the target value, the timer interval could be > increased to slow the indicator down, without actually remaining still. > The point being the indicator would show motion (its still 'alive' and > not hung) between calls to set the Progress value. If the task > proceeds quickly, the bar moves quickly, and if the task goes slow, > the bar moves slow. Compare that with the progress indicator for > Internet Explorer for example. While waiting for the response from > some server, the progress bar continues to move, fast at first, and > if the delay is prolonged, the bar actually slows down, but does not > stop entirely until the page either loads, or times out. > Once more, I see a progress indicator as more of a '"I'm still busy" > indicator rather than actually being representative of the amount of > work completed. If every task was instantanious, there would be no > need for such an indicator, but because it may appear to be froze while > crunching some numbers, the progress indicator let's the user know > that the program is not hung, its just taking a while to do the work. > FWIW I don't mind using objects, but OTOH, I don't want to be > adding more work for myself either. > LFS
All good points. I'm sure the OP will benefit from your insight. There is always more than one way to go about something and no one solution is ideal for all situations, no matter how enamored we might be with our choosen one. I was only pulling your chain a bit, knowing as I do your feelings on OO. <bg> -ralph
|
Thu, 16 Sep 2010 02:18:40 GMT |
|
 |
MP #9 / 25
|
 Pass object or Raise Event?
Quote:
>>> If you did half the >> > work, Progress = 50, if you then did 10 more percent, Progress = 10 >> > and so on. The timer would smooth it out so it doesn't appear to >> > be so jerky.
I like the idea of smoothing out the increments if necessary, but I see that as icing on the cake. I'd be happy getting the "right" basic structure down first...in terms of who talks to whom and how, etc. Quote: >> So just how do the grandchild, ... excuse me, make that Sub-SubRoutines >> go >> about letting the Form 'know' they are about 10%, 20%, ... or about >> "half >> done"? > Forms are globally scoped by default. Any routine in the project can > access > properties of such a form.
re: the quote "Any routine in the project can access properties of such a form. " Sorry, I'm not understanding how to do that... doesn't that mean the classes need a reference to the form.? how do they know it's name otherwise? unless they were "coupled" to a particular gui, which is being said seemingly to not be the right way??? Quote: > Having the form use a timer would smooth out the display.
nice idea Quote: > The point being the indicator would show motion (its still 'alive' and > not hung) between calls to set the Progress value.
absolutely Quote: > Once more, I see a progress indicator as more of a '"I'm still busy" > indicator rather than actually being representative of the amount of > work completed.
yep Thanks mark
|
Thu, 16 Sep 2010 10:13:51 GMT |
|
 |
MP #10 / 25
|
 Pass object or Raise Event?
Quote:
>> objective, show progress to user as visual cue of activity > I'm no puritian, if I needed a progress bar on the UI, available to > many different functions, I'd use the form itself to handle the > display of the progress bar.
actually there is no UI currently required in particular in this app. the only UI is the progress indicator showing work being done the final output is that certain kinds of documents have been affected in certain ways. the final result can be reported in a text file and can be opened in notepad or not, if the user wishes to see it. otherwise they just know that a job has been requested and they're informed when it's done. Give it a property (Progress) and Quote: > a timer so that the bar can appear to move, even while the work > is being done. > Then to indicate progress, you'd set the form's Progress property > to the amount of the work you've just finished. If you did half the > work, Progress = 50, if you then did 10 more percent, Progress = 10 > and so on. The timer would smooth it out so it doesn't appear to > be so jerky. Its just a rough estimate anyway, so I see no need to > be exact, and no desire to make the whole thing an OO monster... > <g> > LFS
|
Thu, 16 Sep 2010 10:20:44 GMT |
|
 |
MP #11 / 25
|
 Pass object or Raise Event?
It would just be boring if all the experts had the same answer <g>
Quote:
>> Hi all, >> assume some classes working together to some end (which, composed of >> since there are multiple classes working at different times and one >> "progressDisplayingObject" my question is: > Since there is only one "Progress Displaying Object" I would tend to go > along with Plan A.
what about the issue of coupling here? doesn't the events option produce weaker coupling? as per Steves viewpoint? Quote: > One small addition: When I have certain attributes that must be set > upfront > before an object can do its job I prefer to use a 'constructor' instead of > setting a 'Property'.
Sorry, not sure I understand. Are you referring to my example of passing a ProgressDisplayObject to a "helper" class? Set moController = CreateController Set moProgObj = CreateProgressObject Set moController.Progress = moProgObj <---- is this what you refer to as setting a 'Property' above? so are you saying write the CreateController to require an arg of the progDisplay object so I don't set the property directly? Set moProgressDisplayObject = CreateProgressDisplayObject Set moController = CreateController(moProgressDisplayObject) ??? Quote: > Since VB doesn't support constructors a certain amount > of discipline is required in either case, but you're less likely to forget > a > 'constructor' parameter. > Public WithEvents evt As CEvent: Set evt = New CEvent > .... > Dim cls As CClass1: Set evt = New CClass1 > cls.Init( evt, ... ) > However, the real issues that you are likely to run into isn't with the > callbacks from Grandchildren to Parent, it with be with receiving messages > from items in Collections and Arrays. So you might find yourself dealing > with more than one strategy.
fortunately at the moment I'm not faced with that situation <g> Quote: > These might help: > The venerable Karl Peterson code: > http://vb.mvps.org/samples/project.asp?id=objarrays > and Bryan's Collections: > http://www.mvps.org/vbvision/collections.htm > -ralph
Thanks as always for your kind advice. Mark
|
Thu, 16 Sep 2010 10:31:29 GMT |
|
 |
Ralp #12 / 25
|
 Pass object or Raise Event?
Quote: > It would just be boring if all the experts had the same answer > <g>
Not really. I find most things go better and serene when everyone does things my way. I like boring. <g> Quote:
> >> Hi all, > >> assume some classes working together to some end (which, composed of > >> since there are multiple classes working at different times and one > >> "progressDisplayingObject" my question is: > > Since there is only one "Progress Displaying Object" I would tend to go > > along with Plan A. > what about the issue of coupling here? doesn't the events option produce > weaker coupling? as per Steves viewpoint? > > One small addition: When I have certain attributes that must be set > > upfront > > before an object can do its job I prefer to use a 'constructor' instead of > > setting a 'Property'. > Sorry, not sure I understand. Are you referring to my example of passing a > ProgressDisplayObject to a "helper" class? > Set moController = CreateController > Set moProgObj = CreateProgressObject > Set moController.Progress = moProgObj <---- is this what you refer to as > setting a 'Property' above? > so are you saying write the CreateController to require an arg of the > progDisplay object so I don't set the property directly? > Set moProgressDisplayObject = CreateProgressDisplayObject > Set moController = CreateController(moProgressDisplayObject) > ???
Pretty much. Only because it is easier for me to remember to always call Init() on most objects than a litany of Properties. But that has nothing to do with your question so excuse the interruption. But. I wasn't paying close enought attention and didn't notice the direction of the assignment. (I was assuming that you would naturally be doings things my way.) If it were me, I would assign the Controller to the ProgObj. Set moController = CreateController Set moProgObj = CreateProgressObject Set moProgObj.Progress = moController That's where this comes in.... "what about the issue of coupling here? doesn't the events option produce weaker coupling? as per Steves viewpoint?" You can go back and forth with definitions of 'coupling' and 'linking' and the relative strength of one thing over th'other. It all boils down to 'dependancy'. From the point of view of the object - "What do I need to do my job and service my clients"? From the point of view of the client - "What object should I use to get what I want and what does it need"? From the point of view of the programmer - "How much {*filter*}to I have to set up to get something to work"? Problem: A Form willing to listen. A bunch of objects with things to say. Create a Controller object Have it implement IDataStuff IFileStuff IProcStuff Have it generate a ProgressEvent Private Sub IDataStuff_SendMsgBack( ...) Raise Event ProgressEvent(...) End Sub In the Form Dim WithEvents evt As Controller ... Private sub evt_ProgressEvent(someValue, ....) Update ProgressBar End Sub ' each of the little objects ' CData Private mControl As IDataStuff Public Sub Init(control As IDataStuff) set mControl = control End Sub Private Sub DoingDataStuff '... mControl.SendMsgBack( something interesting) ... End Sub Nobody's obligated to anyone except all the little data, file, and Processing objects need an interface. But I can paste anything in there that supplies that. The Form only expects to catch an event from Controller Everyone's happy. Life is good. -ralph
|
Thu, 16 Sep 2010 12:45:12 GMT |
|
 |
Steve Gerrar #13 / 25
|
 Pass object or Raise Event?
Quote:
> Problem: > A Form willing to listen. > A bunch of objects with things to say. > Create a Controller object > Have it implement > IDataStuff > IFileStuff > IProcStuff > In the Form > Dim WithEvents evt As Controller > ' each of the little objects > ' CData > Private mControl As IDataStuff > Public Sub Init(control As IDataStuff) > set mControl = control > End Sub
Aren't you setting yourself up for a cross-linked objects problem? It seems to me you now have to make sure that every CData object lets go of mControl. Or first should I ask, who's the parent of the little CData objects? Starting out, anyway, I like parent to child communication through procedures, and child back to parent communication through events. Down the hierarchy with procedures, back up with events. Unless you hit a performance problem, it is usually cleaner.
|
Thu, 16 Sep 2010 13:10:43 GMT |
|
 |
Ralp #14 / 25
|
 Pass object or Raise Event?
Quote:
> > Problem: > > A Form willing to listen. > > A bunch of objects with things to say. > > Create a Controller object > > Have it implement > > IDataStuff > > IFileStuff > > IProcStuff > > In the Form > > Dim WithEvents evt As Controller > > ' each of the little objects > > ' CData > > Private mControl As IDataStuff > > Public Sub Init(control As IDataStuff) > > set mControl = control > > End Sub > Aren't you setting yourself up for a cross-linked objects problem? > It seems to me you now have to make sure that every CData object lets go of > mControl. Or first should I ask, who's the parent of the little CData objects?
No more than any other time one 'News' something (or should that be 'Sets' something) and passes it on. That's what Terminate is for. It all comes back to ownership. I set it, I destroy it. I forget it, I get screwed. If someone wants hand-holding they can go to .net. <g> Quote: > Starting out, anyway, I like parent to child communication through procedures, > and child back to parent communication through events. Down the hierarchy with > procedures, back up with events. Unless you hit a performance problem, it is > usually cleaner.
Sounds like a reasonable plan to me. -ralph
|
Thu, 16 Sep 2010 14:48:16 GMT |
|
 |
Steve Gerrar #15 / 25
|
 Pass object or Raise Event?
Quote:
>> Aren't you setting yourself up for a cross-linked objects problem? > No more than any other time one 'News' something (or should that be > 'Sets' something) and passes it on. That's what Terminate is for. It > all comes back to ownership. I set it, I destroy it. I forget it, I > get screwed. > If someone wants hand-holding they can go to .net. <g>
Actually I agree with that. ;-) As I think about it, I realize that for progress reporting, I don't do some elaborate chain of command. I have a global object, which has methods that can be called from anywhere, and has a handle on the main form. Sort of a hot line to the president, I guess. It's easy to control what mode it is in, and easy to call the progress methods from anywhere, without a lot fussing about.
|
Fri, 17 Sep 2010 00:06:24 GMT |
|
|
Page 1 of 2
|
[ 25 post ] |
|
Go to page:
[1]
[2] |
|