1. Trang chủ
  2. » Công Nghệ Thông Tin

professional java user interfaces phần 10 pptx

61 223 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Professional Java User Interfaces Phần 10
Trường học Standard University
Chuyên ngành Computer Science
Thể loại Bài giảng
Năm xuất bản 2006
Thành phố City Name
Định dạng
Số trang 61
Dung lượng 1,99 MB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

The BitmapSymbol class implementation The bitmap object represents an image in the container, as shown in Figure 16.1 onpage 570.. We defined deep actions as the proper way to useSwing’s

Trang 1

Try launching the executable sandbox.jar file, and have fun with the actualprogram

An example interaction

Suppose the user has already performed some manipulations on the objectsshown in Figure 16.2 First undo the rotation on the bitmap using the contextualmenu, as in Figure 16.3

Note that in the interaction example the clipboard is not used – that is, nothingwas cut or copied, so the paste command remains disabled

New lines are added just like objects of the type ‘image,’ by clicking on the vant tool button, then clicking in the sandbox area where the new line is to beplaced Double-clicking on a line allows its control points to be edited, as shown

rele-in Figure 16.4 Control porele-ints are moved by draggrele-ing them with the mouse Lrele-ineediting mode is disabled by selecting another object, or by clicking somewhereelse on the sandbox area To add new control points to a line when in edit mode,the mouse right button and the control key are used together

Figure 16.3 Undoing a rotation (Liquid)

Trang 2

16.3 The Sandbox architecture

Now we can look at the implementation of the Sandbox component

Chapter 2 introduced the OOUI approach, mentioning its usefulness both as ameans for designing the GUI, as well as a way to organize the resulting implemen-tation The OOUI approach can be used as a fully-fledged composable unitstrategy2

The software design discussed here is arranged around the development of thead-hoc component using a top down, functional partition Figure 16.5 shows aninitial high-level division of the software design A common approach in manyarchitectures is to separate the domain definition from the application logic – that

is, the functionalities made up of simpler, low-level features that expose thedomain to users3 An e-mail inbox, for example, represents the generic domain of

an archive of e-mails Possible actions in such a domain depend on the purpose ofthe application For example, an inbox component could be used in a customer-care application in which customer complaints are filed with e-mails that can

Figure 16.4 Modifying the control points of a poly-line (Liquid)

2 See Chapter 7

3 From a GUI design viewpoint this reminds us of functional user interfaces – that is, cation GUIs designed around a set of functions

Trang 3

appli-sorted by specific heuristics to extract a tentative subject, category, urgency, and

so on By implication, we are differentiating our ad-hoc component and the set ofelementary functions it provides for interaction with the rest of the world from thecommands a user will employ to interact with it

Following this decomposition, we will build our component around a set ofgeneric, low-level functionalities that in turn will be used by other specializedcode that packs them into higher-level, useful commands for user interaction.Figure 16.6 shows a possible refinement of the initial decomposition of Figure 16.5.The most important part is the ad-hoc component itself, which interacts with thecontrol subsystem that is responsible for maintaining the coherency of the wholeGUI The third division accounts for the commands issued by the user, each partbeing composed from several Java classes This high-level functional divisionfocuses only on the more important portions of the implementation, and inten-tionally omits auxiliary code

The GUI will be designed by refining the simple functional organization shown inFigure 16.6 iteratively This decomposition takes advantage of the peculiarity ofthe application, that of an ad-hoc component as the center of the application, butcan be applied to the development of any GUI

Figure 16.5 An initial high-level functional decomposition

Figure 16.6 Refining the functional decomposition

Trang 4

It would be nice to create a flexible, highly expandable framework, but the key issuehere is to provide a simple, lightweight design Features that are desirable, such asallowing for runtime palette loading – loading new graphical objects from a file andusing them without having to reinstall a new version of the application – can be leftfor future versions.

16.4 The Sandbox component

First we concentrate on building the ad-hoc component, the core of the tion, beginning software design refinement from the ad-hoc component

applica-Top-down refinement of functional organization

Conceptually the Sandbox component is a container of graphical objects Both theobjects and the container itself can issue commands by means of contextualmenus This functionality can be modeled using the Commandable interface fromChapter 15

An initial refinement of the software design for the ad-hoc component in Figure16.6 is shown in Figure 16.7

So far there is the visual container, which we’ll implement as a Java class calledSandboxPanel, and the objects it contains that are manipulated by the user.Different objects will extend a base class, AbstractSymbol

Organizing object communication

How should we organize the communication between the container and its objects?One of our requirements is to guarantee flexibility to the ad-hoc component interms of new object classes that can be used A clean way to obtain this is to dele-gate responsibility to the objects themselves The container merely holds objects

Figure 16.7 From the component to an initial high-level class diagram

