|
SYS-CON.TV Webcasts
Comments
Did you read today's front page stories & breaking news?
SYS-CON.TV
|
Top Links You Must Click On
Web Services Make Your Design Ideas Speak: Using UML in PowerBuilder Projects
UML has become the de facto standard graphical notation for software projects
Apr. 15, 2009 01:30 PM
PowerBuilder Developer's Journal A picture is worth a thousand words. We all have heard this saying a countless number of times. But what if you don't understand what is drawn in the picture? I was approached once with a request to review requirements specifications for some module. The document contained a scheme that represented the place of the module within the system. The picture was mainly made of rectangles and arrows. The rectangles had labels and I figured out that they represented other modules in the system. But I couldn't understand the meaning of the arrows. When I asked the author about them, the answer was "Well, they are just arrows." After a pause, "They show that there's some connection between the modules" was added.
Which UML? What to Draw For the description of the static aspect of the system design, most of the time I use only two types of the UML Structure Diagrams: the Component and Class Diagrams. As for the dynamic aspect of design, I find the Sequence Diagram is used most often. Sometimes two other UML Behavior Diagrams, the Activity and State Machine Diagrams, are useful. Component Diagram Probably, a short word is needed about what I mean by dependency here. I like using this term in the general sense that if a change in module A may lead to a change in module B, then module B is dependant on module A. In fast-growing systems, paying attention to dependencies is one of the key points of the creation of applications that are easier to maintain and extend - the fewer dependencies we have between the modules, the more resilient to changes inside each module the system is. There are probably two main kinds of dependencies between modules that can be found in PowerBuilder applications. The first one is derived from the provided and required interface relationship between classes. The second one is based on data use: one module produces some data that are consumed by another module. Although dependency via data is often important, I rarely depict it in diagrams not going beyond mere text remarks. What I focus my attention on is the provided and required interface relationship between modules, and here the component diagram comes in handy. Although there are no explicit means in PowerBuilder to limit the visibility of class names of the module to classes from other modules and programmers can use whichever class they want, it's up to the designer to establish which classes should be used to call for the functionality that the module provides. Let's have a look at Figure 1. The UML Component notation is used for depicting modules and sub-modules. The ball or lollipop represents the provided interface (the class that is to be used for accessing the module functionality). The socket represents the required interface - the class that is going to be used by other modules (here, Module B). In most cases, the names of the provided and required interfaces are the same, but they can be different if they belong to the same inheritance hierarchy and the required interface is more general (it's higher in the hierarchy) than the provided interface. As a simple illustration of usage of the component diagram, a data-centric application can be taken. Suppose the module being described is responsible for entry of particular data. Let's call it Capturer. The module is accessed by opening one of its windows, w_capturer. If we want to draw this as the component diagram, it would look something like Figure 2. If w_capturer expects parameters passed to it and the n_capt_parm object contains all the data needed by w_capturer, it may be drawn like the diagram shown in Figure 3. However, n_capt_parms might as well be omitted. Whether to draw it or not depends on many factors such as the context of the diagram (e.g., if all windows in the system are opened with the same parameter's object, there's no need to draw it for each window), whether n_capt_parms contains only input parameters or is also used for output parameters (as for me, I omit the objects that are only used as input parameters more easily), whether drawing n_capt_parms makes the diagram too cluttered (we want to create as clear and readable diagrams as possible), and so on. In the UML, only classes may be used as the provided and required interfaces. However, I use the notation with certain freedom; it happens that the access point for the module functionality is a global function and I draw it as the provided interface. If w_capturer is opened by other modules indirectly calling the global function f_capturer, it might be drawn as shown in Figure 4. It's important to remember what kind of classes we draw on component diagrams - the classes that act as interfaces between modules. Class Diagram If someone were to come up to you in a dark alley and say, "Psst, wanna see a UML diagram?" that diagram would probably be a class diagram. This type of the UML diagram offers a lot of notations, but I find that PowerBuilder applications can be described using just a few of them: class (with properties and methods), generalization, association, composition, and dependency. Let's start with classes. To avoid ambiguity, I must remind you that the names we give objects in PowerBuilder painters can play two roles in most cases - they can be a class name and an object name. When we draw class diagrams, we always assume only the former role of object names despite referring to "object names." I'm following this traditional naming and use the terms "class name" and "object name" interchangeably where possible when speaking about class diagrams. The general view of a class in the class diagram is shown in Figure 5. The box with three compartments is a complete form of the class representation. The first one contains the class name (in the UML, it should be written starting with a capital, but I personally often don't do this). The second compartment contains instance variables, controls (visual objects), and non-visual objects inserted into the class. They are called attributes in the UML. The last compartment contains events and functions of the object. They are called methods in the UML. If the object has no methods, the third compartment can be skipped. If the object has no attributes but has methods, I draw the second compartment empty. If you don't want to present the class in detail on a particular diagram, you may draw just a box with the object name. Although the UML has a special notation for attributes and methods, I use something in between this notation and the PowerBuilder declaration notation for variables, events, and functions. For attributes, its full form looks like this: access name : datatype For access, the following abbreviations are used: + for public, - for private, and # for protected. As in PowerBuilder notation, in case of arrays, the brackets and dimension follow the variable name. The similar notation is used for events and functions: access name( parameters ) : datatype As you can see, I don't make any distinction between functions and events. It's easy to explain why. Usually the method names have prefixes - ue_ for events and of_ for functions -so it's clear from the first sight what is what. Although the standard events do not have a prefix, their names are well known, so no problems arise in this case either. Parameters are presented as the list of their datatypes (simple datatypes or class names) delimited by a comma. Datatype is specified if the method has a return value. Now let's look at the example in Figure 6. The custom DataWindow control dw_customer is presented. It has a protected instance double variable customer_id, the public function of_retrieve that accepts a double and returns a long, the public user event ue_save that returns a boolean, and a script written in the standard rowfocuschanged event. Actually, this picture contains some unnecessary things. We know that events are always public, and most programming teams use prefixes for variables denoting their datatype. In reality I'd draw dw_customer as it's shown in Figure 7. To depict that one object is inherited from another, use the generalization UML notation. Let's look at Figure 8. It shows that dw_customer is inherited from a DataWindow. The rule of thumb for drawing the generalization notation is that the head of the arrow points at the ancestor. There are two other ways of showing attributes that are objects: association and composition. The alternative ways of drawing this are shown in Figure 9. On the left side, both in_parms, an instance variable of the n_capt_parms class, and dw_sheet, a DataWindow control placed on the window, are shown as attributes of w_capturer. On the right side, the same relationships are shown using the association notation for in_parms and the composition notation for dw_sheet. Note that the names of association and composition placed on the arrows are, in fact, the names of the attributes. You may ask why in_parms is depicted using association and dw_sheet is depicted using composition. I won't discuss the theoretical difference between association and composition. This simple rule tells ypu when you should use what is considered ownership of the referenced object. If the object that contains the attribute object is the only one having a reference to it (the object owns the attribute exclusively), use the composition notation. Another rule that helps to identify composition is how and when the referenced object is created and destroyed. If the object creates the attribute (very often on its own creation) and destroys it on getting destroyed itself, use the composition notation. These two rules help identify compositions very easily. For example, use composition for all controls; although other object may have references to them, the object that contains them creates and destroys them. The same applies to the inserted non-visual object - use composition if you want to draw them separately from the owning class. For all the other attributes use the association notation. The rule of thumb for drawing both notations is that the arrow head points to the object that is the class of the attribute. The label of the arrows is the name of the attribute. The last (but not the least!) notation is dependency. Draw it for the objects that are used as local variables and arguments to methods. For example, if w_frame opens w_capturer in some of its scripts and does not store the reference to it in its instance variable, this relationship can be drawn as it's shown in Figure 10. The head of the arrow points to the object that is used by the other one. Creating sketches is very subjective and this is especially true for class diagrams; it is the author who decides what needs to be drawn and what may be omitted. You want to create diagrams that are as clear and legible as possible but that still effectively communicate the design decisions. Even with this limited set of notations, quite wordy and cluttered diagrams may be created. For example, the fact that a window contains a DataWindow control is more accurately presented in Figure 11 (if you have any doubts about this, take a look at the script generated by PowerBuilder when you place a control on a window). Do you need to show your design in such detail? It's up to you and it depends on many factors. You need to clearly understand what is the key point you want to emphasize and what is insignificant for the sketch detail (that may be more or less obvious in terms of realization if you discuss design of a future module or may be easily and quickly looked up in the code if you're dealing with the design description of an existing system). With this understanding, you'll be able to decide whether to use attributes or associations and compositions, whether to show all attributes and methods or only some of them, whether to show access for attributes and methods or not, and so on so forth. Sequence Diagram If you are familiar with this type of UML diagram, you can guess that the most common notations I use describing PowerBuilder applications are objects (with their lifetimes and activation bars) and synchronous messages. A simple scenario where the DataWindow control is retrieved on the opening of its parent window is depicted in Figure 12. The boxes represent the objects that are participants in the interaction within the scenario. The full notation for the object name is: object name : Class name It's often clear from the context which class the object belongs to, so I often omit class names. If the particular instance of the class is of no importance, the object name can be omitted; keep the colon and write only the class name. The vertical lines represent the object lifetimes and the bars indicate that the object gets the control flow. The arrows are synchronous messages. I use them for the events and functions whose calls are triggered (in contrast to those posted). The open event is shown using the found message notation. It means that the sender of this message is unknown or external in the sense that either the message is system-generated (as in the case of the open event) or the sender is out of the scope of the diagram. The first message on any sequence diagram is usually a found message and is the starting point of the scenario being described. There's a special way to represent a self-call, shown in Figure 13. The ue_retrieve event is triggered to w_capturer from its open event script. The ue_retrieve event script then calls the retrieve DataWindow function. When the activation bar for ue_retrieve ends, the control flow goes back to open. Sometimes it's important to show when objects get created and destroyed. There's also the possibility of showing a return value from a call. If the system has an object responsible for fetching different options for the modules, and w_capturer needs to get its particular options in the form of another object, it could be drawn as shown in Figure 14. Most of the time I don't draw return calls, using it mainly for showing the correspondence between the returned object and the one that receives message calls. As you can see, ln_capt_options is the one created by of_get_capt_options and then used and destroyed in the open event script. The last thing about drawing sequence diagrams is showing methods that are posted. I use the asynchronous call along with found message UML notations, although the PowerBuilder posted methods are not precisely what is meant by asynchronous calls in the UML. If the ue_retrieve event is posted and I am to draw it all in one diagram, the result would be similar to Figure 15. It's clear from the diagram that after posting of ue_retrieve, execution of the open event script is not interrupted. It's also clear that ue_retrieve starts executing only after open has finished its work. From the notation point of view, the difference between the synchronous and asynchronous calls is in the arrowhead. As is the case with class diagrams, it's easy to create cluttered, difficult-to-read diagrams by drawing every possible detail. It's important to stay focused on the key points of the interaction you are describing. Other Behavior Diagrams This May Not Be Enough Tools and Resources Reader Feedback: Page 1 of 1
Enterprise Open Source Magazine Latest Stories . . .
Subscribe to the World's Most Powerful Newsletters
Subscribe to Our Rss Feeds & Get Your SYS-CON News Live!
|
SYS-CON Featured Whitepapers
Most Read This Week |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||