Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

.NET

Visual Programming with Reusable Objects


DEC95: Visual Programming with Reusable Objects

Visual Programming with Reusable Objects

Construction-from-parts can simplify development

Carol Jones and Morgan Kinne

Carol and Morgan are software engineers for IBM. Morgan can be contacted at [email protected], and Carol, at [email protected].


Although well-known in other industries, the idea of construction from parts is just now catching on in the software industry. To build a house, for example, you wouldn't consider designing and manufacturing every piece from scratch. Likewise, rather than designing and developing every bit of software for an application, it's much easier to assemble standard, prebuilt parts.

Parts are software objects that support a simple, standard interface protocol. Parts can vary widely in capabilities, ranging from simple (buttons and arrays, for example) to complex (forms or even entire applications). Complex parts are typically built by combining a number of simple parts into one.

IBM's VisualAge is an object-oriented development environment that uses this construction-from-parts paradigm. With this environment, you build applications by visually arranging and connecting the prefabricated parts (objects) found on the VisualAge parts palette. Furthermore, you can extend the environment by adding any Smalltalk object to the palette.

VisualAge provides a set of primitive, visual parts that are basic building blocks for user interfaces--a push button or list box, for example. Any of these primitive visual parts can be combined to form reusable composite parts such as a form or window. These composite parts can then be added to the parts palette, so that they are available for other development projects.

VisualAge also provides primitive nonvisual parts, such as arrays and digital audio players, which work the same as visual parts. You can write your own nonvisual parts in Smalltalk, such as a customer object (or rule) that sends a reminder notice whenever a customer's payment is overdue. These nonvisual parts can also be combined to form composite parts that can be added to the palette. Combining parts into new, composite reusable parts is what differentiates construction-from-parts technology from concepts such as software modularity.

Public Interfaces

The basic principle of visual programming in VisualAge is defining an application's behavior by connecting the actions, events, and attributes of the parts. These elements are the part's public interface. Actions are the functions or methods that a part knows how to perform. A part can notify other parts of the occurrence of an event. The simplest example is a push button, which notifies other parts when it is pressed by signaling its clicked event. Attributes are the data stored by a part, such as a balance for a bank-account part, or a result for a database-query part.

When you build parts for VisualAge, you write Smalltalk code defining these actions, events, and attributes. For example, actions are simply methods that VisualAge calls. Attributes are usually stored in instance variables, and you write Smalltalk methods that return, compute, or set the values of attributes.

Building a Timer Part

To illustrate how construction-from-parts works, we'll design and implement a timer, which is a nonvisual part that runs for a certain length of time, then notifies other parts when the time limit has expired. The timer is able to automatically restart after each expiration.

First, we'll use the VisualAge tools to create a new application that contains a nonvisual part named Timer. The result is an empty part that we enhance using the appropriate VisualAge editors. The next, and most important step is to construct Timer's public interface.

You define a part's public interface using VisualAge's Public Interface Editor. The editor, which uses a notebook metaphor (see Figure 1), includes separate pages for attributes, actions, and events.

The public interface of Timer has an integer-length attribute that contains the number of milliseconds that will elapse before the timer expires. It also has a Boolean repeat attribute indicating whether the timer automatically restarts. The actions needed are start and stop. A timerFired event is needed to notify other parts when the timer has expired.

Once the definition is complete, VisualAge generates much of the Smalltalk code for you; see Listing One. You can generate code as often as you wish to accommodate changes in a part's public interface, although retaining any code changes involves some manual steps.

The timer part's definition causes the generation of both get and set selectors for each attribute. A generated set selector stores an input value in the attribute variable and notifies VisualAge that the value has changed. VisualAge can then notify other parts of the new value. Typically, a generated set selector needs no modifications. Get selectors, on the other hand, are normally modified to use lazy initialization to establish a default value for the attribute. Action methods are always modified and frequently notify other parts of events. You can also add other variables and methods as required to complete the part's logic (Listing Two).