Trang 5

and performs some common operations on them Objects are responsible fordrawing themselves on screen, for interacting with the user, and so on Thisapproach offers a great deal of flexibility, allowing for the use of different objecttypes without impacting the container

The container needs access to objects to draw them on the screen Objects in turnnow have many responsibilities delegated to them, rather than keeping them allcentralized Following this strategy, mouse input events, for example, need to bere-issued to the relevant objects, so that the objects themselves can consume themouse event as required, transparently to the container and to other classes.Objects are black boxes as far as the other classes are concerned – the essence ofobject-oriented programming

Suppose that an object is modified and the changes are committed The containerneeds to be notified so that it can refresh the screen to show the changes Decou-pling responsibilities at design time is always a great idea, but generally it needsextra care when it comes to runtime object communication One possible solution

is to adopt the Observable pattern (Gamma et al 1994) This makes the designmore modular – components interested in object behavior just register themselves

as observers – and allows for future expansion, with more sophisticated eventsbeing distributed by more complex objects, while keeping the overall designsimple When we need to make the director class communicate with objects, wecan then just add it as another listener without making any change to the classstructure Figure 16.8 shows this arrangement

Instead of creating our own event classes, we adopt the Observer/Observablemechanism implemented in the java.util package This decision has the prac-tical drawback that the Observable class must be extended, instead of aninterface for objects to be observed, but this is not a problem

Figure 16.8 Making objects communicate with their container

Trang 6

Using the Observer design pattern simplifies class coupling enormously A classinterested in object behavior registers itself as an Observer This application hastwo classes that are interested in monitoring object behavior:

• The graphic container – when an object changes, it is time to refresh the screen

• The director, which manages the coordination among interacting objects

We could have mimicked the Swing API, for example for screen area invalidation,but we prefer to explore and illustrate alternative mechanisms

We avoid an MVC architecture for our ad-hoc component to show that similardesigns can solve the same problems We don’t need the flexibility of multipleviews of the same model, and such a complex arrangement might make it difficult

to focus on the important ideas

The SandboxPanel class

The SandboxPanel class is the graphical container of AbstractSymbol instances

It is implemented as a specialization of the JPanel class, as shown in Figure 16.9

The SandboxPanel class is responsible for showing objects to the user and makingthem available for direct manipulation Apart from this, the graphical container is

Figure 16.9 The SandboxPanel class

Trang 7

essentially a passive object, manipulated by the director class To ease the tion between the two classes we allow the graphical container to invoke thedirector directly.

interac-Objects are drawn on the screen by means of the paint() method, which has beencustomized to support our logical model Interested users can find more informa-tion on the paint() method in (Fowler 2000)

The container scans the ordered list of objects, drawing each of them by invokingtheir draw() method The order of the list defines the graphical layering of theobjects, so that the method putInFront() simply moves the given object to theend of the ordered list, ensuring that it will be painted last and thus lie graphically

‘above’ all the others The graphical container provides other methods for ulating objects, such as remove() for eliminating an object

manip-Another duty of the container is to capture user input by means of mouse eventlisteners, decode it, and submit it to interested components

The mouseClicked() method handles the following events:

• It adds new objects to the graphical container

• It manages double clicks for placing objects into edit mode

• It handles contextual menu pop-up

• It selects objects

• It issues events to the object itself

The mouseDragged method simply reissues the event to the relevant object, so thatthe object can manage the event accordingly

Following a top-down approach, we now turn to the objects themselves At thislevel of detail we manage the AbstractSymbol class as the most general classrepresenting any object able to be added to the container Now we refine the part

of the ad-hoc component that represents objects

The features implemented in this example are chosen for illustrative reasons, andthe application has been simplified in a variety of ways Only single selection isavailable, for example, so its clipboard can contain only one object at a time Thisavoids the implementation of multiple-selection interaction with the mouse,dragging a rectangle on the screen or holding the shift key while selecting moreobjects This could be implemented easily using an array of objects in the clip-board In addition, only two object types are implemented, but the design canallow for a much larger object palette The number of different object classes islimited to focus on the more interesting aspects There are no save or load features,although they could be added relatively easily by means of serialization extended

to all the involved classes There is no way to delete all the objects at once, or tocreate a new, blank sandbox

Trang 8

Graphical objects

The AbstractSymbol class is the most generic object to be contained and ulated by the Sandbox component It represents the behavior of any object thatcan be added to the framework, and is shown in Figure 16.10

manip-Users can select an object, move it within the container, and open it for editing bydouble-clicking on it These features translate into three properties of theAbstractSymbol class shown in Figure 16.10, respectively: selected,editMode,and location There are a small number of methods that apply to any object, bothfor convenience:

• The initializeAt() method for creating an object at a given point

