|
From: dhtml on 5 Apr 2008 14:59 On Mar 19, 1:21 pm, "H. S. Lahman" <h...(a)pathfindermda.com> wrote: > Responding to dhtml... > > >>> What does stack-based scope mean? I've seen you post that before. > >> Technically it means that state is only defined within the current call > >> stack. That is, a procedure can only reliably access a state variable > >> that has been defined/set by a procedure that is currently on the call > >> stack. Thus > > >> procedure A() > >> integer x; // definition in local stack scope > >> x = 5; > >> z = B(x); > > >> integer function B(x, y) > >> integer z; // definition in local stack scope > >> z = x * 2; // valid because A() put x on the stack > >> prior to calling B(). > >> z = x + y; // not valid because y is not defined on the stack. > >> return z; // pass state back up the call stack > > >> One could have defined y above as a global variable rather than an > >> argument. It would then have scope outside the stack. But bitter > >> experience has demonstrated that unrestricted global scope is a Bad > >> Idea. > > > It is the same in JS: Global variables are a Bad Idea. > > > The stack-based scope example, in JS:- > > > function a() { > > var x = 1; > > return b(); > > } > > function b() { > > return function() { alert(typeof x); };// undefined; > > } > > > a()(); > > > - Indicates that - x - is not accessible, but does not indicate > > anything about the - x - variable in - a -. > > However the following:- > > > function a() { > > var x = 1, y = document; > > // statements... > > y = null; > > return function() { alert(typeof x); };// undefined; > > } > > > a()(); > > > Shows that - x - lives on, even after a() has returned (so does - y -, > > unfortunately). Variable x, a Reference, would appear to be on the > > heap, not the stack. It cannot be written to, > > because does not provide the ability to access the Scope object that - > > x - is a property of. > > > How is this related to the OO-ness of EcmaScript 4? > > Stack-based scope is primarily of interest in functional programming > environments; they depend upon the fact that state can only be accessed > in a controlled fashion on the stack. That severely limits who can > access state data but the downside is the hierarchical dependency > structure of the operations that combine to produce any given state > being passed on the stack. > > In the OO world state data is accessed directly on a peer-to-peer basis. > That access requires navigating an existing relationship path between > the method needing the data and the object owning the data. The access > is limited (i.e., preventing the method from accessing the wrong > object's data) by the way the relationships along that path are > instantiated. > > Thus the OO approach has no hierarchical structure at all. > [snip about monads] > > >>> In JavaScript, we have function scope to form closure. nested > >>> functions -> nested scope, forming a chain. Though I think this is > >>> probably not what you meant. > > > It sounds like this actually is what you mean by stack-based scope. > > Yes. That nesting depends upon a stack architecture. > Good. So we're talking about the same thing. ES4 keeps the stack based scope. It's powerful and useful and you can't really get rid of it cause it's so popular. But it's abused. It gets misused for language shortcomings (no private modifier). So there will be other options (private and - let -, for block scope). > >> Such returns create an instant hierarchical dependency in the caller > >> implementation on what the callee does. One cannot specify what the > >> caller does in a DbC sense without also somehow specifying what the > >> callee does. Similarly, one cannot unit test the caller without a > >> correct implementation of the callee. (Stubbing the callee in the test > >> harness to provide the correct values for the test case inputs is just > >> self-delusion; one is just testing the test harness.) > I haven't realized problems with not being able to test the caller. The one thing that does bother me is the dependency chain. I don't like dependency chains for reasons you're talking about. I'm using closures in a pattern that is borrrowed from Perl. http://perldesignpatterns.com/?ExportingPattern Funny, btw: http://perldesignpatterns.com/?SoftwareQualityLevels > >>>> In OOA/D message and method are separated. Messages are announcements > >>>> about something the sender did and there is no expectation of what the > >>>> response (if any) will be. That enables rigorous decoupling of method > >>>> implementations. However, message and method are married in procedural > >>>> message passing because the message is the procedure signature. Worse, > >>>> one names procedures by what they do, so sending a message always > >>>> implies an expectation of what will happen. > > > I think it is still possible to unit test effectively in FOM system. > > Perhaps you can help improve my testing methodologies. > > Obviously, one can unit test procedural program units. However, it tends > to get messy because of the dependencies. The longer the chain, the more > of the program that needs to already be correctly implemented. IOW, one > needs to select units for unit testing in a particular, bottom-up order. > That, in turn, implies that units are developed in a bottom-up order. In > addition, when testing units near the top of the hierarchy chain one is > effectively functionally testing most of the application, which may > require substantial processing time. Worse, the combinatorial growth of > logical paths may make exhaustive testing of the unit a practical > impossibility. > The library that I have developed started with lower level units because I know that the higher level units will need them, so I wrote them and tested them thoroughly. The thing that still bothers me is the dependency chain. I would like to use DIP to have higher level modules depend on lower level ones, but there are places where I can't or don't know how to pull it off elegantly. These are places where I use a completely stateless, non behavioral object as a collection of functions. Things like APE.dom, for example. APE.dom includes: getOffsetCoords(el) getStyle(el, prop); getContainingBlock(el); They all basically work on an - el - property. Now, it would be possible to make an object - Element - that decorated el, but I can't figure out a way to make it both efficient and light. Most of the time I don't need all that functionality on one object. Moreover, I have separated these functionality into sub-groups so that I can test them. In fact, one function I wrote, getOffsetCoords now has 92 tests (46 doubled with scrolling). The tests cover different conditions that can occur in different browsers. Another option that I could use would be to have an Element wrapper that included variable functionality, depending on which modules were included. The methods would be changed to:- Element.prototype.getOffsetCoords(); Element.prototype.getStyle(prop); Element.getContainingBlock(); Where - el - argument is removed and instead the function executes in context of decorator - Element and Element has an el property. I don't see what this would buy me, in terms of testability. I could stup out the decorator Element, but would still have to deal with all the garbage related to the browsers (wack APIs, bugs, et c). Talking about complexity of combinations of method calls, add n browser environments to the mix greatly increases complexity increase. > Typically unit testers cop out and stub the called functions, letting > the test harness supply the correct values for the input test stimuli. > But, as I pointed out, that is just a form of self-delusion. The problem > is that though each descendant unit may get unit tested, the > combinations don't. IOW, the unit under test depends upon the > descendants playing together properly and that isn't unit tested when > stubbing. > It can be a problem when the lowest-level components are buggy. > Now suppose that all behavior methods are procedures that do not return > values. Also suppose we ensure that the current values of whatever data > the method under test needs are timely. That is, the method is defined > such that it only needs the current state of the solution when it is > invoked. (This essentially eliminates a circular dependence where the > method under test relies on a method it calls to set a data value it > will subsequently use). > I think this means that behavioral methods can call other methods, but shouldn't rely on state change this causes. So if I had a _show() method that called position() and show():- _show : function() { this.position(); this.show(); this._isHidden = false; } > Now to unit test that method all we need to know are: (a) what values > any inputs have, including state data in other objects that the method > will access; (b) what values in other objects it sets during its > operation; and (c) what messages it issues (message ID and data packet). > IOW, all one needs to know is the state of the solution before and after > invoking the method under test. Since OO messages are announcements, it > is sufficient to simply know that the proper messages were sent; one > does not have to know what would happen in response to those messages. > > A major goal of OOA/D is to define behaviors in exactly that sort of > self-contained fashion where they only depend on the current state of > the solution represented by message data and the current values of > relevant state variables (object knowledge attributes). > > [There is actually a rigorous way to apply DbC to ensure that the state > of the objects attributes are correct whenever one invokes a method. One > can rigorously apply that technique to connect the dots of flow of > control between the object behaviors in the solution. But it depends on > behaviors being abstracted in a cohesive, atomic, and self-contained > manner. IOW, it depends on good OOA/D problem space abstraction.] > > Of course one still has the play-together problem for multiple units, > but that is the job of functional testing at the subsystem and system > level. The difference is that when we unit test OO methods we can always > exhaustively test the full method specification. How much that > contributes to reliability is anyone's guess. However, IME OO > development has consistently reduced defects by at least 50% compared to > procedural development. (That experience includes controlled experiments > where the same MLOC application was rewritten completely twice using > different methods.) > I think exhaustive testing is a significant factor. In fact, if it is a goal, then the methods that get written will be more testable. I don't like finding myself in situations where testing the code is too painful. Integrated Messaging Exchange. > >>> Why is it bad to name the callback based on what it does? > >> It is not just callbacks. Since we name procedures by what they do and > >> invoke them by name, that implies and expectation by the caller on what > >> will happen. IOW, in the procedural/functional paradigms the message is > >> essentially Do This. > > > You're comparing two types of messaging. I'm going to make up some > > terms: > > > FOM - Function Oriented Messaging > > - a dummy callback function is defined on the object's prototype. > > The function name indicates the type of message. The object's > > prototype callback 'dummy' can be shadowed by an instance property > > that provides implementation. > > > OOM - Object Oriented Messaging > > - an "addSubscriber" function allows a subscriber with a "notify" > > function to be added. The message is a parameter to "notify" (or can > > also be a - type - property of the payload). > > No, I'm afraid not. There are two types of messages in OOA/D, but they > don't map like that. One type of message is a synchronous service that > only accesses knowledge attributes synchronously. Getters/Setters are > the common form of this type of message and a getter does return a value. > > The other type of message is for behavior collaborations (i.e., solution > flow of control). These invoke behavior responsibilities and they never > return values. The form is always {message ID, <optional data packet>} > and they are always announcements of something the message sender did. > In OOA/D these are asynchronous because that is the most general way to > describe behavior communications. (One can always implement an > asynchronous communication model in a synchronous environment > unambiguously but one cannot always implement a synchronous > communication model in an asynchronous environment unambiguously.) > > There is normally no need for any sort of addSubscriber or notify > construct. That is only needed when one needs to abstract that sort of > processing _from the problem space_ to solve the problem in hand (e.g., > the Observer design pattern). > I see. > However, this is all beside the point; I think the whole topic of > callbacks is a red herring. The issue is that in procedural hierarchies > there is always an implied expectation (Do This) when a message is sent > because messages are named by what will happen. The mindset in OO > development is the opposite; messages are always announcements (I'm > Done) and there is no expectation of what will happen next. (more below > on the OO view of callbacks.) > > >> The purpose of this separation of message and response is to break the > >> hierarchical chains of operations resulting from functional > >> decomposition. Object behaviors are supposed to be atomic, cohesive, > >> intrinsic, and self-contained. For that to be true their specification > >> cannot depend on other behaviors. Combining the I'm Done view with doing > >> flow of control at a different level of abstraction ensures that one > >> doesn't think about chains of operations when defining individual object > >> responsibilities. > > > I see. One problem I'm noticed in one of the native Host objects - > > HTMLInputElement - is:- > > > var q = document.getElementsByName('q')[0]; > > q.onfocus = function( e ) { > > e.preventDefault(); > > alert(this); > > }; > > > q.focus(); > > > This can be run againstwww.google.com. The problem is sort of a > > chicken and the egg. q.focus and q.onfocus say "do this", but they're > > contradictory. > > > In a more OOM system, would we have a - focus - method? If not, how > > would it work? > > Unfortunately I don't do JavaScript and I don't know anything about the > example problem space, so I don't really have much clue what the example > is doing. > > It's calling focus() on the input (e.g. "Do This"). This triggers the browser to draw focus to the input. This causes the browser to call the associated onfocus function, which in turn calls event.preventDefault(). event.preventDefault() should stop the input from actually receiving focus(). But that already happened. Part of the reason for having a focus() method is to draw focus to the input. This probably propagates back to an internal focusManager that uses some sort of activeElement idiom to determine which element has focus, then remove focus from that element and set focus to the new element. > > >>> If the callback were a GenericEventHandler, it would require a lot of > >>> internal if/else, or switch, to check the message type. > >> From an OOA/D perspective, all messages look like {message ID, <data > >> packet>}. One can have as fine a granularity in the message IDs as one > >> needs. At OOP time one just maps message ID -> method name and <data > >> packet> -> method arguments. The real decoupling of implementations came > >> when one defined what individual methods did using the I'm Done mindset. > > > Here's a code example comparing OOM to FOM:- > > > OOM:- > > // attach callback: > > aButton.addSubscriber( anObject ); > > > anObject.buttonEventHandler( data ) { > > switch( data.type ) { > > case "mouseover" : { > > this.handleButtonMouseover( data ); > > break; > > } > > // more cases > > } > > } > > > The OOM approach: > > (1) Decouples the callback with I'm Done (or I'm Doing, in this > > case). > > FOM:- > > > // attach callback. > > aButton.addCallback( "click", anObject.buttonClicked, anObject ); > > > // callback. > > anObject.buttonClicked = function( data ) { }; > > > The FOM approach: > > (1) allows for a variable - this -. > > (2) directly wires a "click" event to - anObject -. > > > This allows for the callback to "talk back" to - aButton - by > > returning false (to prevent the default user action). > > > In FOM, - aButton - becomes dependent on handling a return value of > > the callback:- > > > if( clickCallback() == false ) > > preventDefaultAction() > > > -The conditional check that the button object peforms is the > > questionable coupling resulting from FOM. But how bad is it, really? > > > It's possible to test the callback by passing a fake payload. > > It's easy to test any class that calls callbacks, to verify that the > > payload is passed. > > In an OO context one would probably implement callbacks differently. For > the GUI example, Button and other display objects would be encapsulated > in a subsystem while the solution logic that responded to user actions > was in another system. The Button would send an appropriate message to > the solution subsystem when the user did something to the Button. The > solution subsystem interface would decode the message ID and relay the > message to the appropriate object to provide a business response to the > user action. > > Thus the subsystem interfaces would provide the decoupling such that > objects in either subsystem would have no idea objects in the other > subsystem even existed. In doing so the subsystem interface provides a > mapping service between the display context and the business context > (e.g., a message ID of buttonXClicked in a GUI might map into > saveCustomerZToDatabase in the business context). > An Event Notification System. > There are problem space situations, though, that would require the sort > of registration that procedural callbacks represent _within a > subsystem_. We commonly deal with those using the Observer pattern. In > effect one introduces an intermediary object to manage the "callback" > mapping. The object needing to announce something would send an ordinary > announcement message to the intermediary object whenever it did something. > > Objects that need to know about that announcement "register" with the > intermediary object by sending it a message with some context > information. The intermediary object then maps that context information > to the announcement messages it expects to receive. So when an > announcement it received, it relays that announcement to whoever > registered to know about it. > > You will note that the intermediary object performs the same role as the > subsystem interface in the GUI example. It provides decoupling by > encapsulating the mapping of announcement messages to receivers using > some sort of context information provided during registration. That is, > the entire callback mechanism is encapsulated in the intermediary object > that exists solely to manage that. > Notifying receivers can be abstracted. There's a few slightly different ways to accomlish that. ONe way is to have an "event" function on the Button, the other is to have a custom event Object on the button. With the function approach, the event subsystem creates the event object and binds the callbacks to that object. function Calendar() { this.selectEvent = new CustomEvent("onselect"); // Intermediary object. } function Calendar() { } // Default callback fuction is noop. // subsystem shadows this with a same-named instance prop. Calendar.prototype.onselect = function(){}; The Calendar code would have in one of it's methods:- Calendar.clickHandler = function(e) { var isADate = false, date = null; //... if(isADate) { this.selectEvent.fire( {date: date} ); // or - // this.onselect( {date: date} ); } } - In the first approach the event is subscribe with:- aCalendar.selectEvent.addCallback( fun ); In the second approach, the subsystem assigns an instance - onselect - property to the object, if it doesn't exist (the first time a callback is added). The subsystem creates the intermediary object. It's a little more going on behind the scenes. EventRegistry.add( aCalendar, "onselect", fun ); if(!anObject[ type ]) { anObject[ type ] = EventRegistry.notifySubscribers(this); // create a bound function. } > Note that the Observer pattern is very consistent with the I'm Done > mindset of defining responsibilities. The object generating a message > knows nothing about who will respond to it or what the response will be. > Similarly, the responder registers based on some context *it* > understands so it doesn't know who will send the message or why they > will send it. The mapping between sender and receiver contexts is then > provided by the subsystem interface or an intermediary Observer object. > > >> In the OO paradigm there are essentially only three levels of > >> decomposition: Subsystem, object, and responsibility. Subsystems collect > >> objects; and objects collect responsibilities. Thus program elements are > >> aggregated quite differently than the indefinite nesting of > >> functionality under functional decomposition. In particular, objects are > >> defined as combinations of both knowledge and behavior with equal stature. > > > Are objects about more about behavior or knowledge (data)? JavaScript > > treats an object as if it were a collection of data. > > In the OO paradigm what an object is responsible for knowing and what it > is responsible for doing have equal stature. In practice, it is quite > often possible to abstract the problem space either in terms of > knowledge or in terms of behavior, more commonly some combination of > both. Which will be best depends upon the overall solution needs. > > For example, consider a simple Traffic Light in one direction. One can > abstract it as a pure knowledge holder by giving it a 'color' attribute > and a setColor(RED | YELLOW | GREEN) setter that just set the 'color' > attribute. That might be appropriate if all the problem needs to do is > keep track of what the current color is. > > But if the Traffic Light is really a device driver that physically > changes a traffic light's color, one might want behaviors for setRed(), > setYellow(), and setGreen() that each wrote to different hardware > registers with actions like read/modify/write. That would be an > exclusively behavioral perspective. > > One could also provide a state machine: > > [Red] <----------+ > | | > | change | > | | > V | change > [Green] | > | | > | change | > | | > V | > [Yellow] --------+ > > where each state has an appropriate action to set the hardware and there > is a single event to toggle the color. The state machine then also > captures sequencing constraints on the color changes internally in the > state machine transitions. (In the data view above, those constraints > would have the be implemented in some other object.) One might also have > a 'color' attribute that each state action set and a getColor getter to > determine the current state of the light for other objects. > The color seems secondary to the synchronization. An advanced system would allow an abulance to fire a change event, too. I suppose the ambulance signal would trigger some component of the traffic light system which would fire an event. > Bottom line: knowledge and behavior in objects are often > interchangeable. It all depends on what problem one needs to solve. > > >> The biggest single difference between OO development and traditional > >> development is the elimination of hierarchical dependencies that are > >> inherent in functional nesting. OO collaborations are always > >> peer-to-peer with each behavior being logically indivisible at the > >> subsystem's level of abstraction. So there are no long chains of nested > >> functionality and one uses announcement messages to connect the dots of > >> flow of control. > I understand this. I think I could still benefit from having a more disciplined OO programmer reviewing my JS. > > What about a composite? That's not peer-to-peer. For example:- > > Map.Entry<K,V>. An unordered list (<UL>), the Document itself is a > > composite. > > Alas, I haven't a clue what Map.Entry<K,V> is about. B-( > That's an entry in a Map. A map is a set of Entry. It's keyed by type K and holds values of type V. That's in Java, anyway. ES4 will have that too. > In OO Land, composites are made up of peer objects that have > relationships with one another. For example: > > [Book] > | 0..1 > | start of > | > | R1 > | > | > | 1 0..1 follows > [Chapter] <------+ > | 0..1 | > | precedes | R2 > | | > +-------------+ > > >>> So there is a layer of indirection to decouple the subscriber from the > >>> publisher? > >> There can be in OOA/D because UML treats object interfaces as separate > >> model elements from the classes that define what objects are. The > >> interface describes the messages the class members will process while > >> the classes themselves describe the intrinsic responsibilities the > >> object have. Then the developer provides a mapping between interface > >> message and class method. > > >> However, there need not be any indirection in the OOP code. As I > >> indicated, the reason one separates message and method in OOA/D is to > >> provide a mindset that will strongly discourage defining methods so that > >> they depend on context outside the object. Once one has successfully > >> decoupled method implementations at the design level, it doesn't matter > >> how they actually communicate. IOW, separation of message and method and > >> thinking in terms of I'm Done (rather than Do This) in collaborations > >> is really just to ensure the right frame of mind for the developer when > >> defining object responsibilities and flow of control. > > I see. So the callback is private, and cannot have meaning to anyone > > else. There is no reason that it should be externally observable, as > > part of the object's public interface. There should be no method of: - > > buttonClickHandler - on the object's public interface. Contrast to > > that, making - buttonClickHandler - externally observable would tie - > > anObject - to it's delegate button. > > Not quite. The responsibility to do something is public. The > implementation of the method that provides the responsibility is hidden. > In this context, though, we are really talking about what the > responsibility semantics *is*. That definition needs to be atomic and > self-contained. > > One reviewer test of object implementations is to mentally move all of > the messages (calls) generated by the method to the beginning or end of > the method and change their order. If the results of execution don't > change, then the method doesn't depend on the responses. > I see. Taking a look at some code. I have a Calendar that wants to be shown when a text input box is focused. You've probably seen these on a travel site before. When the text input is blurred, the calendar should hide, but when the text input's blur is caused by the user clicking within the calendar, the calendar should not hide. The user might have wanted to change the month or year. If the calendar is hidden, this won't be possible. There are some areas where I find difficulty in following this advice in my own code. Like this:- _hide : function(e) { if(this._isHidden) return; if(this._cancelHide) { this._cancelHide = false; return; } this.onhide(e); if(this.constructor.activeCalendar === this) this.constructor.activeCalendar = null; stac // allow this.hide(document.getElementById(this.calendarId).style); this._isHidden = true; }, > > The the way to acheive such restricted access in javascript is through > > use of a closure. The problem with this (besides the fact that it > > leads to a very long method for a closure): A closure with an event > > handler callback can lead to memory leaks in Internet Explorer:- > > Right. That's because the closure spans multiple nested operations but > the callback can happen any time in the middle. > If this is abstracted to a closure-generator that does the method binding, the problem is avoided. > BTW, one way to look at this is that closure in an OO context is > different than in a procedural/functional context. Since OO behaviors > are not supposed to return values the caller uses or modify state > variables the caller accesses after the call, the functional closure is > much simpler in the OO case because it is just the scope of the caller > method. > > But the nasty part is OO closure around data integrity (i.e., ensuring > all the attributes accessed by the method are timely). That is trivial > in functional development, tricky in procedural, and potentially a bear > in OO. Fortunately, as I mentioned above, one has DbC to rigorously > (albeit somewhat tediously) resolve it in complicated situations. > > [A corollary is that one will tend to have more procedures and more > messages in an OO design. That's because ensuring data integrity in the > OO context often requires some degree of "handshaking" to make sure data > gets set in the proper sequence. As it happens, object state machines > are ideally suited to this -- which is something R-T/E developers > figured out decades ago but the rest of the software industry still > hasn't got yet.] > I would fall into the "rest pf the software industry." I've never done any embedded development. I'm a pure JS hacker with Java edu. The Calendar is here: http://dhtmlkitchen.com/ape/example/widget/calendar/ http://dhtmlkitchen.com/ape/build/widget/calendar/Calendar.js Garrett > -- > There is nothing wrong with me that could > not be cured by a capful of Drano. > > H. S. Lahman > h...(a)pathfindermda.com > Pathfinder Solutionshttp://www.pathfindermda.com > blog:http://pathfinderpeople.blogs.com/hslahman > "Model-Based Translation: The Next Step in Agile Development". Email > i...(a)pathfindermda.com for your copy. > Pathfinder is hiring:http://www.pathfindermda.com/about_us/careers_pos3.php. > (888)OOA-PATH
From: H. S. Lahman on 6 Apr 2008 11:08 responding to dhtml... >> Obviously, one can unit test procedural program units. However, it tends >> to get messy because of the dependencies. The longer the chain, the more >> of the program that needs to already be correctly implemented. IOW, one >> needs to select units for unit testing in a particular, bottom-up order. >> That, in turn, implies that units are developed in a bottom-up order. In >> addition, when testing units near the top of the hierarchy chain one is >> effectively functionally testing most of the application, which may >> require substantial processing time. Worse, the combinatorial growth of >> logical paths may make exhaustive testing of the unit a practical >> impossibility. >> > > The library that I have developed started with lower level units > because I know that the higher level units will need them, so I wrote > them and tested them thoroughly. > > The thing that still bothers me is the dependency chain. I would like > to use DIP to have higher level modules depend on lower level ones, > but there are places where I can't or don't know how to pull it off > elegantly. These are places where I use a completely stateless, non > behavioral object as a collection of functions. Things like APE.dom, > for example. APE.dom includes: > <snip exampple> The chain is there for three reasons. One is that procedural scripting languages encourage it, even if they have object-based features. You can get around that by being disciplined, but it can be a pain (more below). The second problem is that the infrastructures need to deal with specific 3GL implementation problems like stateless objects. Thus an OOA/D model would have stateful objects and would rely on OOP implementation to separate state from behavior. But the infrastructures already provide that so one has to build around them rather than building the higher level OOA/D specification in a natural way and converting it. The third problem is that the infrastructure code is often designed procedurally and employs standard procedural idioms like callbacks. IOW, it is designed around Do This usage. That makes it hard to design procedures properly that need to use the infrastructure. >> Typically unit testers cop out and stub the called functions, letting >> the test harness supply the correct values for the input test stimuli. >> But, as I pointed out, that is just a form of self-delusion. The problem >> is that though each descendant unit may get unit tested, the >> combinations don't. IOW, the unit under test depends upon the >> descendants playing together properly and that isn't unit tested when >> stubbing. >> > > It can be a problem when the lowest-level components are buggy. Bingo. Neither the specification of the higher level procedure or the test results are focused enough. >> Now suppose that all behavior methods are procedures that do not return >> values. Also suppose we ensure that the current values of whatever data >> the method under test needs are timely. That is, the method is defined >> such that it only needs the current state of the solution when it is >> invoked. (This essentially eliminates a circular dependence where the >> method under test relies on a method it calls to set a data value it >> will subsequently use). >> > > I think this means that behavioral methods can call other methods, but > shouldn't rely on state change this causes. Yes. > > So if I had a _show() method that called position() and show():- > > _show : function() { > this.position(); > this.show(); > this._isHidden = false; > } Not exactly. I am not sure what the this.show() is about here. Let's try a more concrete example. Suppose we have a notion of Position in an ATC system that is composed of both a specific location and an error envelope. An OO object might look something like: class Position { public: boolean isHidden; display(); ... private: Coordinate myPosition; ErrorAxis myMajorAxis; ErrorAxis myMinorAxis; displayPosition(); // displays Coordinate displayErrorEnvelop(); // constructs envelop around axes ... } Position::display() { displayPosition(); displayErrorEnvelop(); this.isHidden = FALSE; } Since the caller of Position::display() only needs to know that it is time to display a position (more precisely, that it did something that requires that a position be displayed), the full specification of Position::display() is that isHidden is reset and messages have been sent to display the position and display the error envelop. (To keep things simple, those are just private methods of the same object, but they would usually be in another object.) Now if one were to do this in a procedural manner one might have: display (Coordinate c, int majorAxis, int minorAxis) { displayPosition(c); displayErrorEnvelop(c, majorAxis, minorAxis); isHidden = FALSE; } and the displayPosition and displayErrorEnvelop procedures were in a library somewhere. Now the specification of display() is that it calls the other methods with the right data and resets the isHidden state variable (wherever that is). Similarly, displayPosition and displayErrorEnvelop are specified purely in terms of what data they are passed. Since the dependency chain is broken -- in part by passing Coordinate to displayErrorEnvelop so that it can be self-contained -- one can rewrite the procedure as: display (Coordinate c, int majorAxis, int minorAxis) { isHidden = FALSE; displayErrorEnvelop(c, majorAxis, minorAxis); displayPosition(c); } and everything will Just Work. Note that in the OO mindset the name 'display' for the procedure is not quite accurate because that procedure is not really responsible for the actual display. All it is specified to do is reset the isHidden state variable. The calls that it makes are really just messages that it must issue to announce that it has reset the isHidden state variable. Thus the rendering responsibility belongs to whoever cares about those messages -- and it is conveniently self-contained. Thus the developer is deciding who cares about isHidden being reset at a higher level of abstraction (overall solution flow of control). The fact that the announcement messages happen to also identify the explicit responding procedure is a serendipitous side effect of 3GL procedural message passing. So to remove dependencies in procedural contexts one needs to religiously do several things: (1) never return values that the caller uses. (2) specify the procedure's processing purely in terms of input state variables or in terms of business rules that specify sequencing of actions. [Actually sequencing constraints are much more robustly handled by providing handshaking for interacting state machines. But that's a different story that is orthogonal to the OOness of JS...] (3) ensure that any state variables the procedure needs have timely values _when the procedure is invoked_. (4) think of calls a procedure makes as messages that announce something the procedure did. IOW, the procedure specification is simply what it did itself and that it announced what it did properly. >>>>> If the callback were a GenericEventHandler, it would require a lot of >>>>> internal if/else, or switch, to check the message type. >>>> From an OOA/D perspective, all messages look like {message ID, <data >>>> packet>}. One can have as fine a granularity in the message IDs as one >>>> needs. At OOP time one just maps message ID -> method name and <data >>>> packet> -> method arguments. The real decoupling of implementations came >>>> when one defined what individual methods did using the I'm Done mindset. >>> Here's a code example comparing OOM to FOM:- >>> OOM:- >>> // attach callback: >>> aButton.addSubscriber( anObject ); >>> anObject.buttonEventHandler( data ) { >>> switch( data.type ) { >>> case "mouseover" : { >>> this.handleButtonMouseover( data ); >>> break; >>> } >>> // more cases >>> } >>> } >>> The OOM approach: >>> (1) Decouples the callback with I'm Done (or I'm Doing, in this >>> case). >>> FOM:- >>> // attach callback. >>> aButton.addCallback( "click", anObject.buttonClicked, anObject ); >>> // callback. >>> anObject.buttonClicked = function( data ) { }; >>> The FOM approach: >>> (1) allows for a variable - this -. >>> (2) directly wires a "click" event to - anObject -. >>> This allows for the callback to "talk back" to - aButton - by >>> returning false (to prevent the default user action). >>> In FOM, - aButton - becomes dependent on handling a return value of >>> the callback:- >>> if( clickCallback() == false ) >>> preventDefaultAction() >>> -The conditional check that the button object peforms is the >>> questionable coupling resulting from FOM. But how bad is it, really? >>> It's possible to test the callback by passing a fake payload. >>> It's easy to test any class that calls callbacks, to verify that the >>> payload is passed. >> In an OO context one would probably implement callbacks differently. For >> the GUI example, Button and other display objects would be encapsulated >> in a subsystem while the solution logic that responded to user actions >> was in another system. The Button would send an appropriate message to >> the solution subsystem when the user did something to the Button. The >> solution subsystem interface would decode the message ID and relay the >> message to the appropriate object to provide a business response to the >> user action. >> >> Thus the subsystem interfaces would provide the decoupling such that >> objects in either subsystem would have no idea objects in the other >> subsystem even existed. In doing so the subsystem interface provides a >> mapping service between the display context and the business context >> (e.g., a message ID of buttonXClicked in a GUI might map into >> saveCustomerZToDatabase in the business context). >> > > An Event Notification System. Bingo. In OO contexts a common way to implement communications among subsystems is with an event-based subsystem interface. In addition, I am a translationist who relies on a full code generator to generate an application from an OOA model. All of the translation-based methodologies mandate that object behavior be expressed with object state machines. That's because an asynchronous communication model is the most general so the code generator can always implement it unambiguously in any environment (serial single process, concurrent, distributed, whatever). So for a translationist, even objects communicate with events. B-) BTW, it is no accident that the RAD IDEs all employ event-based infrastructures (i.e., one provides VB code for event hooks). The RAD IDEs do that for the same reason translationists do it -- it is the most general way to do things and it also provides decoupling because events are pure data transfer messages. The RAD IDE just doesn't need data packets with the events since all they announce is that it is time to do something, which is captured in the event ID. >>>> In the OO paradigm there are essentially only three levels of >>>> decomposition: Subsystem, object, and responsibility. Subsystems collect >>>> objects; and objects collect responsibilities. Thus program elements are >>>> aggregated quite differently than the indefinite nesting of >>>> functionality under functional decomposition. In particular, objects are >>>> defined as combinations of both knowledge and behavior with equal stature. >>> Are objects about more about behavior or knowledge (data)? JavaScript >>> treats an object as if it were a collection of data. >> In the OO paradigm what an object is responsible for knowing and what it >> is responsible for doing have equal stature. In practice, it is quite >> often possible to abstract the problem space either in terms of >> knowledge or in terms of behavior, more commonly some combination of >> both. Which will be best depends upon the overall solution needs. >> >> For example, consider a simple Traffic Light in one direction. One can >> abstract it as a pure knowledge holder by giving it a 'color' attribute >> and a setColor(RED | YELLOW | GREEN) setter that just set the 'color' >> attribute. That might be appropriate if all the problem needs to do is >> keep track of what the current color is. >> >> But if the Traffic Light is really a device driver that physically >> changes a traffic light's color, one might want behaviors for setRed(), >> setYellow(), and setGreen() that each wrote to different hardware >> registers with actions like read/modify/write. That would be an >> exclusively behavioral perspective. >> >> One could also provide a state machine: >> >> [Red] <----------+ >> | | >> | change | >> | | >> V | change >> [Green] | >> | | >> | change | >> | | >> V | >> [Yellow] --------+ >> >> where each state has an appropriate action to set the hardware and there >> is a single event to toggle the color. The state machine then also >> captures sequencing constraints on the color changes internally in the >> state machine transitions. (In the data view above, those constraints >> would have the be implemented in some other object.) One might also have >> a 'color' attribute that each state action set and a getColor getter to >> determine the current state of the light for other objects. >> > > The color seems secondary to the synchronization. An advanced system > would allow an abulance to fire a change event, too. I was modeling a simpler traffic light (i.e., very few have that feature). So... > > I suppose the ambulance signal would trigger some component of the > traffic light system which would fire an > event. > >> Bottom line: knowledge and behavior in objects are often >> interchangeable. It all depends on what problem one needs to solve. This is where that comes in. A priori data and behavior are interchangeable. But with a specific suite of requirements in hand, one view will usually be the "best". >>>>> So there is a layer of indirection to decouple the subscriber from the >>>>> publisher? >>>> There can be in OOA/D because UML treats object interfaces as separate >>>> model elements from the classes that define what objects are. The >>>> interface describes the messages the class members will process while >>>> the classes themselves describe the intrinsic responsibilities the >>>> object have. Then the developer provides a mapping between interface >>>> message and class method. >>>> However, there need not be any indirection in the OOP code. As I >>>> indicated, the reason one separates message and method in OOA/D is to >>>> provide a mindset that will strongly discourage defining methods so that >>>> they depend on context outside the object. Once one has successfully >>>> decoupled method implementations at the design level, it doesn't matter >>>> how they actually communicate. IOW, separation of message and method and >>>> thinking in terms of I'm Done (rather than Do This) in collaborations >>>> is really just to ensure the right frame of mind for the developer when >>>> defining object responsibilities and flow of control. >>> I see. So the callback is private, and cannot have meaning to anyone >>> else. There is no reason that it should be externally observable, as >>> part of the object's public interface. There should be no method of: - >>> buttonClickHandler - on the object's public interface. Contrast to >>> that, making - buttonClickHandler - externally observable would tie - >>> anObject - to it's delegate button. >> Not quite. The responsibility to do something is public. The >> implementation of the method that provides the responsibility is hidden. >> In this context, though, we are really talking about what the >> responsibility semantics *is*. That definition needs to be atomic and >> self-contained. >> >> One reviewer test of object implementations is to mentally move all of >> the messages (calls) generated by the method to the beginning or end of >> the method and change their order. If the results of execution don't >> change, then the method doesn't depend on the responses. >> > > I see. Taking a look at some code. I have a Calendar that wants to be > shown when a text input box is focused. You've probably seen these on > a travel site before. > > When the text input is blurred, the calendar should hide, but when the > text input's blur is caused by the user clicking within the calendar, > the calendar should not hide. The user might have wanted to change the > month or year. If the calendar is hidden, this won't be possible. > > There are some areas where I find difficulty in following this advice > in my own code. Like this:- > > _hide : function(e) { > if(this._isHidden) return; > > if(this._cancelHide) { > this._cancelHide = false; > return; > } FWIW, I am not enchanted with this. It is using behavior to indirectly deal with whether the element is hidden. That is, there are two state variables that provide different spins on the same basic problem semantics. Either the element is hidden or it isn't. I think one should change that status directly in response to the solution context changing. IOW, whoever now sets cancelHide should, instead, call _show. If necessary, _show should have logic to decide whether isHidden really needs to be reset based on other current context information. (Conversely, whoever now resets cancelHide should call _hide, which can, in turn, look at other context variables to see if one really wants to reset isHidden.) One reason this is fragile is because a lot might happen between when cancelHide is set/reset and when _hide/_show is called. That can lead to synchronization problems during maintenance. Another consideration is that cancelHide really isn't capturing the problem context. The real context is whether the display is already blurred or not, which seems to be triggered by the user clicking within the calendar (i.e., giving it focus). If one more directly captures the user context, the possibility of maintenance screwups is reduced. IOW, capture whether the user focus is in the calendar directly (e.g., currentFocus as an enum of the display elements that could have focus) and then let _show and _hide make decisions vis a vis isHidden based on that. It will then be harder for a maintainer to mess things up. > this.onhide(e); > if(this.constructor.activeCalendar === this) > this.constructor.activeCalendar = null; > stac > // allow > this.hide(document.getElementById(this.calendarId).style); > this._isHidden = true; > }, > >>> The the way to acheive such restricted access in javascript is through >>> use of a closure. The problem with this (besides the fact that it >>> leads to a very long method for a closure): A closure with an event >>> handler callback can lead to memory leaks in Internet Explorer:- >> Right. That's because the closure spans multiple nested operations but >> the callback can happen any time in the middle. >> > If this is abstracted to a closure-generator that does the method > binding, the problem is avoided. I'm not sure what you mean, but from your discussion it seems awkward enough to avoid by not having nested dependency chains in the first place. B-) -- There is nothing wrong with me that could not be cured by a capful of Drano. H. S. Lahman hsl(a)pathfindermda.com Pathfinder Solutions http://www.pathfindermda.com blog: http://pathfinderpeople.blogs.com/hslahman "Model-Based Translation: The Next Step in Agile Development". Email info(a)pathfindermda.com for your copy. Pathfinder is hiring: http://www.pathfindermda.com/about_us/careers_pos3.php. (888)OOA-PATH
From: dhtml on 16 Apr 2008 01:53 On Apr 6, 9:08 am, "H. S. Lahman" <h...(a)pathfindermda.com> wrote: > responding to dhtml... > > >> Obviously, one can unit test procedural program units. However, it tends > >> to get messy because of the dependencies. The longer the chain, the more > >> of the program that needs to already be correctly implemented. IOW, one > >> needs to select units for unit testing in a particular, bottom-up order. > >> That, in turn, implies that units are developed in a bottom-up order. In > >> addition, when testing units near the top of the hierarchy chain one is > >> effectively functionally testing most of the application, which may > >> require substantial processing time. Worse, the combinatorial growth of > >> logical paths may make exhaustive testing of the unit a practical > >> impossibility. > > > The library that I have developed started with lower level units > > because I know that the higher level units will need them, so I wrote > > them and tested them thoroughly. > > > The thing that still bothers me is the dependency chain. I would like > > to use DIP to have higher level modules depend on lower level ones, > > but there are places where I can't or don't know how to pull it off > > elegantly. These are places where I use a completely stateless, non > > behavioral object as a collection of functions. Things like APE.dom, > > for example. APE.dom includes: > > <snip exampple> > > The chain is there for three reasons. One is that procedural scripting > languages encourage it, even if they have object-based features. You can > get around that by being disciplined, but it can be a pain (more below). > > The second problem is that the infrastructures need to deal with > specific 3GL implementation problems like stateless objects. Thus an > OOA/D model would have stateful objects and would rely on OOP > implementation to separate state from behavior. But the infrastructures > already provide that so one has to build around them rather than > building the higher level OOA/D specification in a natural way and > converting it. > > The third problem is that the infrastructure code is often designed > procedurally and employs standard procedural idioms like callbacks. IOW, > it is designed around Do This usage. That makes it hard to design > procedures properly that need to use the infrastructure. > > >> Typically unit testers cop out and stub the called functions, letting > >> the test harness supply the correct values for the input test stimuli. > >> But, as I pointed out, that is just a form of self-delusion. The problem > >> is that though each descendant unit may get unit tested, the > >> combinations don't. IOW, the unit under test depends upon the > >> descendants playing together properly and that isn't unit tested when > >> stubbing. > Testing events is a little tricky. If it's necessary to test the callback, then just pass the expected type in the payload. Testing the thing that fires the event requires creating the environment where that event will fire. aMethod: function() { this.changeEvent.fire({value:1}); } Where aMethod is an instance or prototype method. > > It can be a problem when the lowest-level components are buggy. > > Bingo. Neither the specification of the higher level procedure or the > test results are focused enough. > > >> Now suppose that all behavior methods are procedures that do not return > >> values. Also suppose we ensure that the current values of whatever data > >> the method under test needs are timely. That is, the method is defined > >> such that it only needs the current state of the solution when it is > >> invoked. (This essentially eliminates a circular dependence where the > >> method under test relies on a method it calls to set a data value it > >> will subsequently use). > > > I think this means that behavioral methods can call other methods, but > > shouldn't rely on state change this causes. > > Yes. > > > > > So if I had a _show() method that called position() and show():- > > > _show : function() { > > this.position(); > > this.show(); > > this._isHidden = false; > > } > > Not exactly. I am not sure what the this.show() is about here. > [snip display example] > > and everything will Just Work. > > Note that in the OO mindset the name 'display' for the procedure is not > quite accurate because that procedure is not really responsible for the > actual display. All it is specified to do is reset the isHidden state > variable. The calls that it makes are really just messages that it must > issue to announce that it has reset the isHidden state variable. Thus > the rendering responsibility belongs to whoever cares about those > messages -- and it is conveniently self-contained. > > Thus the developer is deciding who cares about isHidden being reset at a > higher level of abstraction (overall solution flow of control). The fact > that the announcement messages happen to also identify the explicit > responding procedure is a serendipitous side effect of 3GL procedural > message passing. > > So to remove dependencies in procedural contexts one needs to > religiously do several things: > > (1) never return values that the caller uses. > > (2) specify the procedure's processing purely in terms of input state > variables or in terms of business rules that specify sequencing of > actions. [Actually sequencing constraints are much more robustly handled > by providing handshaking for interacting state machines. But that's a > different story that is orthogonal to the OOness of JS...] > I see. > (3) ensure that any state variables the procedure needs have timely > values _when the procedure is invoked_. > > (4) think of calls a procedure makes as messages that announce something > the procedure did. IOW, the procedure specification is simply what it > did itself and that it announced what it did properly. > > > >>>> In the OO paradigm there are essentially only three levels of > >>>> decomposition: Subsystem, object, and responsibility. Subsystems collect > >>>> objects; and objects collect responsibilities. Thus program elements are > >>>> aggregated quite differently than the indefinite nesting of > >>>> functionality under functional decomposition. In particular, objects are > >>>> defined as combinations of both knowledge and behavior with equal stature. > >>> Are objects about more about behavior or knowledge (data)? JavaScript > >>> treats an object as if it were a collection of data. > >> In the OO paradigm what an object is responsible for knowing and what it > >> is responsible for doing have equal stature. In practice, it is quite > >> often possible to abstract the problem space either in terms of > >> knowledge or in terms of behavior, more commonly some combination of > >> both. Which will be best depends upon the overall solution needs. > How do you explain Math? Math knows nothing about what it is. Is this one of those "stateless objects"? Math is a bunch of static methods that take parameters, plus it has some constants (PI, E). Is a Math object OO? APE.dom is like java.lang.Math > > > > I suppose the ambulance signal would trigger some component of the > > traffic light system which would fire an > > event. > > >> Bottom line: knowledge and behavior in objects are often > >> interchangeable. It all depends on what problem one needs to solve. > > This is where that comes in. A priori data and behavior are > interchangeable. But with a specific suite of requirements in hand, one > view will usually be the "best". > Apriori data. I found a Wikipedia entry for that, but it seemed out of context and I didn't get it. How do you make the connection here? "One view" of what is best? > >>>>> So there is a layer of indirection to decouple the subscriber from the > >>>>> publisher? > >>>> There can be in OOA/D because UML treats object interfaces as separate > >>>> model elements from the classes that define what objects are. The > >>>> interface describes the messages the class members will process while > >>>> the classes themselves describe the intrinsic responsibilities the > >>>> object have. Then the developer provides a mapping between interface > >>>> message and class method. It's making me thing more about those "return false;" statements that we use with DOM 0 events. when a callback "returns false," then the default action is prevented. Particularly with your condition (1) above: "> (1) never return values that the caller uses." DOM Events added e.preventDefault(), as a way of messaging back, saying "Dont Do whatever your'e supposed to". This replaces the "return false." > > Another consideration is that cancelHide really isn't capturing the > problem context. The real context is whether the display is already > blurred or not, which seems to be triggered by the user clicking within > the calendar (i.e., giving it focus). If one more directly captures the > user context, the possibility of maintenance screwups is reduced. IOW, > capture whether the user focus is in the calendar directly (e.g., > currentFocus as an enum of the display elements that could have focus) > and then let _show and _hide make decisions vis a vis isHidden based on > that. It will then be harder for a maintainer to mess things up. > > > this.onhide(e); > > if(this.constructor.activeCalendar === this) > > this.constructor.activeCalendar = null; > > stac > > // allow > > this.hide(document.getElementById(this.calendarId).style); > > this._isHidden = true; > > }, > I see. _hide() can be called from a few places: 1) from input blur 2) from document mousedown 3) from calendarMousedown, after a delay, after a date was selected. FWIW, I did figure out a way to do it using the approach of a "safety zone" (the enum of elements that are safe to mousedown in). New variable: _hasFocus calendarObject._hasFocus gets set and reset in calendarMousedown and document mousedown. I think it's better design, though I don't have a failing test that made me do the change. Under normal circumstances, I wouldn't've made that change, but this is more of a design issue, and I think you've got good ideas (and I have time). This may come in handy when I go and try to add keyboard navigation for the calendar widget. Thanks for the feedback. Was helpful. Garrett > > -- > There is nothing wrong with me that could > not be cured by a capful of Drano. > > H. S. Lahman
From: H. S. Lahman on 16 Apr 2008 11:10 Responding to dhtml... >>>>>> In the OO paradigm there are essentially only three levels of >>>>>> decomposition: Subsystem, object, and responsibility. Subsystems collect >>>>>> objects; and objects collect responsibilities. Thus program elements are >>>>>> aggregated quite differently than the indefinite nesting of >>>>>> functionality under functional decomposition. In particular, objects are >>>>>> defined as combinations of both knowledge and behavior with equal stature. >>>>> Are objects about more about behavior or knowledge (data)? JavaScript >>>>> treats an object as if it were a collection of data. >>>> In the OO paradigm what an object is responsible for knowing and what it >>>> is responsible for doing have equal stature. In practice, it is quite >>>> often possible to abstract the problem space either in terms of >>>> knowledge or in terms of behavior, more commonly some combination of >>>> both. Which will be best depends upon the overall solution needs. > > How do you explain Math? Math knows nothing about what it is. Is this > one of those "stateless objects"? Math is a bunch of static methods > that take parameters, plus it has some constants (PI, E). > > Is a Math object OO? > > APE.dom is like java.lang.Math In general the OO approach is not very good at pure algorithmic processing; procedural or functional programming is much more intuitive for that sort of processing. That's fine in the world of mathematics because the algorithms don't change. So in an OO context such algorithms are relegated to function libraries that are invoked from methods. Thus one could probably identify objects like Pivot and Partition in a Quicksort algorithm but it would be overkill. Typically if one has a complex mathematical algorithm that can be defined to operate on a suite of inputs to produce an output (i.e., a lambda calculus formulation), it will be developed outside the OO development and the access will be encapsulated in an object method. For example I once worked on a large system that required a Ford-Fulkerson network solution for a local problem as part of the overall problem solution. The network algorithm was encoded in a few KLOC of FORTRAN. The OO application set up the input data matrices and then invoked the network algorithm from within a single method. When the algorithm was done the method distributed the results in the returned solution matrix to program object attributes and then the OO solution continued on it merry way. >>> I suppose the ambulance signal would trigger some component of the >>> traffic light system which would fire an >>> event. >>>> Bottom line: knowledge and behavior in objects are often >>>> interchangeable. It all depends on what problem one needs to solve. >> This is where that comes in. A priori data and behavior are >> interchangeable. But with a specific suite of requirements in hand, one >> view will usually be the "best". >> > > Apriori data. I found a Wikipedia entry for that, but it seemed out of > context and I didn't get it. How do you make the connection here? "One > view" of what is best? Not quite what I meant. B-) I should have written it, "A priori, data..." Substitute "Before knowing the detailed requirements" for "A priori". As far as recognizing the "best" solution, that is an issue for software design. When faced with a bunch of alternatives for some particular processing one needs to look at the Big Picture and determine which alternative will play together best with the rest of the solution. In the traffic light example one has to look at the overall problem. Let's say the light is one of two, one in each direction, and the changes are based on some high level view of traffic flow where one wants to ration flow in each direction equitably. Whoever knows that it is time to switch flows doesn't care about the details of G -> Y -> R or even which direction the current flow is going. It just knows it is time to change flows. In that case you don't want the object thinking about traffic flows to need to set the color (e.g., invoking setGreen() on one light and setYellow() the another). That will not be very robust because the implementation of the traffic controller object knows too much about context (i.e., has dependencies on the state of Traffic Light in its implementation). It also needs to know about G -> Y -> R, which is a detailed set of rules that are at a much lower level of abstraction than simply time slicing traffic flows. So the cohesion of the traffic controller is also trashed. In that case one needs a generic message like toggle(). But that leads to complications with the intermediate yellow for one light. So one needs an *overall* solution that will allow the traffic controller to be independent of the current state and detailed processing of the lights but will ensure the G -> Y -> R sequence is honored safely. Contrast that with the view of Traffic Light in a GUI subsystem. In that subsystem all one wants to do is change the color in the display; all the traffic flow concerns are handled by some other subsystem. So all one needs is setColor(...) and the Traffic Light object is a pure data holder. In fact, the semantics of Traffic Light only appears in title or label text strings or specific icons. So someone else who understands the problem domain semantics of traffic control will be telling the GUI subsystem to change the color and what color to change it to. (E.g., in my example model, each state action would send a message to the GUI subsystem with a particular color to set.) >>>>>>> So there is a layer of indirection to decouple the subscriber from the >>>>>>> publisher? >>>>>> There can be in OOA/D because UML treats object interfaces as separate >>>>>> model elements from the classes that define what objects are. The >>>>>> interface describes the messages the class members will process while >>>>>> the classes themselves describe the intrinsic responsibilities the >>>>>> object have. Then the developer provides a mapping between interface >>>>>> message and class method. > > It's making me thing more about those "return false;" statements that > we use with DOM 0 events. when a callback "returns false," > then the default action is prevented. Particularly with your condition > (1) above: > "> (1) never return values that the caller uses." > > DOM Events added e.preventDefault(), as a way of messaging back, > saying "Dont Do whatever your'e supposed to". This replaces the > "return false." Note that callbacks are a form of decoupling in a procedural environment. By registering the callback with the callback invoker, one is removing any need for the callback invoker to understand what will happen in response to something it did. IOW, the callback mechanism allows an I'm Done philosophy when constructing the callback invoker's implementation. All it has to do is announce it has done its thing by defining an event and somebody else provides the response for that event. In theory the callback provider can define the callback purely in terms of what it needs to do. Then the registration process is orthogonal to both the callback provider and the callback invoker so the mapping of events to callbacks can be defined at a different level of abstraction than the implementations of either implementation. Alas, there are two practical problems. One is physical coupling at the 3GL level. The callback is still a particular method signature so for the compiler to write correct code in the callback invoker, the signature (i.e., argument order and types) has to be fixed _by the callback invoker_. So the callback provider must provide a procedure that is defined by the callback invoker's implementation. Because message and method are not separated in procedural message passing at the 3GL level, there is no <convenient> way to insert a mapping function between the callback provider and the callback invoker. So the callback provider is still somewhat dependent on the callback invoker in its implementation, albeit pretty mildly. The other is a psychological problem for the developer. Because the signature is necessarily defined by the callback invoker and by the basic nature of the mechanism, it is very easy to fall into the trap of thinking about what the client is doing when defining the callback invoker's implementation. Thus the very existence of e.preventDefault implies that the callback invoker knows something about its client (i.e., there is a default to prevent). This is very easy to do when the callback invoker must define the signatures AND the events. IOW, it is very easy to assume a Do This attribute towards the client when designing what events to announce. It gets worse when the same developer is defining both the callback invoker and the callback provider. There will be a strong temptation to define the callbacks based on a sequence of operations in the client. That is, it will be convenient to define the events based on how one is designing the client. A variation on this theme is common in interoperability and RAD layered model infrastructures. The infrastructure designer designs to his vision of how the clients *should* work. The result is that the infrastructure tail wags the client dog. Bottom line: callbacks go a long way towards decoupling but not as far as separation of message and method in the OO paradigm. -- There is nothing wrong with me that could not be cured by a capful of Drano. H. S. Lahman hsl(a)pathfindermda.com Pathfinder Solutions http://www.pathfindermda.com blog: http://pathfinderpeople.blogs.com/hslahman "Model-Based Translation: The Next Step in Agile Development". Email info(a)pathfindermda.com for your copy. Pathfinder is hiring: http://www.pathfindermda.com/about_us/careers_pos3.php. (888)OOA-PATH
|
Pages: 1 Prev: Dalai Lama: your smiles charm but your actions harm Next: Object modeling question |