At this point, the timer can be used as is. However, you can refine it by implementing class methods in the part that integrate it more tightly into the VisualAge development environment. (If you don't implement any of these methods, VisualAge uses appropriate default values.)

VisualAge parts typically provide a settings view, which is a visual part that allows a user to specify attribute values for a particular instance of the part. For Timer, it would allow you to set the length and repeat values. You create the view by adding a variable part from the palette to a new visual part named TimerSettingsView. You change the variable part to indicate it is a placeholder for Timer. You tear-off a "quick form" for Timer and drop it in the window part that VisualAge automatically provides. You can add two push buttons and connect the clicked event of one of them to the execute action of the variable part. The clicked event of the other push button is connected to the window's closeWidget action. You tell VisualAge to use this settings view by implementing the customSettingsView class method in Timer. Figure 2 shows the implementation of the custom settings view.

For Timer, we'll also implement class methods that add the part to the parts palette (addPartToCatalog), provide a custom icon for the part (abtInstanceGraphicsDescriptor), provide a descriptive name for the part (displayName), and customize its connection pop-up menu (preferredConnectionFeatures). See Listing Three for the implementation of these class methods.

The fully implemented Timer is now available for reuse in any VisualAge application.

Building the Image Viewer

To illustrate how to use Timer in a typical application, we'll build a slide-show application that's based on the part. This application, however, requires another reusable part--a form that displays bitmaped images, with an attribute to hold a collection of bitmap filenames, and actions to step forward or backward through the files. You start by creating a new visual part called SlideCarousel. Unlike the timer part, this has a user interface. Another difference is that it is a composite part, which means it has other parts contained inside it.

When you create a composite, visual part like this one, VisualAge automatically adds a main window. Since this visual part is intended for use inside other windows, delete the main window and replace it with a form part. This is where the image will be displayed.

For the public interface, define one attribute, images, and two actions: displayNext and displayPrevious (Listing Four). As with the timer part, define a suitable icon and descriptive name for this new part, and add it to the parts palette.

Now that you have some useful parts, you assemble them into an application. Begin by creating another visual part named SlideShow. Note that this is also a reusable part: Just like any other VisualAge part, you can add it to the parts palette.

Inside the window, place a SlideCarousel part and size it to fill most of the window. Below this, add two buttons for moving forward and backward through the slides. Next, add Timer from the palette. You use Timer to advance the slides after five seconds, if no button is pressed.

Since visual programming is all about connecting parts, you visually add connections that start the timer when the window opens and stop it when the window closes. Make connections between the timer and each button, to stop and restart the timer when the button is clicked. To advance the slides, connect the timer's timerFired event to the carousel's displayNext action. To make the buttons work, connect their clicked events to the carousel's displayNext action and displayPrevious action.

To complete the application, you need to set initial values for certain attributes. One of them is the carousel's images attribute. To set this, write a Smalltalk script that runs when the window first opens, and adds several filenames to the list of images (see Listing Five).

The last step is setting the attributes for the timer. Set the length to 5000 milliseconds and the repeat attribute to True. Figure 3 shows the finished application and its connections. Figure 4 shows the running application.

Summary

Your success depends on a sufficient supply of standard parts that you can draw upon. As more parts are developed, they will become increasingly useful and require less new code. Reusing code saves time and reduces the chance of introducing new errors.

Figure 1: Constructing the Timer part's public interface.

Figure 2: Timer custom settings view with connections.

Figure 3: Visually constructed slide-show application.

Figure 4: Executing the slide-show application.

Listing One

"Generated IBM Smalltalk code for the Timer part"
AbtAppBldrPart subclass: #Timer
  instanceVariableNames: 'length repeat'
  classVariableNames: ''
  poolDictionaries: ''
eventTimerFired: anObject
  "Notify other parts that the timer has expired."
  self signalEvent: #timerFired.
length
  "Return the value of length."
  ^length
length: anInteger
  "Save the value of length."
  length := anInteger.
  self signalEvent: #length with: anInteger.
repeat
  "Return the value of repeat."
  ^repeat
repeat: aBoolean
  "Save the value of repeat."
  repeat := aBoolean.
  self signalEvent: #repeat with: aBoolean.
start
  "Perform the start action."
stop
  "Perform the stop action."

Listing Two

"Modified and added code for the Timer part"
AbtAppBldrPart subclass: #Timer
  instanceVariableNames: 'length repeat timer'
    classVariableNames: ''
    poolDictionaries: ''
eventTimerFired: anObject
  "Notify other parts that the timer has expired."
  self signalEvent: #timerFired.
  timer := nil.
  self repeat ifTrue: [self start].
length
  "Return the value of length."
  length isNil ifTrue: [self length: 0].
  ^length
length: anInteger
  "Save the value of length."
  length := anInteger.
  self signalEvent: #length with: anInteger.
repeat
  "Return the value of repeat."
  repeat isNil ifTrue: [self repeat: false].
  ^repeat
repeat: aBoolean
  "Save the value of repeat."
  repeat := aBoolean.
  self signalEvent: #repeat with: aBoolean.
start
  "Perform the start action."
  timer := CwAppContext default
             addTimeout: self length
             receiver: self 
             selector: #eventTimerFired:
             clientData: nil.
stop
  "Perform the stop action."
  timer notNil
    ifTrue: [CwAppContext default removeTimeout: timer].

Listing Three

"Timer class methods"
addPartToCatalog
  "Adds the part to the VisualAge parts palette"
  | category |
  category := AbtPartsCatalog current categoryNamed: 'Models'.
  category isNil
    ifFalse: [category addPart: (self symbol)].
customSettingsView
  "Answer an instance of the custom settings view"
  ^TimerSettingsView newPart.
displayName
   "Answers the default name for this kind of part"
  ^'Timer'
preferredConnectionFeatures
  "Answer an array of items to place on the connection pop-up menu"
  ^#(length repeat start stop timerFired)
abtInstanceGraphicsDescriptor
      "Answer the icon for the part on the palette"
  ^(AbtIconDescriptor new
      moduleName: 'abticons';
      id: 277)

Listing Four

"Code for the SlideCarousel part"
AbtAppBldrView subclass: #SlideCarousel
    instanceVariableNames: 'images index '
    classVariableNames: ''
    poolDictionaries: ''
displayNext
    "Perform the displayNext action."
    | i | 
    i := (self index + 1).
    (i > self images size) ifFalse: [
          self index: i.
          (self subpartNamed: 'Label1') graphicsDescriptor: (self images at: i)
          ].
displayPrevious
      "Perform the displayPrevious action."
    | i | 
    i := (self index - 1).
    (i <= 0) ifFalse: [
          self index: i.
         (self subpartNamed: 'Label1') graphicsDescriptor: (self images at: i)
          ].
images
      "Return the value of images."
    images isNil ifTrue: [images := OrderedCollection new].
    ^images
images: anOrderedCollection
    "Save the value of images."
    images := anOrderedCollection.
    self signalEvent: #images
             with: anOrderedCollection. 
index
  "Return the value of index"
    index isNil ifTrue: [index := 0].
    ^index
index: aNum
  "Save the value of index"
    index := aNum

Listing Five

"Code for the SlideShow application"
AbtAppBldrView subclass: #SlideShow
    instanceVariableNames: ''
    classVariableNames: ''
    poolDictionaries: ''
initializeBitmaps
  "set up the collection of images"
  (self subpartNamed: 'Slide Carousel') images
    add: (AbtBitmapDescriptor new
                moduleName: 'ABTBMP30'; id: 416);
    add: (AbtBitmapDescriptor new
                moduleName: 'ABTBMP30'; id: 417);
    add: (AbtBitmapDescriptor new
                moduleName: 'ABTBMP30'; id: 418).
  (self subpartNamed: 'Slide Carousel') displayNext
DDJ


Copyright © 1995, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.