• The contains() method, which verifies whether a given point lies within that object area

and for interacting with the rest of the world:

• The processMouseEvent() method manages mouse input

• The getContextualMenuItems() method implements the Commandableinterface for publishing user commands)

Finally, we need a clone() method for creating new objects

Figure 16.10 The AbstractSymbol class

Trang 9

From a practical viewpoint, the contains() method is used for picking an object,given its screen coordinates This method in turn uses getRectBounds(), so thatsimple-shaped object classes don’t have to implement the contains() method.This is helpful for objects that have a rectangular shape For other objects types, itwill be necessary to override the contains() method When the user clicks on anobject to select it, the container class scans the list of all objects to find the first onewhose contains() method returns true Note that the first one in the list will alsoappear to the user as the visually top-most one.

In real applications when several dozen or more different objects can be employed

in the same palette, it is crucial to design the object class hierarchy carefully, both

to maximize code reuse among different object classes, and to keep the designeasy to maintain and expand in the future Such considerations are out of ourscope here, but (Marinilli 2000) gives more details

The BitmapSymbol class implementation

The bitmap object represents an image in the container, as shown in Figure 16.1 onpage 570 The BitmapSymbol class implements this simple type of object Theclass is shown in Figure 16.11

Figure 16.11 The BitmapSymbol class

Trang 10

The bitmap object exposes the following two commands to the user via thegetContextualMenuItems() method of the Commandable interface:

• Rotate the object, performed by the rotate() method

• Change the bitmap image, implemented by the ChangeImage action class

This is described in The Actions class on page 584.

A default image is used when creating a new bitmap object – see the related code

in the BitmapSymbol class

The draw method is invoked to paint an object onto the screen The image isrotated at the current rotation angle, depending on the rotate commands previ-ously performed on the object

The setImage() accessory method implementation is worth mentioning Afterupdating the internal data with the new bitmap image, the method invokes theobserver/observable mechanism This will invoke all its listeners, giving them achance to react to the event The director checks for logical – business domain –coherence, while the graphical container repaints itself, refreshing the screen withthe new object image

Open-ended communication via events

The BitmapSymbol class is very simple and doesn’t use all the flexibility the designallows The sequence diagram in Figure 16.12 shows a representation of thePoliLine class’ mouseEvent() implementation

SandboxPanel aSymbol

processes the event

processMouseEvent(me)

a new MouseEvent me is sent

to the component

processes the event internally, without the container knowing

Swing framework

Figure 16.12 Objects independently process mouse events

Trang 11

The mouseEvent() method in the PoliLine class implements the GUI interactionstyle used by poly-line objects By dragging the control points that appear whenthe line is being edited, the appearance of the poly-line changes While standarddragging is implemented for all object classes, when moving them around in thecontainer area, this class processes mouse events in a specialized way Adding anew control point to the line is achieved through the same method as is used toprocess mouse events We could also have used a custom action, such as those thatare provided by the Commandable interface.

An abstract poly-line shape, composed of a sequence of lines, is represented as asequence of control points – see the generalPath variable in the source code.Straight lines connecting pairs of control points are drawn directly in the sandboxgraphical container by the draw method If an object is selected, a bounding box isdrawn around it

The container forwards all mouse events to listening objects Objects are selves left with the responsibility of interpreting low-level mouse events, as well

them-as the right-click for contextual menu pop-up The code for commands common

to all objects is kept in the root class of the object hierarchy, AbstractSymbol (seeFigure 16.10) The approach of composing menus is chosen because it leads to asimpler class arrangement, especially as regards visibility Note that our solutiondoesn’t limit the freedom of AbstractSymbols to handle mouse events.Although programmatically feasible, it can be confusing for users to have thesame input event (a mouse right-click) associated with context-dependent actions,such as for example both displaying a contextual menu and modifying controlpoints in a curve

Figure 16.13 shows the sequence diagram for contextual menu composition

4 See Chapter 4 for the GUI design for composing commands, and Chapter 6 for the ware design

Trang 12

soft-The Action framework

The higher-level user commands are shown as one of the three main parts of ourinitial decomposition in Figure 16.6 We used ‘shallow actions’ in the Libraryapplication in Chapter 15 for handling user commands The Sandbox applicationinstead demonstrates the Command pattern at work, employing what we referred

to as ‘deep actions’ in Chapter 6 We defined deep actions as the proper way to useSwing’s Action class to implement the Command design pattern fully

Figure 16.14 restates the Command design pattern from Chapter 6

getContextualMenuItems

getContextualMenuItems

Creates menuItems based on the specific actions

MouseListener:

sandboxPanel

Show the popup menu

Figure 16.13 Creating the contextual menu for an AbstractSymbol instance.

Figure 16.14 The Command design pattern

Trang 13

Using the Swing library, the Invoker is usually a JMenuItem, a JButton instance,

or similar The ConcreteCommand is the command instance that is set up by theClient class, usually the main frame or the director The Receiver is the class thatactually carries out the action execution Following Java conventions, the latterclass implements the ActionListener interface

This approach enables several features, at the affordable price of a little additionalcomplexity The main benefits are:

• The whole implementation becomes closer to the domain representation than with command code centralization

• Behavior specific to a single command is logically localized within an Actionsubclass

• Undo and redo features stem naturally from this approach

• The class organization that results is clearer and more systematic than that when using a centralized mechanism for commands This is especially true for large and complex applications

• This pattern has been adopted extensively in Java APIs, both in Swing and SWT and in other toolkits

On the other hand, such an approach has some drawbacks, and these more evident

in smaller projects Mainly, it produces more and smaller classes, the commandsthemselves This means additional complexity that has to be tamed with extraeffort at design time, primarily with class interaction and management

One solution is to use a static repository, a cluster of many, small classes obtainedstatically at design-time, or a dynamic one, using a runtime container such as ahash table, for example The Sandbox application uses both strategies in theActions class, which acts as a container class to rationalize action-related codemaintenance Action classes are grouped statically in the Actions class as innerclasses, and held at runtime in a collection object managed by the Actions classitself This is an implementation trick to avoid a proliferation of small classes TheActions class is not a proper factory class because it doesn’t create new actions,but merely provides the same action instances to interested clients

The Actions class

The implementation of the Actions class is interesting for several reasons It can

be seen essentially as a static design-time container of actions This arrangementhas been adopted to ease code maintenance, as discussed in Chapter 15, wherethe analogous class was ActionRepository Apart from grouping the code forthe Action instances used in the application, the Actions class serves as adynamic repository as well – that is, live instances are kept in memory at runtime.This is achieved using a hash map that stores the instances of the required actions

Trang 14

The class name string is used to retrieve such instances when they are needed.This type of organization works nicely for this particular application, in which weneed only one instance of each command When new instances are needed, youcan resort for example to the Prototype design pattern (Gamma et al 1994), as we

do for objects

Within each Action subclass is the related Edit inner class, needed for supportingnon-trivial undo/redo operations The Actions class, and some of its innercommand classes, is shown in Figure 16.15

The constructor is kept private to avoid instantiation by other objects, which inturn is done through the init static method The latter method is used to pass theDirector instance needed for correct Actions initialization The constructorinitializes the dynamic repository (the static instance variable map) statically – that

is, it is hardwired into the code The getAction() method is used to query thedynamic repository

Figure 16.15 The Actions class

Trang 15

This class is not a factory class, in that it doesn’t create new object instances, butrather keeps a predefined set of them available to other classes as needed.The Action subclasses used in the simple class framework are subclasses of theUndoableAction class, which in turn specializes Action for undo behavior Weadopt two slightly different designs for the Command pattern: the first one dealswith classes such as ChangeImage or Rotate, while the second arrangement is asimplified version of the first and is discussed later

The ChangeImage class implements the command for placing an image as abitmap object The implementation could have employed a content view of thebitmap object to gather all the properties of the object into one command with asingle dialog, but such an arrangement has already been used in Chapter 6, sowe’ve used a different approach here All the details of the action, such as the icon,the tool-tip, the shortcut, and so on, are prepared in the constructor The heart ofthis class is the actionPerformed() method, which executes the command,manipulating the rest of the application directly as needed

This is a completely different approach than the ‘shallow’ actions illustrated inChapter 6 Here the actionPerformed() method in the Action subclass takescharge of everything needed to carry out the command, in a distributed fashion –that is, the code related to commands is not centralized in one class, but is distrib-uted throughout the related action classes A file chooser dialog is displayed forselecting an image from the file system that is then substituted for the previousimage The old image is not lost, but is kept in the related Edit object in case it isneeded for undoing the action The proper Edit instance is created and stored inthe history of executed commands, administered by the Director class TheChangeImage class also employs an inner class, an Edit subclass, which is in turn

an inner class of the Action class specialized for representing ‘change image’commands

Following the Command pattern, we define a class specialized in handling oneparticular command To fully support undo and redo operations we need a furthercustom class that represents the ‘edits’ obtained by means of the command Boththe Edit and Action subclasses are tightly coupled, so the inner class implemen-tation mechanism is ideal in this case The Edit0 inner class at lines 99–123 in theChangeImage action stores all the information and the code for undoing the masterclass’ command

This elaborate design has several characteristics:

• From an OOP viewpoint it is natural – that is, it stems directly from the ties involved, so that even the resulting static class diagram is expressive, which could be useful if new developers are added to a team

enti-• It is easy to maintain, because the code is gathered systematically in defined areas

Trang 16

well-• It is easy to expand without modifying the existing code

• Drawbacks of static class clustering are scalability, the strain imposed on the runtime class loader when a lot of small objects are loaded, and the runtime occupancy when many such instances are kept in memory throughout an application’s execution

Our design is simplified by the double role we assigned to the Director class Thedirector works as a central access point for the manipulation of the Sandboxcomponent The use of fully-fledged Action classes poses the problem of themany objects that need to be visible to the action itself for ‘doing’ and ‘undoing’its commands This is why we pass the director to the Action class

It is a coincidence that we have just one instance of each action class alive at anytime in the application We could equally have used just one Add class, for example,and slightly modified the dynamic repository by providing two methods, getAc-tion() and createNewAction(), the latter employing the clone() method forcreating new instances from those in the dynamic repository

The design approach described above tends to produce many similar commandclasses To reduce the number of classes, we employ a simplified version of theCommand pattern The simplest commands and their related specialized inneredit classes can be factored out into common classes We adopt this tactic for theAbstractUndoableEdit subclasses, using the Editclass as the commonest editinstance that all other edit classes specialize We are interested in its use forhandling simple commands like cut, paste, and remove

Take the Cut class Here we have a different scheme than the ChangeImage action.The cut command passes the request for command execution to the director We use

a general-purpose edit class, Edit, that is manipulated by the Cut class’ methods.This conservative design spares us code lines and unnecessary complexity We canuse it because of the nature of some of the commands employed – when there aremany simple commands that resemble each other, this arrangement can makesense over the cleaner and more powerful one seen for theChangeImage actionclass

Undo-redo support

Another part of our command framework is undo/redo support We need a class

to store executed commands In the swing undo package these are called edits –

every action performed is stored in a new edit instance for future use5

5 We are only interested here in undo/redo implementation issues For details about GUIdesign issues related to undo support, see (Cooper 2003)

Trang 17

When an object is rotated, for example, the application creates a new Edit objectthat stores the rotate action, together with its argument – the object that has beenrotated If the user asks for the operation to be undone, the Edit instance is picked

up and the undo method of the Rotate action class is invoked, restoring the ation as it was before the command was issued

situ-This scenario is a simple one In real-world cases more sophisticated nisms need to be employed, for example coalescing single small undos intolarger ones, supporting undoable commands by means of specialized excep-tions, and so on

mecha-The Edit class

The Edit class represents the generic ‘executed action’ by the user on the system,and is recorded for future use in undo/redo operations It is implemented as asubclass of the AbstractUndoableEdit class, part of the swing library for undosupport It is shown in Figure 16.16

AnEdit instance stores a command and its argument Such a class can be used intwo ways:

• For instantiating simpler edits, those that take only one object as an argument and that can be un/done by invoking the corresponding command class

• As a base class for creating classes that are specialized for recording and handling more complex edits

Figure 16.16 The Edit class

Trang 18

Recording edits

A few words about a data structure useful for managing undo and redo commandsare appropriate here, but readers not interested in such implementation details canskip this section

For simple undo support there is no problem, because a simple Stack instancecould contain all the edits to be undone When a redo command is to be supported,however, an additional stack can be used in which edits popped from the undostack are pushed onto the redo stack, and vice-versa, or a specialized data struc-ture could be used The following shows a simple implementation of such a datastructure

Suppose the following user commands have been issued:

1 A new object symb1 is added to the Sandbox

2 The object is rotated

3 A new object symb2 is added to the Sandbox

4 The first object is put in front of the others

5 A new object symb3 is added to the Sandbox

6 The previous action (addition of symb3) is undone

7 The previous action (symb1 move front) is undone

8 The redo command is issued, so that the symb1 rotation is restored

Figure 16.17 shows the state of the command history at the end of these interactions

If the user were to issue a new command, say ‘add new object4,’ it would beinserted to the right of the one pointed to by the index value, and the index incre-mented, moving right one position For a redo command, the current index pointerwould be moved to the right, so that it points to the next command to be undone

To keep the data structure to a manageable size and avoid expanding it nitely, a maximum size parameter can be enforced and the oldest edits discarded The source code for this chapter is available in the sandbox package

indefi-Figure 16.17 The CommandHistory implementation using an ordered list

Trang 19

Memory issues

Recording edits using strong references (that is, the usual normal Java ences) can result in a nạve design – memory occupancy grows indefinitely withapplication use, so that sooner or later the heap memory is completely filledwith undo records This is especially true when working with large images This

refer-is just the kind of situation we want to avoid

Limiting the CommandHistory size to keep memory use under control vents this problem, but may limit the user experience – limiting the number ofcommands that can be undone can result in nasty surprises for users

circum-To provide a more flexible mechanism, we could release memory only when it isreally needed The simplest solution for this situation, from a technical viewpoint,

is to use WeakReferences, special references that can be reclaimed by the garbagecollector when the JVM is short of free heap memory The net effect for the user is

to experience a sudden reset of the undo history Using a ReferenceQueueinstance means that the application is notified when the most weakly referencededits are about to be discarded The user can then be informed about what is going

on, for example by providing a pop-up message dialog like the one shown inFigure 16.18

From a technical perspective this solution is quite simple, as the garbage collectortakes charge of all the work From a usability viewpoint, though, this solution couldcause problems, as the memory flushing happens unpredictably and ougtside ofuser control – and it often tends to happen during delicate, complex, and memory-consuming operations It may therefore disrupt the user’s work, and will certainlycreates a vaguely unpleasant feeling

A better solution from a usability viewpoint is to leave the user in control of theapplication This can be done by checking memory occupancy before issuing anundoable command This allows users to decide whether or not to continue withthe operation, reducing the probability of their being trapped in harmful situa-tions In this approach the application might show a dialog like that shown inFigure 16.19 before executing the command

Figure 16.18 Notifying users of undo history (Ocean1.5)

Trang 20

Note that Figure 16.19 has a reassuring Cancel option – even though it is dant, given the No option This design choice is provided deliberately to ease user

redun-comprehension of the GUI in challenging situations when users are not used tothis messages and become anxious about the security of their data Look and feeldesign guidelines, for example those for Java and Apple Macintosh, explicitlydictate that every command has a ‘cancel’ option

This type of preemptive control can be achieved as shown in the following fied code extract:

simpli-if (Runtime.getRuntime().freeMemory()<MINIMUM_THRESHOLD){

int userChoice = requirePermissionForCommand();

if (userChoice==JOptionPane.OK_OPTION) {// execute command without undo support} else {

// return without executing command}

} else {// execute command with undo support}

Solutions that combine several techniques could of course be used For example,

we could use preemptive controls together with a policy of releasing the oldestedits explicitly, by setting their references to null and checking that they are nolonger referenced using a memory profiler

Which solution is best? It depends on the application When developing a cial application in which the effect of every command must be tracked, thepreemptive control shown in Figure 16.19 may be the simplest and most usablesolution In other cases there may be no need to record every operation exactly,and a less intrusive application could be used in which the oldest edits simply

finan-‘slip away’ without users noticing

Figure 16.19 Preemptive control provides less intrusive notification (Ocean1.5)

Trang 21

This is where the director class comes in, and has a twofold purpose Its main duty

is to implement the Mediator design pattern6 for overseeing the interactionamong different parts of the GUI Such a class also turns out to be a good candi-date for centralizing action-related code, which minimizes class coupling Actionsneed only to see the director class, which in turn executes commands by manipu-lating the rest of the GUI This ensures logical coherence, undo/redo support, andalso rationalizes class communication Intuitively, the director class represents the

‘brains’ of the application

Figure 16.20 shows this connection scheme for the Cut action class

6 See Chapter 6

Figure 16.20 The Cut action executes the related command through the director class

Trang 22

In this example we are going to limit control logic to action coherence, as we willsee later in the implementation details.

The director is coupled to the other classes by an event-based mechanism Thedirector listens to object events and acts directly on the container class, as shown

in Figure 16.21

The director also manages the undo/redo support by means of the tory class, as shown in Figure 16.22

CommandHis-Figure 16.21 Making objects communicate with the rest of the world

Figure 16.22 The control framework

Trang 23

The Director class

The Director class implements the Mediator design pattern, which was duced in Chapter 6 It is shown in Figure 16.23 below

intro-To see how this works, suppose the user invokes the cut command:

1 The cut method is invoked

2 The currently-selected object is stored to the clipboard, then removed from the graphical container

3 The whole operation is recorded for undo support

4 The graphical container is informed of the remove operation and updates itself, making the selected object disappear

5 An event is issued to the director to refresh command coherence

Figure 16.23 The Director class

Trang 24

6 If the clipboard was previously empty, after the execution of this command the paste action is automatically enabled.

The sequence diagram in Figure 16.24 illustrates this operation

The director manages the following:

A group of actions, described in Managing actions next.

• The graphical container, the sandbox instance variable

• The internal clipboard, where copied or cut objects are stored

• The set of already-performed actions, for undo/redo support

• The object to be added to the graphical container, if any – when the user clicks the ‘add new object’ button, the director is informed and set to ‘add’ mode

Managing actions

The director is responsible for a group of commands These are initialized bythesetupActions() method common to all directors, and is overridden fromthe AbstractDirector class Such commands are packaged together for use via

Edit:

edit1

CommandHistory: cmdHst01

remove the selected symbol from the container cut()

new

post(edit1)

Figure 16.24 Executing the ‘Cut’ action

Trang 25

the getActionToolBar() method Other commands are out of director’s scope,such as those that are contextual to a specific object Keeping a group of commandscentralized in one class is useful in practice, because it minimizes class couplingand visibility The latter is a classic OOP technique: lessening visibility helps toavoid cluttering the whole design and avoid misuses of public methods byother classes.

The command composition mechanism is implemented with the methods lectedSymbolsMenuItems() and getContextualMenuItems() When the graphiccontainer is queried by the user – when requesting a contextual menu – the director

getSe-is invoked The director merges the commands available from the selected object, if any, with those provided by the container

currently-The director also takes care of the execution of some commands Consider thecopy() method invoked by the Copy action The currently-selected object is

cloned and stored in the clipboard It is then deselected, because the standardclone() mechanism also copied the selected attribute value from the original,selected object, so it needs to be explicitly de-selected Finally a check for logicalcoherence among actions is executed, as discussed in the next section7

Enforcing logical constraints on actions

The checkActions() method maintains coherence among all actions This is aone of the key benefits of the Mediator pattern

This application has three different kinds of constraints on actions:

A group of commands (Move front, Cut, Copy, and Remove) operates on

selections: if no object is selected, these actions cannot be invoked and should

be disabled

• Other action types need a non-empty clipboard: in the Sandbox application

only the Paste action has this constraint.

• Undo/redo commands are available only when an action has been performed (undo) or when at least one old action has been undone (redo).Abstracting from this simple example, the task of centralizing action coherence is

a common one in sophisticated GUIs Enabling or disabling actions following theapplication domain logic is a feature that is often dictated directly by the GUIdesign From an implementation viewpoint, when the number of actions to check

at any one time is non-trivial – perhaps ten or more checks, with related methodcalls and the like – the design suggested in the director class used in the example

7 The example code does not include undo support for copying objects

Trang 26

component needs to be modified One solution is to provide specialized events,and their related listeners, for the various types of constraint to be enforced Thisavoids the frequent computation of a unique, catch-all, expensive, method.Instead, more specialized methods are invoked only when needed This is thesame idea that was used for enhancing the event delivery mechanism of the oldAWT library

As the size of applications grows, the use of a director class becomes more andmore useful You may even end up with one or more specialized classes that aresolely responsible for keeping your many commands logically coherent Thisreduces the maintenance costs associated with dispersing control code locallythroughout the various widgets’ listeners

We are now ready to see all the different parts put together in the final application

16.7 The whole picture

Figure 16.25 shows the overall static class diagram of the Sandbox application

We have finished the iterative refinement steps, and the application is ready torun But what about the top-down approach initially adopted to organize thedesign? Figure 16.26 shows how the final classes match the initial functionalpartition

Figure 16.25 The static class diagram of the Sandbox application

Trang 27

Gray classes are not part of our framework Note that the only class not contained

in any of the three parts devised at the beginning is the Main class containing thewindow and the main method

The preliminary high-level partition shown in Figure 16.6 on page 574 has beenrespected almost completely, apart from the communication flows among differentfunctional parts

16.8 Stressing the software design

At this point, some authors would hastily conclude the chapter, perhaps chantingthe many virtues of design patterns, or exhorting readers to tweak the proposedsource code Instead, we take a different path, one rarely taken in technical publi-cations in which only perfect – or supposedly perfect – solutions are presented

We already know that our design is too simple and limited to deal effectively with

features like extensibility and flexibility What we still don’t know is where its

major weak points are

There is nothing wrong with code that works well Code organization can of course

be improved by refactoring, or by more painful design restructuring, but the realtest of a design – the chances of making it better, or the risk of degradation – comeswhen changes are needed Below we briefly analyze how our ‘toy’ Sandbox designreacts to change

Figure 16.26 The static class diagram mapped to the functional partition

Trang 28

Adding objects and commands

The first experiment is to see what happens to the design when new commands

such as Print or Save are added to the application Given the design, the

minimum set of steps needed to, say, add a new object class to the Sandbox cation are:

appli-1 Create a new action class, usually by extending UndoableAction containing the code for the relevant command

2 Enlist the new action class in the dynamic repository held by the Actionsclass

3 Register the new action class semantically within the Director instance This

in turn involves two steps: (i) Attaching the command to the toolbar, to make

it available to users, and (ii) Writing the code that determines how the new command is going to interact with the rest of the application

A further interesting expansion is the addition of new objects, such as text, box, orellipse, for example This involves defining a new object class, and the creation of

an action for adding such objects to the drawing The minimal steps we need toadd a new object class to the Sandbox application are:

1 Create the new class that implements the object, extending AbstractSymbol

or some of its subclasses

2 Make a new action class for creating and adding objects of the new type to drawings

3 Register the new action class in the dynamic repository held by the Actionsclass

4 Make the director provide the new ‘create object…’ action to the rest of the application by attaching it to the toolbar

5 Modify the Director class to handle the intended interactions and control for the new object

The addition of a new object type to the design of the Sandbox application,excluding its special commands, therefore involves the creation of a new class,and adding code to other two classes, Actions (to add the ‘create new object’command) and Director The latter dependency is suspect, because the ‘createnew object’ action doesn’t involve any special control from the Director.The point is that we gave the simple Director class the responsibility of creating

all the commands, not only those that need centralized control, such as Copy or

Undo This may become a problem as the number of ‘not controlled’ actions

outnumbers the controlled ones – the Director class may become cluttered withcode unrelated to interaction control Such code can be factored out in, forexample into a SymbolPalette class, that is provided with mechanisms forsemantic registration of specific actions with the director

Trang 29

The design’s weak points

Adding new objects or commands is relatively easy: the real trouble starts when

we need to implement ‘horizontal’ features that might affect many existingclasses in unforeseen ways Adding zoom support, for example, could impactthe whole design deeply The effect of implementing other features might bemore circumscribed, such as multiple views of the same drawing, for example,

by adopting a fully-fledged MVC approach, or the ability to group and ungroupobjects

The relative complexity of just adding a new command to the application shouldring alarms that suggest closer inspection

Our director implementation seems to be centralizing some of other classes’responsibilities too much, such as command execution and command status (that

is, whether a command should be enabled or disabled given specific externalstates) This stems directly from the limited use of events in our design – commandclasses delegate the execution of actions to the director This simplifies manythings initially, such as control: the director pulls together all the data needed toexecute and control actions

However, such a design is too tightly coupled to be maintainable in even a simplereal-world situation in which tens of commands to be managed: following such acentralized design in the evolution of an application will lead to a large, convo-luted Director class with a myriad of responsibility-free little classes delegating to

it To use a colorful metaphor, it’s like feeding a baby monster, currently so smalland cute that we don’t realize its intrinsically evil nature that, once big enough to

be out of control, will devour us

Despite that it seems that sound design patterns were adopted, and what happened

in reality was a misuse of them For example, controlling whether the Cut action

should be disabled could be done within the Command pattern approach in adecentralized fashion by letting the Cut action listen to clipboard events and enable

or disable itself accordingly

There is another, subtler issue with the proposed design Commands in the designaffect at least three classes: the Action subclass, the Actions repository that isqueried for new commands, and the Director that executes the command itselfand takes care of maintaining coherence among the actions and objects contained

in the sandbox container This can be seen as a simple, currently harmless lack ofdecoupling in the design This is a well-known aspect of software Orthogonalsystems, using (Hunt and Thomas 2000) terminology, are those systems in whichthere are no ‘effects between unrelated things.’ Perhaps this is exaggerating a

little, but if it is necessary to modify the implementation of the Save command to

provide a new file format, for example, why should it also be necessary to studythe implementation of three other unrelated classes as well?

Trang 30

16.9 Introducing JHotdraw

JHotDraw is a Java GUI framework for technical and structured graphics that isderived from an initial Smalltalk design by Ward Cunningham and Kent Beck Ithas been developed as a ‘design exercise’ in Java by Erich Gamma and ThomasEggenschwiler, and has a design that relies heavily on standard design patterns

It is freely available at http://www.jhotdraw.org

We introduce this framework as a gallery of interesting design solutions to theproblems found with our simple Sandbox application It would make little sense

to compare our toy application, with its ten classes, with such a framework, whichhas more than 180 classes, the many systems developed using it, and the manyexperienced designers who have worked on its design, but nevertheless its intro-duction may help further discussions

The class diagram in Figure 16.27 shows only the interfaces for clarity, not the realimplementations The concrete class structure is made up of implementationclasses for the interfaces shown in the following diagrams

A framework is a reusable, ‘semi-complete’ application that can be specialized

to produce custom applications (Johnson 1998)

Frameworks are different than usual class libraries, even though sometimesthe term is also used for complex class libraries OOP frameworks usuallyrepresent a domain of interest (for example insurance) or application area (forexample GUI toolkits) with OOP technology, with the explicit aim of beingreused Frameworks are intended to be reused, with client code extendingthem and make them concrete as required, and often define particular controlflows such as inversion of control, and other devices for reuse such as hookmethods, base types to be extended by client code, and so on

Figure 16.27 The main core class diagram of the main classes of the JHotDraw

framework

Ngày đăng: 12/08/2014, 23:23