CSc 668/868: Object Oriented Programming
© 2008 by Dr. Barry Levine
1
TOPICS .................................................................................................................................................................. 8
Note on Group Work ...................................................................................................................................... 11
CSC 868 Oral Presentations ......................................................................................................................... 12
AN INTRODUCTION TO OBJECT-ORIENTED ANALYSIS AND DESIGN................................................... 14
MACRO LEVEL PROCESS ..................................................................................................................................... 21
OBJECT-ORIENTED ANALYSIS ............................................................................................................................. 22
OBJECT-ORIENTED DESIGN ................................................................................................................................. 22
IMPLEMENT A STORE/POST (POINT OF SALE TERMINAL) SYSTEM ............................................................ 23
POST .................................................................................................................................................................. 24
DATABASES ......................................................................................................................................................... 25
OBJECT-ORIENTED ANALYSIS ............................................................................................................................. 26
CONCEPTUAL MODEL FOR POST (OBJECTS FOUND IN THE REAL WORLD – NOUNS DRAWN FROM THE
REQUIREMENTS/SPECIFICATION) .......................................................................................................................... 28
OBJECT-ORIENTED DESIGN ................................................................................................................................. 30
HIGH-LEVEL STAGES IN THE STORE/POST SOFTWARE SYSTEM: ....................................................................... 31
POST ASSIGNMENTS........................................................................................................................................... 32
SAMPLE FILE I/O API .......................................................................................................................................... 34
USE CASE VIEW .................................................................................................................................................. 38
LOGICAL VIEW .................................................................................................................................................... 38
RELATIONSHIPS ................................................................................................................................................... 38
COMPONENT VIEW .............................................................................................................................................. 39
SOME NOTES ON JAVA ...................................................................................................................................... 40
THIS IN JAVA........................................................................................................................................................
40
this Used in Method Calls.............................................................................................................................. 41
ENUMERATED TYPES AND TYPE SAFETY ............................................................................................................ 42
ARCHITECTURAL DESIGN (APPENDIX 2) ..................................................................................................... 43
ARCHITECTURAL PATTERNS ................................................................................................................................ 43
Model-View-Control ...................................................................................................................................... 43
Client-Server .................................................................................................................................................. 43
Reflection ....................................................................................................................................................... 44
Layers ............................................................................................................................................................ 44
PACKAGE STRUCTURES ....................................................................................................................................... 44
JAVA'S REMOTE METHOD INVOCATION - RMI........................................................................................... 46
DISTRIBUTED GARBAGE COLLECTION ................................................................................................................. 49
OBJECT REGISTRATION ....................................................................................................................................... 49
RMI EXAMPLE - ACCOUNTS USED BY ATM'S ..................................................................................................... 49
Steps to run this example (JDK 1.5 on Windows XP): ................................................................................ 53
RMI EXAMPLE - REMOTE METHODS RETURNING REMOTE OBJECTS .................................................................... 55
Steps to run this example (JDK 1.5 on Windows XP): ................................................................................ 59
RMI CLIENT-SERVER IMPLEMENTATION OF THE POST PROBLEM ..................................................... 62
MAIN OBJECTS SHIPPED FROM SERVER (STORE) TO CLIENT (POST) .................................................................. 62
MAIN OBJECTS SHIPPED FROM CLIENT TO SERVER ............................................................................................. 62
OBJECTS ON CLIENT (POST) ............................................................................................................................... 63
OBJECTS ON SERVER (STORE) ............................................................................................................................. 63
COMMENTS ......................................................................................................................................................... 63
OBJECT ORIENTED PROGRAMMING PRINCIPLES .................................................................................... 64
DOCUMENTATION................................................................................................................................................ 64
2
MESSAGE PASSING VERSUS FUNCTION CALLS ...................................................................................................... 64
QUALITY OF AN INTERFACE ................................................................................................................................. 65
INHERITANCE (TYPE/SUBTYPE RELATIONSHIP)..................................................................................................... 67
SUBCLASSING HEURISTICS ................................................................................................................................... 67
SOFTWARE REUSE ............................................................................................................................................... 70
SUBCLASS/SUBTYPES .......................................................................................................................................... 71
POLYMORPHIC VARIABLES .................................................................................................................................. 72
REVERSE POLYMORPHISM ................................................................................................................................... 72
REPLACEMENT VS REFINEMENT .......................................................................................................................... 74
IMPLEMENTATION ISSUES .................................................................................................................................... 76
COVARIANCE AND CONTRAVARIANCE (OVERRIDING METHODS) ......................................................................... 78
COVARIANT RETURN TYPES ................................................................................................................................ 78
MULTIPLE INHERITANCE ..................................................................................................................................... 79
POLYMORPHISM (DEALS WITH TYPES) ................................................................................................................. 80
FORMS OF POLYMORPHISM ................................................................................................................................. 80
VISIBILITY AND DEPENDENCE ............................................................................................................................. 82
COUPLING AND COHESION................................................................................................................................... 82
LAW OF DEMETER ........................................................................................................................................... 85
OVERRIDING VERSUS SHADOWING ...................................................................................................................... 87
OVERRIDING: ACCESSIBILITY AND EXCEPTIONS .................................................................................................. 88
A NOTE ON PROTECTED VARIABLES AND METHODS ........................................................................................... 90
FRAMEWORKS AND PATTERNS ............................................................................................................................ 91
ANOTHER LOOK AT CLASSES .............................................................................................................................. 92
OOP AND SOFTWARE ENGINEERING ............................................................................................................ 93
ABSTRACTION ................................................................................................................................................ 93
INFORMATION HIDING ................................................................................................................................. 93
ABSTRACTION AND INFORMATION HIDING ........................................................................................... 94
LOCALIZATION ............................................................................................................................................... 94
SOFTWARE ENGINEERING CONCEPTS......................................................................................................... 95
SURFACE AREA............................................................................................................................................... 95
FACTORS INFLUENCING SURFACE AREA................................................................................................. 95
HARDWARE IC'S: ............................................................................................................................................. 95
SOFTWARE IC'S: .............................................................................................................................................. 95
SOFTWARE QUALITY .................................................................................................................................... 96
MODULES ......................................................................................................................................................... 97
TOP-DOWN VS OBJECT-ORIENTED DESIGN ............................................................................................. 98
UNIT TESTING ..................................................................................................................................................... 99
SQUEAK/SMALLTALK - 80 ............................................................................................................................... 101
IDENTIFIERS: ..................................................................................................................................................... 102
MESSAGE EXPRESSIONGS .................................................................................................................................. 102
SELF................................................................................................................................................................. 103
CLASS VARIABLES............................................................................................................................................. 104
GENERATORS .................................................................................................................................................... 105
CLASSES AND METACLASSES ................................................................................................................... 107
REFLECTION AND PERSISTENCE (APPENDIX 5) ...................................................................................... 116
REFLECTION ...................................................................................................................................................... 116
REFLECTIVE SYSTEMS ....................................................................................................................................... 116
STRATEGIES....................................................................................................................................................... 119
Static Structure ............................................................................................................................................ 120
RUNTIME CLASSES ............................................................................................................................................ 121
3
Runtime Type Information in Java .............................................................................................................. 122
DYNAMIC INSTANTIATION ................................................................................................................................. 123
Dynamic Instantiation in Java ..................................................................................................................... 124
Dynamic Instantiation in C++ (The Prototype Pattern) ............................................................................. 124
PERSISTENCE ..................................................................................................................................................... 126
Databases .................................................................................................................................................... 126
DATA STREAMS ................................................................................................................................................. 129
Output Streams ............................................................................................................................................ 129
Input Streams ............................................................................................................................................... 130
OBJECT STREAMS .............................................................................................................................................. 130
DATABASES ....................................................................................................................................................... 136
Connection ................................................................................................................................................... 137
Statements .................................................................................................................................................... 137
Result Set ..................................................................................................................................................... 138
PROGRAMMING AND DESIGN PRINCIPLES ............................................................................................... 140
CHALLENGES IN MODERN PROGRAMMING ......................................................................................................... 140
Coupling and variabilities ........................................................................................................................... 140
WHAT IS A GOOD DESIGN? ................................................................................................................................. 140
DOCUMENTING POLICY...................................................................................................................................... 140
OBJECT ORIENTED ANALYSIS AND DESIGN ....................................................................................................... 141
EVENT NOTIFICATION (APPENDIX 3) ......................................................................................................... 144
OVERVIEW ........................................................................................................................................................ 144
DESIGN PATTERNS ............................................................................................................................................ 144
THE PUBLISHER-SUBSCRIBER PATTERN ............................................................................................................ 146
Dynamic Structure ....................................................................................................................................... 147
The Publisher-Subscriber Pattern in Java................................................................................................... 148
Example: Monitoring Devices (continued) .................................................................................................. 148
JAVA FOUNDATION CLASSES (JFC) ................................................................................................................... 151
CONSTRAINT NETWORKS .................................................................................................................................. 153
PROGRAMMING NOTE ....................................................................................................................................... 157
SOME IMPORTANT DESIGN PATTERNS ...................................................................................................... 160
MODEL-VIEW-CONTROLLER ............................................................................................................................. 160
ADAPTOR PATTERN ........................................................................................................................................... 160
CHAIN OF RESPONSIBILITY PATTERN ................................................................................................................. 160
DECORATOR PATTERN ...................................................................................................................................... 160
OBSERVER PATTERN ......................................................................................................................................... 160
PROXY PATTERN ............................................................................................................................................... 161
STRATEGY PATTERN ......................................................................................................................................... 161
STREAMS PATTERN (PIPES AND FILTERS, DATAFLOW) ...................................................................................... 161
REFERENCES ..................................................................................................................................................... 161
REFACTORING TO PATTERNS ....................................................................................................................... 162
1. COMPOSE METHOD REFACTORING ................................................................................................................ 162
Example ....................................................................................................................................................... 162
Benefits and Liabilities ............................................................................................................................... 164
2. REPLACE CONDITIONAL LOGIC WITH STRATEGY ........................................................................................... 165
Example ....................................................................................................................................................... 165
Benefits and Liabilities ................................................................................................................................ 168
3. REPLACE CONDITIONAL DISPATCHER WITH COMMAND ................................................................................. 169
Example ....................................................................................................................................................... 169
Benefits and Liabilities ................................................................................................................................ 172
4. REPLACE TYPE CODE WITH CLASS ................................................................................................................ 173
4
Example ....................................................................................................................................................... 173
Benefits and Liabilities ................................................................................................................................ 174
FRAMEWORKS. TOOLKITS, AND POLYMORPHISM (APPENDIX 4)....................................................... 175
OVERVIEW ........................................................................................................................................................ 175
FRAMEWORKS ................................................................................................................................................... 175
Example: Application Frameworks ............................................................................................................. 176
Example: Client-Server Frameworks .......................................................................................................... 176
OBJECT-ORIENTED CONCEPTS .......................................................................................................................... 177
Polymorphism .............................................................................................................................................. 177
Abstraction .................................................................................................................................................. 178
WORKING WITH UNKNOWN CLASSES ................................................................................................................ 181
MFW: A Framework for Music Applications .............................................................................................. 182
Heterogeneous Collections .......................................................................................................................... 182
Virtual Factory Methods.............................................................................................................................. 183
FACTORIES IN JAVA ........................................................................................................................................... 185
Inner Classes ............................................................................................................................................... 185
Local Classes ............................................................................................................................................... 186
Anonymous Classes ..................................................................................................................................... 186
Closures, Functors, and Thunks ................................................................................................................ 187
POWER TYPES AS FACTORIES ............................................................................................................................ 189
TOOLKITS .......................................................................................................................................................... 191
Generic Methods ......................................................................................................................................... 193
Singletons .................................................................................................................................................... 194
EXAMPLE: A SIMPLE APPLICATION FRAMEWORK .............................................................................................. 195
Calculator .................................................................................................................................................... 195
THE CONSOLE FRAMEWORK ............................................................................................................................. 196
EXAMPLE: PIPELINES ......................................................................................................................................... 199
PIPES: A Pipeline Toolkit .......................................................................................................................... 202
PROGRAMMING NOTES ...................................................................................................................................... 207
Stereotypes ................................................................................................................................................... 207
The Java Collections Framework ................................................................................................................ 208
MORE ON DESIGN PATTERNS ....................................................................................................................... 209
ADAPTOR PATTERN ........................................................................................................................................... 210
FAÇADE PATTERN ............................................................................................................................................. 212
COMPARISON BETWEEN FAÇADE AND ADAPTOR PATTERNS .............................................................................. 213
ENCAPSULATION ............................................................................................................................................... 214
BRIDGE PATTERN .............................................................................................................................................. 216
COMPOSITE PATTERNS ...................................................................................................................................... 218
DELEGATION (APPENDIX 6) .......................................................................................................................... 219
OBJECTS AS STATES .......................................................................................................................................... 219
DELEGATION DEFINED ...................................................................................................................................... 221
Adapters ....................................................................................................................................................... 221
HANDLE-BODY IDIOMS ..................................................................................................................................... 223
Shared Bodies .............................................................................................................................................. 224
PRESENTATION AND CONTROL (APPENDIX 7) ......................................................................................... 228
THE MODEL-VIEW-CONTROLLER ARCHITECTURE ............................................................................................ 228
Static Structure ............................................................................................................................................ 229
GUI TOOLKITS .................................................................................................................................................. 230
Views and View Notification ........................................................................................................................ 230
Example: MFC's Document-View Architecture (Develop word processor) ................................................ 231
BUILDING AN APPLICATION FRAMEWORK (AFW) ............................................................................................. 234
5
AFW 1.0: A CUI APPLICATION FRAMEWORK ................................................................................................... 234
Design .......................................................................................................................................................... 234
Implementation ............................................................................................................................................ 235
Example: Account Manager ........................................................................................................................ 239
AFW 2.0: A GUI APPLICATION FRAMEWORK WITH MULTIPLE VIEWS .............................................................. 241
Design .......................................................................................................................................................... 241
Implementation ............................................................................................................................................ 241
COMMANDS AND COMMAND PROCESSORS ....................................................................................................... 246
AFW 3.0: A GUI APPLICATION FRAMEWORK WITH A COMMAND PROCESSOR ................................................. 248
DESIGN .............................................................................................................................................................. 248
Implementation ............................................................................................................................................ 249
EXAMPLE: POLYGON VIEWER CUSTOMIZATION OF VERSION 3.0 OF AFW ........................................................ 257
Design .......................................................................................................................................................... 260
Implementation ............................................................................................................................................ 260
MEMENTOS ....................................................................................................................................................... 262
Static Structure ............................................................................................................................................ 262
RESOURCE MANAGERS ..................................................................................................................................... 263
INTEGRATING PATTERNS IN THE DESIGN OF A HIERARCHICAL FILE SYSTEM ............................ 265
FOUR MAIN BENEFITS OF PATTERNS:.................................................................................................................. 265
GOALS FOR THE SYSTEM: .................................................................................................................................. 265
APPLICATION OF THE COMPOSITE PATTERN ...................................................................................................... 266
Intent of the Composite Pattern: .................................................................................................................. 268
Applicability Section - use Composite when ................................................................................................ 268
Directory...................................................................................................................................................... 269
Include a mkdir command ........................................................................................................................... 269
mkdir version 1 - problems: ......................................................................................................................... 270
mkdir version 2 - simplification via uniform treatment: .............................................................................. 271
mkdir version 3 - use of downcasting: ......................................................................................................... 272
ADDING SYMBOLIC LINKS: PROXY PATTERN .................................................................................................... 273
FILE SYSTEM CLASS STRUCTURE WITH COMPOSITE AND PROXY ...................................................................... 275
VISITOR PATTERN ............................................................................................................................................. 276
Visitor Code With No Common Operation Behavior................................................................................... 277
Visitor Code With Common Operation Behavior ........................................................................................ 279
Modifications to Code Using Visitor ........................................................................................................... 280
Visitor Benefit .............................................................................................................................................. 280
DELETING FILES AND DIRECTORIES WITHIN A SINGLE USER PROTECTION FRAMEWORK (E.G. SINGLE USER ON A
PC, NOT A MULTI-USER SYSTEM UNDER E.G. UNIX) - THE TEMPLATE PATTERN ............................................... 281
SUMMARY OF DESIGN PATTERNS USED IN THE FILE SYSTEM DESIGN ............................................................... 285
ACTIVE AND DISTRIBUTED OBJECTS (APPENDIX 8) .............................................................................. 286
MULTI-THREADING ........................................................................................................................................... 286
IMPLEMENTING AND SCHEDULING THREADS ..................................................................................................... 286
INTER-THREAD COMMUNICATION ..................................................................................................................... 287
ACTIVE OBJECTS ............................................................................................................................................... 287
THE MASTER-SLAVE DESIGN PATTERN............................................................................................................. 287
JAVA THREADS ................................................................................................................................................. 288
SCHEDULING ..................................................................................................................................................... 289
Thread States ............................................................................................................................................... 289
Preemptive vs. Nonpreemptive Scheduling .................................................................................................. 289
EXAMPLE: BOUNCING BALLS ............................................................................................................................ 289
PRODUCER-CONSUMER PROBLEMS ................................................................................................................... 293
Example: A Joint Checking Account Simulation ......................................................................................... 293
MONITORS......................................................................................................................................................... 296
DIRECT COMMUNICATION ................................................................................................................................. 299
6
Example: A Date Client ............................................................................................................................... 300
A SERVER FRAMEWORK.................................................................................................................................... 301
Design .......................................................................................................................................................... 301
Implementation ............................................................................................................................................ 301
Example: A Command Server Framework .................................................................................................. 302
INDIRECT COMMUNICATION .............................................................................................................................. 304
Proxies ......................................................................................................................................................... 305
CONCLUSION ..................................................................................................................................................... 308
DOCUMENTATION AND CODING STANDARDS ......................................................................................... 309
SAMPLE CODING STANDARDS ........................................................................................................................... 311
SUMMARY OF NOTES MENTIONED IN THE STRING CLASS .................................................................................. 315
MISCELLANEOUS ISSUES ................................................................................................................................... 316
SWITCH. ...................................................................................................................................................... 316
TRY/CATCH/FINALLY.:.............................................................................................................................. 316
GENERAL COMMENTS: ...................................................................................................................................... 316
Names: ......................................................................................................................................................... 316
GENERAL PROGRAMMING ................................................................................................................................. 317
LAYOUT OF SOURCE FILES (*.JAVA).......................................................................................................... 317
SCRUM: AN EMPIRICALLY-BASED PROCESS FOR SOFTWARE PROJECT MANAGEMENT ............. 319
PRINCIPLES BEHIND THE AGILE MANIFESTO ...................................................................................................... 319
OVERVIEW ........................................................................................................................................................ 320
SCRUM SUMMARY ............................................................................................................................................ 323
SCRUM COMPONENTS AND PROCESSES– DEFINITIONS AND FUNCTIONALITY .................................................... 324
SCALABILITY ..................................................................................................................................................... 352
MANAGEMENT ISSUES....................................................................................................................................... 353
SCRUM NOTES FOR 668/868 .............................................................................................................................. 354
WEEKLY DELIVERABLES (DUE EACH FRIDAY)................................................................................................... 358
SQUEAK SMALLTALK EXERCISE .................................................................................................................. 359
PROJECT INFORMATION ................................................................................................................................ 361
PROJECT MILESTONES (DELIVERABLES INCLUDE MOCKUPS, SPECS, ARCHITECTURE DIAGRAMS AS DETERMINED
BY THE INDICATED MILESTONE) ......................................................................................................................... 362
FINAL PROJECT DELIVERABLES ......................................................................................................................... 362
7
Object-Oriented Programming
CSc 668/868
Dr. Barry Levine
Thornton 949, x81661
Course Number: CSC 668/868
Course Title: Advanced Object Oriented Software Design and Development
Number of Credits: 3
Schedule: Three hours of lecture/discussion per week.
Prerequisite: Senior or graduate standing, and at least a C grade in CSC 413, or consent
of instructor.
Catalog Description
Basic principles of object oriented analysis and design utilizing UML, advanced object oriented
programming principles, design patterns, frameworks and toolkits; Agile software design
processes. Development of a mid-size programming project working in teams. Paired with
CSC 868. Students completing this course may not take CSC 868 later for credit. Extra fee
required.
Topics
1. An Introduction to Object-Oriented Analysis and Design
2. Some Notes on Java
3. Architectural Design
4. Java's Remote Method Invocation – RMI
5. Object Oriented Programming Principles
6. OOP and Software Engineering
7. Software Engineering Concepts
8. Unit Testing
9. Squeak/Smalltalk – 80
10. Reflection and Persistence
11. Programming and Design Principles
12. Event Notification
13. Design Patterns
14. Refactoring
15. Frameworks. Toolkits, and Polymorphism
16. Delegation
17. Presentation and Control
18. Active and Distributed Objects
19. SCRUM: An Empirically-Based Process for Software Project Management
20. Documentation and Coding Standards
Course Objectives and Role in Program
The prime objective of the course is to teach the student to analyze, design and implement
object-oriented software systems by means of a mid-sized project. Students will learn the
8
application of software architectures in various settings, including the application of design
patterns, frameworks and toolkits. The ability to architect software systems is basic to all
subsequent courses in which software development is an integral part. In addition, many of the
concepts facing the prospective developer in modern software technology, such as Refactoring
and team-oriented Agile software development processes are examined in detail and integrated
into general software practice.
Learning Outcomes
At the end of the course students will be able to
 Utilize processes and artifacts to work effectively in a team-oriented development
environment
 Apply various software architectures, including frameworks and design patterns, when
developing software projects
 Develop Smalltalk applications
 Program distributed applications in a Java environment
 Effectively construct medium-sized object-oriented programs.
Project
Student teams will propose and develop a medium-sized project of their own.
Project Meetings
We will have ongoing meetings to discuss the term project - you will bring project
documentation to these meetings (class diagrams, etc.) so I can assess your design efforts and
relevancy to your proposal
Software
Squeak Smalltalk (free) is available for download (see ilearn)
Java is available on the PC’s (free) – you MUST use an IDE such as Netbeans
Grading (approximate)
CSc 668
Labs........................50%
Class Participation.....5%
Term project............45%
CSc 868
Labs........................40%
Class Participation.....5%
Term project...........40%
Presentation............15%
Note: Students enrolled in CSc 868 are required to provide a presentation of a course-related
article found in the current Computer Science literature. All students are required to be present
at the 868 presentations. Up to 10% of the lab points will be deducted from the grade of
students who do not attend the presentations.
Texts
Squeak: Object-Oriented Design with Multimedia Applications, Mark Guzdial, Prentice Hall,
2000
9
Applying UML and Patterns - an Introduction to Object-Oriented Analysis and Design by
Craig Larman, Prentice Hall , [1998] (Optional)
Core Java 2 - Volume II-Advanced Features (5th Edition) by C. Horstmann & G. Cornell,
Prentice Hall, [December 10, 2001] (Optional)
Recommended References:
Design Patterns, Elements of Reusable Object-Oriented Software, Erich Gamma, Richard
Helm, Ralph Johnson, and John Vlissides, Addison Wesley, Reading, MA. 1995.
 "Software Patterns", Communications of the ACM, October, 1996.
 Refactoring to Patterns, Joshua Kertevsky, Addison-Wesley 2005
 Design Patterns Explained: A New Perspective on Object-Oriented Design, A.
Shalloway & J. Trott, Addison-Wesley, 2002
 Pattern Hatching Design Patterns Applied, J. Vlissides, Addison-Wesley, 1998
 Agile Software Development with Scrum, Ken Schwaber & Mike Beedle, Prentice-Hall,
2001
 IEEE Computer, Special Issue on Agile Software Development, June 2003
 Refactoring; Improving the Design of Existing Code, Martin Fowler, Addison-Wesley,
2000
 Framework-Based Software Development in C++, Gregory F. Rogers, Prentice Hall,
1997
10
Note on Group Work
In most cases programmers work in groups (for better or worse) so it's important that you check
out and learn about group dynamics while in school...in fact, many CS programs require
students to work in groups in most courses.
There are many recipes for success/failure that you should learn. Therefore, if you are
struggling with your group during the term I EXPECT you to check things out with me
BEFORE you reach an untenable situation - be proactive!
I will act as facilitator, not judge. My goal will be to help you resolve issues so you can
continue in a productive manner.
Again, SEE ME AS SOON AS POSSIBLE IF YOU ARE ENCOUNTERING GROUP
PROBLEMS.
In summary,

All group members are expected to contribute to the best of their abilities

Each group member MUST participate in teamwork and group work, attend meetings
and be courteous and responsible

All group members get the same grade UNLESS the instructor is told or discovers that
somebody is not contributing fully
You will select a group leader for each group
Role of group leads
 Organize and schedule meetings
 Single point of contact with instructor
 Help resolve issues
 Can request meeting/input from instructor
 Participation in creating deliverables (a bit reduced for group lead tasks)
 Responsible for submitting documents, as described at the end of the Reader
11
CSC 868 Oral Presentations
The additional requirement for people enrolled in CSc 868 is to present a review of an article
found in the Computer Science literature on Object-Oriented Programming. You should peruse
the literature in the library (SFSU library or UC Berkeley library or...) to determine a topic you
are interested in studying. Then bring a copy of the article to me for PRIOR approval. You
will present your review to the class during a regular meeting. We will agree upon a time for
presentation. The presentation should last approximately 30 minutes (the time will depend on
the article; in any case, you should practice your lecture to ensure that it doesn't exceed 25
minutes. This is to allow for any questions). I will give suggestions for each presentation.
Library references are:
- ACM SIGPLAN (especially the conference proceedings on object-oriented programming).
- IEEE Software
- Software: Practice and Experience
- IEEE Transactions on Software Engineering
- Communications of the ACM
- Computer Language
- The Journal of Systems and Software
Unlike the 868 programming assignments, the reviews will be done on an individual basis.
You are required to receive my approval on your proposed review ... (be sure to plan ahead
since it is possible that I will not approve your initial request).
BE SURE TO PREPARE TRANSPARENCIES/POWERPOINT PRESENTATION FOR
YOUR LECTURES. USE EXAMPLES AND PICTURES.
DO NOT DO NOT DO NOT PRESENT YOUR MATERIAL BY READING FROM YOUR
SLIDES!!!
DO NOT BRING ANY NOTES...YOUR PRESENTATION SHOULD FOCUS ON YOUR
SLIDES (people have a tendency to use notes as a crutch and simply read from them)
Of course, if you spend most of the time reading from your slides then it will not be enjoyable
nor very interesting for the audience. Please see me for any suggestions.
12
After presenting a complete reference to your article you should present a coherent outline of
the presentation (frequently refer back to this outline to help the audience follow the flow of
your talk). You should give an introduction, overview of your presentation,
then the main body of your talk and end with any conclusions.
BE SURE TO USE PICTURES AND EXAMPLES to help the audience understand
your talk.
The weight of the review is approximately 15% of your grade for 868. In any case, if you do
not present a review then you will not receive a grade higher than a "C" for the course.
After I approve your article you need to send me the following information:
Your name,
Title, author(s), date of pub, where published
13
An Introduction to Object-Oriented Analysis and Design
Plan/Elaborate
Preliminaries
Build
Thorough
analysis/design/
construction
Deploy in field
 - testing,
delivery to
customers
Macro Process
14
Plan/Elaborate
Schedule resources, budget, etc.
Preliminary investigation report
Requirements spec
Glossary of terms
Prototype (optional)
Investigate some use cases
Build: repeated (iterative) development cycles
Refine plan derived during plan/elaborate
Analyze system - refine use cases, define
significant
use cases
Design - construct other artifacts dealing with
software system
Construct - translate into software
Test
Deploy in field
 - testing, delivery to customers
Macro Process: Expanded
15
Ongoing: Synchronize
artifacts as system
evolves
OOA/D
Requirements Analysis
Domain Analysis
Responsibility Assignments
Associated Documents
Use Cases
Conceptual Model
Design class diagrams
Interaction design
Collaboration diagrams
Consider a Dice Game
Scenario
Use Case: Play a game
Actors: Player
Description: Player rolls dice, win on 7 else lose
Player
1
Rolls
2
name
Die
faceValue
1
2
(# Die)
Plays
1
Dice Game
1
(# games)
Includes
association
Conceptual Model (actors, associations, cardinalities)
16
play()
:Player
d1:Die
1: r1 = roll()
2: r2 = roll()
d2:Die
Collaboration Diagram (flow of messages between
instances/invocation of methods)
17
Player
Die
Rolls
name
faceValue
1
2
play()
roll()
1
2
Plays
1
DiceGame
1
Includes
init()
Design Class Diagram (methods, object associations)
18
Class Diagrams (succinct notation depicting class structure to be
used this term)
Concrete Class:
Class Name
attributes/properties
...
returnType method(Type parameter,…)
...
From sub- to superclass
Abstract Class:
aggregation with ownership
Class Name
attributes/properties
...
returnType method(Type parameter,…)
returnType abstractMethod(): ...
...
aggregation without
ownership
Interface:
<interface>
Interface Name
returnType method(Type parameter,…)
...
19
Class Diagram Examples
Payment
double amountDue
void makePayment(double amountPaid)
double getAmouontDue()
CashPayment
CreditPayment
void makePayment(double
amountPaid)
void makePayment(double
amountPaid)
SfsuClass
SfsuStudent
String courseName
String name
Vector getStudents
note: non-ownership
Registrar might track students’ status
so Students would be associated with
a class and the registrar’s office
Car
Engine
...
String type
...
note: ownership
a specific engine is
made for a specific car
20
String getName()
String getType()
<<<OOADwithUML.ppt>>>
<<<CH1-HIST.PPT>>>
Macro Level Process
1. Plan/Elaborate
define requirements, prototypes, etc. - iterative development
a. Plan: schedule resources, budget, etc.
b. Preliminary investigation report: motivation, alternatives, business needs
c. Requirements specification: needs for product (overview, customers, goals, system
functions/attributes)
d. Glossary: of terms, constraints, rules (ongoing)
e. Prototype: to aid understanding - optional (may do later)
f. Use Cases: prose description of domain processes
g. Use Case Diagrams: illustration of Use Cases and their relationships
h. Draft Conceptual Model: to help understand vocabulary of domain vis-a-vis Use
Cases and requirements spec. (representation of concepts, objects in problem domain)
2.
Build the System
development cycles: refine plan, analyze, design, construct, test (-release)
3.
Deploy
-releases
Each development cycle should be completed in a very definite time bound (e.g. 2 weeks - 2
months)
21
Object-Oriented Analysis
OOA attempts to analyze/understand real life system, domain concepts (e.g. object types,
associations and state changes), requirements, participants (objects), interactions and
responsibilities. It answers the question of what the software will do without concern for how it
will do it.
1. Define Essential Use Cases
2. Refine Use Case diagrams
3. Refine Conceptual Model
4. Refine Glossary
5. Define System Sequence Diagrams
6. Define Operation Contracts
7. Define State Diagrams
Object-Oriented Design
OOD involves architecting the software system and software artifacts (classes, etc.) gleaned
from artifacts produced in OOA.
1. Define Real Use Cases
2. Define reports, user interface/storyboards
3. Define System Architecture
4. Define Interaction Diagrams
5. Define Design Class Diagrams
6. Define Database Schema
22
Note that in OOA one doesn’t deal in software. You might consider that the OOA can be
done by a business person rather than a programmer. OOD is done by a software
engineer.
IMPLEMENT A STORE/POST (Point of Sale Terminal) SYSTEM
GUI (used to init Store - stock, catalog, POST(s), sales log
and get Store information)
STORE
GUI (customer
purchases)
POST
Product
Catalog
…
Client Level - user interface
GUI
POST
Inventory
Business Logic
Sales Log
Authorization Service (e.g.
Verity)
Checks, credit cards
23
Database Servers
DOMAIN EXPERT: Dr. Levine
POST
Product
Quantity
Total
Tendered
Enter Item
Balance
End Sale
Make Payment
Point of Sale Terminal (POST - cash
register)
The POST has a bar code scanner (UPC codes), a cash drawer and a credit card scanner
We need the software to run the terminal
Our modification to the POST GUI: Users will select products from a drop-down product
selection list; the product will be posted, along with the price. A running subtotal will be
displayed.
24
Databases
PRODUCT CATALOG
FIELDS
 Text Description
 gif (picture of product)
 Price
OPERATIONS
 Add new product specification
 Get record, given product id (i.e. its position in the list of products)
 Print catalog in sorted order by product name (print text descriptions, price, quantity)
SALES LOG
FIELDS
 Customer name
 Date/time of transaction
 Invoice information
OPERATIONS
 Add new sale
 Print log ordering by date/time
 Print all purchases by customer
25
Object-Oriented Analysis
Investigate the problem with the perspective of the relevant objects. Find and describe real life
objects and concepts in the problem domain.
A. Specify the requirements of the product - document what is really needed

Overview: system should do ????

Customers

Goals: e.g. increase automation, faster services

System functions: e.g. the system should do ?? (credit payment authorization)

System attributes: e.g. ease of use
B. Develop the Glossary
C. Specify Use Cases: a narrative document that describes the sequence of events of an actor
using a system to complete a process. The events are external events (system events) caused by
an actor outside the system boundary.

Expand the Critical Use Cases

Essential Use Cases: expanded use cases; still free of any technology
commitments/implementation details; still abstract; e.g. the actor action might be: the
customer identifies themselves and the system responds with a set of options; note that
there are no details such as what identifying information the customer presents, etc.

Use Case Diagrams: show relationships amongst use cases.
D. Develop the Conceptual Model: different categories of things in the domain; don’t make
too complex; not fine grained; later refine it. These things are found in the real world, not
26
software components. Better to overspecify rather than under specify. Note that a concept
corresponds to the real world, whereas a class is a software artifact.
Associations between the concepts are depicted by labeled edges.
Attributes are included in each of concept specifications, as appropriate.
27
Records-sale-of
Described-by
1
Product
Catalog
Product
Specification
Contains
1
1..*
description
price
UPC
1
Describes
Used-by
0..1
*
*
*
Sales
LineItem
Stocks
Store
address
name
1
1
1
Item
*
quantity
1..*
1
Contained-in
Logscompleted
1
Houses
1..*
*
Sale
date
time
POST
1
1
Manager
Captured-on
Started-by
1
1
1
1
1
Initiated-by
Records-sales-on
Paid-by
1
Payment
amount
1
Customer
1
Cashier
Conceptual Model for POST (objects found in the real world – nouns drawn from
the requirements/specification)
28
E. Develop the System Sequence Diagrams: Used to better understand the external actors and
the system events they generate - the behavior of the system; derived from the use cases.
These depict a single path through a flow of events. Used to help identify the system’s
objects/relationship between them. The messages are interactions among objects (identifies
behavior). These objects are used to help discover classes. Note that good use cases will help
determine objects, which will help determine classes.
NOTE: There should be one sequence diagram for each use case.
29
Object-Oriented Design
Specify the solution of the problem; the software components
A. Develop Real Use Cases: describes real or actual design of the Essential Use Case in terms
of concrete input/output and its overall implementation - provide sample GUI’s if needed.
Depict actor actions and system responses - e.g. in an essential use case we might say that
customers identify themselves; in a real use case we might say that the customer inserts their
card (a very concrete realization of the essential use case)
B. Specify Interaction Diagrams: Illustrates the message interactions between
instances/classes in the class model. The two types of Interaction Diagrams that are used are 1.
Collaboration Diagrams and 2. Sequence Diagrams.
C. Consider the use of Patterns: named description of a problem and solution that can be
applied in new contexts; e.g. the Controller pattern.
D. Build the Design Class Diagrams: specify the software classes/interfaces which participate
in the software solution; annotate them with design details, such as methods.. Group
components into layered architecture and packages (used to define nested name spaces) to
enhance the specification (i.e. ease of understanding, etc.).
We will generate the class definitions from the Design Class Diagrams. The Collaboration
Diagrams will be used to help determine the class methods.
30
High-level Stages in the STORE/POST Software System:
Startup: init

Store

Stock

Catalog

POST

Sales log
While open for business: Customer purchases items1

Arrives at a date and time

Identifies themselves

Buys one or more items (might have more than one of an item)

Ends sale: pays cash, credit, check (authorization service used to verify checks/credit
cards; assume 10% of the authorization requests are denied)

Receipt is issued
Close STORE
1
Initially, these activities will be simulated with information from a file. Be sure to design
system to minimize changes resulting from plugging in a GUI for interactions with a client
rather than a file. Note that you will plug in a GUI in a subsequent stage.
31
POST Assignments
1. Write a Java program for the POST
Use file I/O for testing. A note on naming: begin all interface names with "I"
e.g. IPost would be an interface whereas Post would be a class
2. Write Java GUI’s for the STORE/POST
Hook up the GUI’s to the Java program for POST using RMI (described later)
32
1. Write a Java program for the Post example in the text. In addition to the Post classes,
as motivated by the conceptual model, you should include the following classes:
Customer
The customer will have a name and provide a series of upc/quantity pairs where the upc
is a string of 4 chars and quantity is an integer; be careful to check that the user
provides valid upc's (hint: the Customer will get the transaction from file and call on
POST methods to do the transaction)
Store
The store will hold information including the product catalog
Manager
The manager will open the store, set up Post(s), put together the product catalog
Be sure to use packages, where appropriate. Do not provide a GUI for this application.
Initialize a simple database from a file (products.txt)
Product Record - one per line
UPC: 4 characters
Text description: 20 columns
Price: floating number as xxxx.xx
(columns 1-4)
(columns 10-29)
(column 35 ..)
Transaction file (transaction.txt) composed of 1 or more multi-line
transaction records:
Each part is on a separate line:
Identifying information: Customer name
Item: upc quantity OR upc if quantity is one
…
(upc in columns 1-4; quantity in column 10 ...)
Item
Payment: <CASH/CHECK $xxxx.xx> OR <CREDIT ddddd> ddddd is credit
card #
BLANK LINE
Invoice Printed by Program
STORE NAME
Customer Name Date Time
Item: <text quantity @ unit price subtotal>
…
Item
-----Total $xxxx.xx
Amount Tendered: xxxx.xx OR Paid by check OR Credit Card ddddd
Amount Returned: xxxx.xx
33
You should turn in the hardcopy in the following order::
1. Output
2. UML Specs:
Use-Case Diagrams
Collaboration Diagrams - only Sequence Diagrams (each sequence diagram will depict a
single Use-Case)
Class Diagrams for ALL classes; be sure to show all instance variables and public member
functions with arguments, their types and return types
3. *.java files with appropriate comments
Sample File I/O API
Consider the following class specs:
class ProductReader { // Product catalog will use this for initialization
ProductReader(String productFile) {…}
boolean hasMoreProducts() // are there more products to read?
ProductSpec getNextProduct() // get/build next product spec
}
class TransactionReader {
TransactionReader (Store store, String transactionFile) {…}
boolean hasMoreTransactions() // are there more Transactions to read?
Transaction getNextTransaction () // get next Transaction
...
}
class Transaction {
TransactionHeader header; // provide a class for header info
TransactionItem transItems[100]; // assume less than 100 items
int numTransItems;
Payment payment;
...
}
34
2. Write Java GUI’s for the STORE/POST
Hook up the GUI’s to the Post program using RMI
STORE - init
Name, current date/time
POST - purchases
Customer provides a name.
Use drop-down selection list for upc (retrieve info from server program via
RMI)
Use list to select quantity
Maintain running total display
Create Payment screen: select from options then display appropriate dialog
Cash: for amount of purchase
Credit card: request number
Check: for amount of purchase
Change: to simplify always assume exact payments are made
You should turn in the hardcopy in the following order::
1. Appropriate screen snapshots – a few interesting screens; also provide any output
2. External Documentation: Indicate where objects are placed with respect to the client app
and server app. Refer to slides in this reader “RMI Client-Server Implementation of the
POST Problem”. Be sure to identify client(s)/server(s) – don’t just say, e.g., client without
indicating if you are identifying the POST or Store. Also, include a written description of
your solution.
3. Sequence diagram indicating collaborations between remote objects.
4. Class Diagrams - structured, to aid in understanding your architecture/deployment of
classes; printed from CASE tool. Be sure to include all classes that are part of this problem,
including classes used both in the first POST lab and this lab.
5. Your code with appropriate comments, ordered appropriately to ease understanding
(i.e. not alpha-order)
Note: When working with GUI’s one normally requires an application coordinator to mediate
between the GUI and the domain layer (domain concepts - e.g. the conceptual model). Various
controllers will be used.
35
Sample Screen
36
Indicate which IDE you are using. Provide appropriate documentation - protocol for
communication with the Java middle tier program, class descriptions, choose good variable
identifiers. Break up the program into packages.
37
Use Case View
1. Use Cases - provide a view from the outside
 Brief description of purpose
 Flow of events
Use Case Diagrams - shows relationship among use cases and actors
Communication
 Extends (e.g. one use case extends another) AB : B may include A’s behavior
 Uses (AB : A includes B’s behaviour)
2. Sequence Diagrams - scenarios are used to describe how use cases are realized as object
interactions; includes actors, objects, and messages (one diagram per use case). Note that
there should be one sequence diagram that depicts the interactions for each use-case.
3. Collaboration Diagrams - another view of a sequence diagram
 Can generate automatically from sequence diagram
 Includes actors, objects, links, messages and data flows
4. Packages - a mechanism for logical groupings (e.g. of use cases, etc.)
Logical View
Examines classes and their static relationships; also dynamic nature of classes


Includes class diagrams and state transition diagrams
Construct different diagrams each showing relevant information (easier to focus on items of
interest)
Relationships
 Association - general association information with roles and multiplicity
 Aggregation - part-of (e.g. an arm is a part-of a person)
 Dependency - client depends on supplier for service (e.g. global objects/classes or supplier
passed as an argument to client class function or supplier declared locally)
 Inheritance
State Transition Diagrams - for life history of objects in a class; for objects that exhibit
significant dynamic behavior
38
Component View
Different software system components
User drags classes from the logical view to the containing component
39
Some Notes on Java
this in Java
this behaves in the same fashion as the self pseudo-variable in Smalltalk - it refers to the
current instance. Consider the following code example below:
class A {
public void m() {System.out.println("Class A, method m ."); }
}
class B extends A {
public void m() {
System.out.println("Class B, method m. Now calling this.m1().");
this.m1();
}
public void m1() {
System.out.println("Class B, method m1 .");
}
}
public class C extends B {
public void m1() {
System.out.println("Class C, method m1.");
// this.m(); // What happens if we include this line?
}
public static void main(String args[]) {
A a = new A();
B b = new B();
C c = new C();
System.out.print("Calling A.m() ");
System.out.print("Calling B.m() ");
System.out.print("Calling C.m() ");
System.out.print("Calling ((B)c).m1()
a.m();
b.m();
c.m();
");
((B)c).m1();
}
}
Output:
Calling A.m() Class A, method m .
Calling B.m() Class B, method m. Now calling this.m1().
Class B, method m1 .
Calling C.m() Class B, method m. Now calling this.m1().
40
Class C, method m1.
Calling ((B)c).m1() Class C, method m1.
this Used in Method Calls
Note that when methods are invoked this is used implicitly as the instance for method searches.
Consider the following program/output to clarify.
import java.util.*;
class SubClass extends BaseClass {
void m1() { System.out.println("m1 in SubClass"); }
}
class BaseClass {
void m() { System.out.println("m in BaseClass"); m1(); } // this.m1()
void m1() { System.out.println("m1 in BaseClass"); }
}
public class test1 {
public static void main (String args[]) {
SubClass s = new SubClass();
s.m();
}
}
Output:
m in BaseClass
m1 in SubClass
41
Enumerated Types and Type Safety
These are types with a finite set of values. Typically, we define symbolic constants to represent
these types. Consider the java.util.Calendar class enumerated types:
public static final int JANUARY = 0;
public static final int FEBRUARY = 1;
...
public static final int DECEMBER = 11;
This is not very satisfactory since the compiler cannot check type errors:
e.g.,
int month = Calendar.DECEMBER;
month++; // not a valid month
Consider the following solution seen in java.awt.Color:
public final static Color white
= new Color(255, 255, 255);
In this case, all values are of type Color. We can only manipulate Colors through the Color
class interface – this ensures type safety.
42
Architectural Design (Appendix 2)
Architectural Patterns
Sometimes the same architectural design recurs in diverse applications. We call this an
architectural pattern. Here are a few examples.
Model-View-Control
Client-Server
43
Reflection
Layers
Package Structures
There are a few other patterns that are used for developing packages. To loosen the coupling
between packages, the facade pattern designates one class as the package interface to the
others. A package only exports its facade:
44
The mediator minimizes the connections between classes within a package:
45
Java's Remote Method Invocation - RMI
(A nice reference: http://java.sun.com/docs/books/tutorial/rmi/index.html)
Similar to CORBA (Common Object Request Broker Architecture) but provides a distributed
Java environment. It is an attempt to provide object location transparency - the client doesn't
write code that depends on the location of the objects; the objects can be local or on some
remote machine.
Client Program
Server Program
Interface
Implementation
remote objects, primitive objects, serializable objects
marshalled parameters
copies of primitive type parameters
marshalled results
copies of primitive type results
remote exception(s)?
Figure 1
46
Client Program
Server Program
Gets proxy for
remote object and
calls its methods
Contains (remote)
objects - remote clients
make calls on the
methods of these
objects
Proxy (stub)
Implementation
RMI transport
marshalled parameters
copies of primitive type parameters
marshalled results
copies of primitive type results
remote exception(s)?
Figure 2
A client program makes method calls on the proxy object, RMI sends the request to the remote
JVM, and forwards it to the implementation. Any return values provided by the implementation
are sent back to the proxy and then to the client's program.
47
Client Program
RMI System
Server Program
Stubs/Skeletons
Stubs/Skeletons
Remote Reference Layer
Remote Reference Layer
Transport Layer
Figure 3
The RMI implementation is essentially built from three abstraction layers. The first is the Stub
and Skeleton Layer, which lies just beneath the view of the developer. This layer intercepts
method calls made by the client to the interface reference variable and redirects these calls to a
remote RMI service. The skeleton understands how to communicate with the stub across the
RMI link. The skeleton carries on a conversation with the stub; it reads the parameters for the
method call from the link, makes the call to the remote service implementation object, accepts
the return value, and then writes the return value back to the stub. In the Java 2 SDK
implementation of RMI, the new wire protocol has made skeleton classes obsolete. RMI uses
reflection to make the connection to the remote service object. You only have to worry about
skeleton classes and objects in JDK 1.1 and JDK 1.1 compatible system implementations.
Note that when remote methods are invoked the RMI system must prepare the method
arguments to be transported across the internet to the server. This argument value preparation is
termed marshalling. If the argument is a primitive type then its copy is passed. If the argument
represents an object then its value is serialized. The stub is responsible for marshalling the
arguments. On the receiving end, the skeleton is responsible for unmarshalling the arguments
in preparation for the method call on the server object. Object serialization essentially flattens
an object and any objects it references. This can be a very involved process for objects that
contain references to other objects, and those objects reference others, and so on.
Conversely, when a result value is returned to the client then the skeleton marshals the value
and the stub unmarshals the value. If there are network problems, etc., then a RemoteException
might be raised and must be handled in an appropriate fashion. The stubs and skeletons are
generated via the rmic program included in the Java system.
The next layer is the Remote Reference Layer. This layer understands how to interpret and
manage references made from clients to the remote service objects. In JDK 1.1, this layer
connects clients to remote service objects that are running and exported on a server. The
connection is a one-to-one (unicast) link. In the Java 2 SDK, this layer was enhanced to support
the activation of dormant remote service objects via Remote Object Activation.
48
The Transport Layer is based on TCP/IP connections between machines in a network. It
provides basic connectivity, as well as some firewall penetration strategies. Note that even if
you are using RMI on a single system you must have an operational TCP/IP configuration on
your computer.
When an object is passed to a remote method, unlike normal parameter passing semantics, RMI
sends the object itself, not its reference, between JVMs. It is the object that is passed by value,
not the reference to the object. Similarly, when a remote method returns an object, a copy of the
whole object is returned to the calling program.
Distributed Garbage Collection
The RMI system provides a reference counting distributed garbage collection algorithm based
on Modula-3's Network Objects. This system works by having the server keep track of which
clients have requested access to remote objects running on the server. When a reference is
made, the server marks the object as "dirty" and when a client drops the reference, it is marked
as being "clean."
Object Registration
Objects that can be referenced by remote clients must be registered as such on the server
machine by using the java registry service rmiregistry. Clients can then lookup remote
object(s) on servers by accessing the naming service on the server.
Consider the following RMI example using remote account objects (perhaps useful within an
ATM context).
RMI Example - Accounts used by ATM's
Server files:
Interfaces: Account.java
Implementation files: AccountImpl.java, RegAccount.java
Client files: AccountClient.java, account.jar
/*
* Account.java
*
*/
package accountInterfaces;
import java.rmi.Remote;
import java.rmi.RemoteException;
49
public interface Account extends java.rmi.Remote {
public String getName() throws RemoteException;
public void setName(String name) throws RemoteException;
public float getBalance() throws RemoteException;
public void withdraw(float amt) throws RemoteException;
public void deposit(float amt) throws RemoteException;
}
/*
* AccountImpl.java
*
*/
package serverAccounts;
import accountInterfaces.Account;
import java.rmi.server.UnicastRemoteObject;
import java.rmi.RemoteException;
public class AccountImpl extends UnicastRemoteObject implements Account {
private float balance = 0;
private String name = "";
public AccountImpl(String name) throws RemoteException {
this.name = name;
}
public String getName() throws RemoteException {
return name;
}
public void setName(String name) throws RemoteException {
this.name = name;
}
public float getBalance() throws RemoteException {
return balance;
}
50
public void withdraw(float amt) throws RemoteException {
balance -= amt;
// Make sure balance never drops below zero
balance = Math.max(balance, 0);
System.out.println("Withdrawn from " + name + ": " + amt +
" New Balance: " + balance);
}
public void deposit(float amt) throws RemoteException {
balance += amt;
System.out.println("Deposit to " + name + ": " + amt +
" New Balance: " + balance);
}
// Move some funds from another (remote) account into this one
public void transfer(float amt, Account src) throws RemoteException {
src.withdraw(amt);
this.deposit(amt);
}
}
/*
* RegAccount.java
*
*/
package serverAccounts;
import accountInterfaces.Account;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class RegAccount {
public static void main(String args[]) {
try {
/*
if (System.getSecurityManager() == null) {
System.setSecurityManager(new SecurityManager());
}
*/
for (int i = 0; i < args.length; i++) {
Account acct = new AccountImpl(args[i]);
51
// Register it with the local naming registry
Registry registry = LocateRegistry.getRegistry();
registry.rebind(args[i], acct);
System.out.println("Registered account for: " + args[i]);
}
} catch (RemoteException e) {
System.out.println("********* " + e);
e.printStackTrace();
}
}
}
/*
* AccountClient.java
*
*/
package accountClient;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import accountInterfaces.*;
public class AccountClient {
public static void main(String args[]) {
try {
Registry registry = LocateRegistry.getRegistry("localhost");
//
Registry registry = LocateRegistry.getRegistry("rmi://thecity.sfsu.edu/Mary");
for (int i = 0; i < args.length; i++) {
String accountHolderName = args[i];
Account acct = (Account)registry.lookup(accountHolderName);
acct.deposit(5000);
System.out.println("Deposited 5000 into account owned by " +
acct.getName());
System.out.println("Balance total: " + acct.getBalance());
}
} catch (Exception e) {
System.out.println("Error while looking up account: " + e);
}
}
}
52
Steps to run this example (JDK 1.5 on Windows XP):
1. Compile all interface files using Java 1.5:
C:\rmi\server>javac accountInterfaces\*.java
2. Package interface files to be shipped to clients
C:\rmi\server>jar cvf account.jar accountInterfaces\*.class
added manifest
adding: accountInterfaces/Account.class(in = 383) (out= 239)(deflated 37%)
3. Compile all server files, including implementation files – use jar file of interfaces
C:\rmi\server>javac -cp account.jar serverAccounts\*.java
C:\rmi\server>dir
Volume in drive C has no label.
Volume Serial Number is 0C13-E520
Directory of C:\rmi\server
12/17/2006 03:23 PM <DIR>
.
12/17/2006 03:23 PM <DIR>
..
12/17/2006 03:23 PM
738 account.jar
12/17/2006 03:12 PM <DIR>
accountInterfaces
12/17/2006 03:13 PM <DIR>
serverAccounts
1 File(s)
738 bytes
4 Dir(s) 137,310,576,640 bytes free
4. Move jar file to client and compile client files:
C:\rmi\client>dir
Volume in drive C has no label.
Volume Serial Number is 0C13-E520
Directory of C:\rmi\client
12/17/2006 03:23 PM <DIR>
.
12/17/2006 03:23 PM <DIR>
..
12/17/2006 03:23 PM
738 account.jar
12/17/2006 03:18 PM <DIR>
accountClient
1 File(s)
738 bytes
3 Dir(s) 137,310,576,640 bytes free
53
C:\rmi\client>javac -cp account.jar accountClient\*.java
5. Start rmiregistry:
C:\rmi>start rmiregistry
C:\rmi>
6. Run server, register exported object(s):
C:\rmi\server>java -cp
account.jar;C:\rmi\server -Djava.rmi.server.codebase=file:C:/rmi/server/account.jar
-Djava.rmi.server.hostname=localhost serverAccounts.RegAccount Jack Mary
Registered account for: Jack
Registered account for: Mary
7. Run client; note that when it’s re-run it continues to use the same server objects:
C:\rmi\client>java -cp account.jar;C:\rmi\client
-Djava.rmi.server.codebase=file:C:/rmi/client/account.jar
accountClient.AccountClient Jack Mary
Deposited 5000 into account owned by Jack
Balance total: 5000.0
Deposited 5000 into account owned by Mary
Balance total: 5000.0
C:\rmi\client>java -cp account.jar;C:\rmi\client
-Djava.rmi.server.codebase=file:C:/rmi/client/account.jar
accountClient.AccountClient Jack Mary
Deposited 5000 into account owned by Jack
Balance total: 10000.0
Deposited 5000 into account owned by Mary
Balance total: 10000.0
8. Check output on server
C:\rmi\server>java -cp
account.jar;C:\rmi\server -Djava.rmi.server.codebase=file:C:/rmi/server/account.jar
-Djava.rmi.server.hostname=localhost serverAccounts.RegAccount Jack Mary
Registered account for: Jack
Registered account for: Mary
Deposit to Jack: 5000.0 New Balance: 5000.0
Deposit to Mary: 5000.0 New Balance: 5000.0
Deposit to Jack: 5000.0 New Balance: 10000.0
Deposit to Mary: 5000.0 New Balance: 10000.0
54
RMI Example - Remote methods returning remote objects
As described above, a client program can obtain a reference to a remote object through the RMI
Registry program. In addition, it can be returned to the client from a method call. When a
method returns a local reference to an exported remote object, RMI does not return that object.
Instead, it substitutes the remote proxy for that service in the return stream1. The following
example will illustrate this situation.
Server files:
Interfaces: Account.java, AccountManager.java
Implementation files: AccountImpl.java, AccountManagerImpl.java,
AccountManagerReg.java
Client files: AccountManagerTest.java, manageAccount.jar
/*
* Account.java
*/
package manageAccountInterfaces;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Account extends java.rmi.Remote {
public String getName() throws RemoteException;
public void setName(String name) throws RemoteException;
public float getBalance() throws RemoteException;
public void withdraw(float amt) throws RemoteException;
public void deposit(float amt) throws RemoteException;
}
/*
* AccountManager.java
*/
package manageAccountInterfaces;
import java.rmi.Remote;
1
Non-remote objects either passed as a parameter or returned as a result from one VM to another VM must be
serializable – a copy of the object (not a stub) is actually passed. Note that Strings are passed as args and returned
as results. Since Strings are not declared as remote, they are copied/serialized and then shipped.
55
import java.rmi.RemoteException;
public interface AccountManager extends Remote {
public Account getAccount(String name) throws RemoteException;
public Account newAccount(String name) throws RemoteException;
}
/*
* AccountImpl.java
*/
package manageAccountServer;
import manageAccountInterfaces.*;
import java.rmi.server.UnicastRemoteObject;
import java.rmi.RemoteException;
public class AccountImpl extends UnicastRemoteObject implements Account {
private float balance = 0;
private String name = "";
public AccountImpl(String name) throws RemoteException {
this.name = name;
}
public String getName() throws RemoteException {
return this.name;
}
public void setName(String newName) throws RemoteException {
this.name = newName;
}
public float getBalance() throws RemoteException {
return balance;
}
public void withdraw(float amt) throws RemoteException {
balance -= amt;
// Make sure balance never drops below zero
balance = Math.max(balance, 0);
System.out.println("Withdrawn from " + name + ": " + amt +
" New Balance: " + balance);
}
public void deposit(float amt) throws RemoteException {
56
balance += amt;
System.out.println("Deposited into " + name + ": " + amt +
" New Balance: " + balance);
}
public void transfer(float amt, Account fromAccount) throws RemoteException {
fromAccount.withdraw(amt);
this.deposit(amt);
}
}
/*
* AccountManagerImpl.java
*/
package manageAccountServer;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
import manageAccountInterfaces.*;
public class AccountManagerImpl
extends UnicastRemoteObject implements AccountManager {
private static java.util.HashMap<String,Account> accounts = new
java.util.HashMap<String,Account>();
public AccountManagerImpl() throws RemoteException {}
public Account getAccount(String name) throws RemoteException {
Account acct = accounts.get(name);
if (acct == null) throw new RemoteException("Account not found: " + name);
return acct;
}
public Account newAccount(String name) throws RemoteException {
Account acct = new AccountImpl(name);
if (accounts.get(name) != null) throw new RemoteException("Attempt to create duplicate
account: " + name);
accounts.put(name,acct);
System.out.println("New account for: " + name);
57
return acct;
}
}
/*
* AccountManagerReg.java
*
*/
package manageAccountServer;
import manageAccountInterfaces.*;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class AccountManagerReg {
public static void main(String argv[]) {
try {
AccountManager mgr = new AccountManagerImpl();
Registry registry = LocateRegistry.getRegistry();
registry.rebind("manager", mgr);
System.out.println("Registered account manager");
} catch (RemoteException ex) {
System.out.println("*********** " + ex);
ex.printStackTrace();
}
}
}
/*
* AccountManagerTest.java
*
*/
package client;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import manageAccountInterfaces.*;
58
public class AccountManagerTest {
public static void main(String argv[]) {
try {
Registry rmtReg = LocateRegistry.getRegistry();
AccountManager mgr = (AccountManager)rmtReg.lookup("manager");
Account m;
try {
m = mgr.getAccount("Mary");
} catch (RemoteException ex) {
m = mgr.newAccount("Mary");
}
m.deposit(2000);
System.out.println("Deposited 2000 into account owned by " +
m.getName());
System.out.println("Balance now totals: " + m.getBalance());
if (m.getBalance() > 4000) {
m.withdraw(25);
System.out.println("Withdrawn 25 from account owned by " +
m.getName());
System.out.println("Balance now totals: " + m.getBalance());
}
} catch (RemoteException ex) {
System.out.println("*********** " + ex);
ex.printStackTrace();
} catch (NotBoundException ex) {
ex.printStackTrace();
}
}
}
Steps to run this example (JDK 1.5 on Windows XP):
1. Compile all interface files using Java 1.5:
C:\rmi\server>javac manageAccountInterfaces\*.java
2. Package interface files to be shipped to clients
C:\rmi\server>jar cvf manageAccount.jar manageAccountInterfaces\*.class
added manifest
adding: manageAccountInterfaces/Account.class(in = 389) (out= 242)(deflated 37%)
59
adding: manageAccountInterfaces/AccountManager.class(in = 317) (out= 198)(deflat
ed 37%)
3. Compile all server files, including implementation files – use jar file of interfaces
C:\rmi\server>javac -cp manageAccount.jar manageAccountServer\*.java
C:\rmi\server>dir
Volume in drive C has no label.
Volume Serial Number is 0C13-E520
Directory of C:\rmi\server
12/17/2006 02:05 PM <DIR>
.
12/17/2006 02:05 PM <DIR>
..
12/17/2006 01:57 PM
1,131 manageAccount.jar
12/17/2006 01:55 PM <DIR>
manageAccountInterfaces
12/17/2006 01:59 PM <DIR>
manageAccountServer
1 File(s)
1,131 bytes
4 Dir(s) 137,315,061,760 bytes free
4. Move jar file to client and compile client files:
12/17/2006 02:01 PM <DIR>
.
12/17/2006 02:01 PM <DIR>
..
12/17/2006 02:03 PM <DIR>
client
12/17/2006 11:39 AM
1,131 manageAccount.jar
1 File(s)
1,131 bytes
3 Dir(s) 137,315,127,296 bytes free
C:\rmi\client>javac -cp manageAccount.jar client\*.java
5. Start rmiregistry:
C:\rmi>start rmiregistry
C:\rmi>
6. Run server, register exported object(s):
C:\rmi\server>java -cp manageAccount.jar;C:\rmi\server -Djava.rmi.server.codeba
se=file:C:/rmi/server/manageAccount.jar -Djava.rmi.server.hostname=localhost man
ageAccountServer.AccountManagerReg
Registered account manager
7. Run client; note that when it’s re-run it continues to use the same server objects:
60
C:\rmi\client>java -cp manageAccount.jar;C:\rmi\client -Djava.rmi.server.codeba
se=file:C:/rmi/client/manageAccount.jar client.AccountManagerTest
Deposited 2000 into account owned by Mary
Balance now totals: 2000.0
C:\rmi\client>java -cp manageAccount.jar;C:\rmi\client -Djava.rmi.server.codeba
se=file:C:/rmi/client/manageAccount.jar client.AccountManagerTest
Deposited 2000 into account owned by Mary
Balance now totals: 4000.0
8. Check output on server
C:\rmi\server>java -cp manageAccount.jar;C:\rmi\server -Djava.rmi.server.codeba
se=file:C:/rmi/server/manageAccount.jar -Djava.rmi.server.hostname=localhost man
ageAccountServer.AccountManagerReg
Registered account manager
New account for: Mary
Deposited into Mary: 2000.0 New Balance: 2000.0
Deposited into Mary: 2000.0 New Balance: 4000.0
61
RMI Client-Server Implementation of the POST Problem
(Documentation)
In any client-server application it is very important to indicate the deployment of the objects of
interest to better understand the nature of the computation. There are many issues that must be
considered regarding the location of the participating objects, including security, scalability,
network traffic, network integrity and other efficiency concerns. These considerations must be
addressed during the analysis of the system. For instance, consider the situation wherein the
central store computer (server) crashes. Should this prevent the POST(s) from serving
customers?
I have provided two simple tables below that depict the deployment of the objects. Be sure to
study these tables so you might better understand how to provide similar documentation for
your projects.
Main Objects Shipped from Server
(Store) to Client (POST)2
<<You should fill in the table entries
appropriately>>
Main Objects Shipped from Client to
Server
ProductCatalog
Server (Store)
Post
Client (POST)
Store (stub)
Store (skeleton)
Invoice
2
For each object, indicate whether its remote reference is shipped or a copy is shipped.
62
Objects on Client (POST)
CashPayment
Objects on Server (Store)
CashPayment
Comments
Various Payments are
created on the client and
shipped to the server as
part of an invoice
<<You should fill in the
remaining table entries
appropriately>>
In addition to these deployment tables be sure to provide adequate documentation so I can
understand the main object interactions (sequencing). Also, note that the GUI classes should
not contain any business logic; they are only concerned with the presentation layer.
63
Object Oriented Programming Principles
Documentation
 User Manual: prepare before design effort to ensure an understanding of the clients’ desires

Design documentation: document important design decisions to help understand factors
influencing choices; important reference information

Prepare for change: predict sources of change
Message passing versus function calls
 messages are passed to instances (receivers); methods are determined by class of receiver;
the instance is passed as an implied argument (use self, this to reference the argument)

function calls are matched to function bodies based on the types (classes) of (all of) the
arguments
64
Quality of an Interface
1. Cohesion – all of its methods are related to a single abstraction
e.g.,
public class MyContainer {
public void addItem(Item anItem) {….}
public Item getCurrentItem() { … }
public Item removeCurrentItem() { … }
public void processCommand(String command) { … }
…
}
Note that processCommand might fit better in another class; leave the MyContainer class to do
what it does best – store Items.
2. Completeness – interface should support all operations that are a part of the abstraction that
the class represents.
e.g.,
The Stack class should contain a method to check if the Stack is empty
3. Convenience – ensure that the interface makes it convenient for clients to do associated
tasks, especially common tasks.
e.g.,
Prior to Java 5 System.in could not read lines of text or numbers. This was fixed by the addition
of the java.util.Scanner class
4. Clarity – the interface should be clear to programmers
e.g.,
LinkedList<String> list = new LinkedList<String>();
list.add(“A”);
list.add(“B”);
list.add(“C”);
…
ListIterator<String> iterator = list.listIterator();
While (iterator.hasNext())
System.out.println(iterator.next());
…
ListIterator<String> iterator = list.listIterator(); // cursor is just before the first element
iterator.next(); // advance cursor
iterator.add(“X”); // AXBC
…
iterator.remove(); // the documentation is confusing – is X or A removed?
65
5. Consistency – the operations in a class should be consistent with each other with respect to
names, parameters and return values, and behavior.
e.g.,
from the Java library:
new GregorianCalendar(year, month-1, day)
The month is between 0 and 11 but the day is between 1 and 31!
66
Inheritance (type/subtype relationship)
A
B

C
B is_a A
Substitutability: A’s can be replaced by B’s or C’s; they have the same behavior (maybe B
has more behaviors or refines behaviors of A - all public methods of A are available in B)
Subclassing heuristics
 Specialization: subtype
B is_a A ==> B is a subclass of A (Lion is_a Mammal; Toyota is_a Car; ...)
If we have B has_a_component A then we should create an instance variable to record A (A
is an attribute of B..e.g. Human has_a Arm)

Specification: parent has abstract methods (like an interface); specifies behavior that the
subclass implements

Construction: use parent based on its behavior; e.g. a Stack is_a List (limit
adding/removing operations) - this violates the is_a relationship so the Stack is not really a
subtype of List (dangerous)
Consider a symbol table for a compiler where the keys are symbol names and the values
are fixed fields; the attributes of symbols. An Array has keys that are integer values. A
Dictionary is like an Array but the keys are arbitrary values.
A) class SymbolTable : Dictionary
B) class SymbolTable : Object
or
(we will use an instance variable to record the
Dictionary)
A) is shorter to code (an important consideration) than B) since we are using inheritance
rather than rewriting methods; but we can manipulate a SymbolTable as a Dictionary !!!!!
67
This would allow us to use the protocol of Dictionary in (perhaps) a meaningless fashion.
What if we want to re-implement SymbolTable as a HashTable...users’ code might use
Dictionary protocol !!!!
Solution B) is more clear to users.
We are using Dictionary to help construct SymbolTable.

Generalization/Extension: In this case the subtype extends (or, generalizes on) the
behaviour of the supertype (e.g. use when building on the system classes); It’s better to use
Specialization rather than Generalization (except when it’s necessary -- we can’t change the
system classes). For example, suppose when we define the system we include a Window
class where the background is only black and white. Then we might add
ColoredWindow : Window
class
where ColoredWindow contains a field to indicate the
additional background colors

Limitation: limit parent’s behavior Consider defining class Stack : Deque where in
Stack we override the irrelevant parts of Deque (a Double ended queue) protocol with error
messages.
This violates the is_a relation and should be avoided where possible. We are
imposing limitations on the Deque.
68

Variance: child is a variation of parent. Suppose we have Mouse and GraphicsTablet
classes with similar code but neither is really a subclass of the other (these are variations of
pointing devices). Then maybe we should make one a subclass of the other and use
limitation to override methods where appropriate (e.g. class GraphicsTablet : Mouse ). It
would be better to use an abstract superclass such as: class PointingDevice { … }
class Mouse : PointingDevice { … }
class GraphicsTablet : PointingDevice { … }

Combination: inherit features from more than one parent (multiple inheritance)
69
Costs of Inheritance
Benefits of Inheritance
execution speed (associated with dynamic
Software Reuse
binding)
program size (use general classes)
code sharing
message passing overhead
improved reliability - build from pre-tested
components
program complexity (complex inheritance
consistency of interface - abstract classes
hierarchy - dependencies)
rapid prototyping (due to reuse)
polymorphism
information hiding
Software Reuse
 via inheritance

via composition (has_a relation; Person has_a Arm; Class fields)
The right culture is needed to support reuse

must build solid reusable components

must provide guidelines for producing reusable components

willingness to reuse by programmers

willingness of managers to pay for development
70
Subclass/Subtypes
1. Static vs dynamic typing: how are variables and values related; id’s/variables denote values
(strings of bits)
Static typing - the type is associate with a variable - e.g.
float f; … f = “xxx”; the compiler catches this error
Dynamic typing - the type is associated with a value; normally a reflection mechanism is
used to query the type of the value
2. Static vs dynamic binding: method invocation
Should the binding for information be associated with the static class of a variable or the
A
dynamic class?
B
C
A a; // static class is A; dynamic class could be B or C
Efficiency - static is faster
Error detection - with static one can catch errors at compile time
Flexibility - static is less flexible, creates rigidity, inhibits reuse (can’t predict subclasses
which would want to refine methods)
3. Subclass vs subtype: how a class can be used in place of another
71
Polymorphic Variables
is_a  class B : public A { … }
A a; B b;
b is in essential respects a representative of A  values of type B can be assigned to variables
of type A; reverse is not normally permitted
e.g.
a = b; // ok
b = a; // not ok ; this is Reverse Polymorphism
A subclass is a class that can be substituted (using polymorphism) with no observable effect.
Not all subclasses are subtypes.
C++ - use subclasses for subtypes
Java - use subclasses or interfaces for subtypes
Reverse Polymorphism
class B: public A {…} B b; A a; … b = a: // ??

problem of identity - can I tell if “b” is holding a “A”?

task of assignment  assign value of A to variable of B?

problem solved if reflection is available

polymorphic variables: values maintain their types (reflection)
72
Smalltalk

Dynamic type

polymorphic variables

legality of message passing checked at runtime

reverse polymorphism is not a problem
C++

Instances of classes are statically typed, not polymorphic; pointers and references can hold
polymorphic values

Values don’t have type information

Method binding can be performed using dynamic class (pointers and refs - virtual)

Legality of message passing checked at compile time based on static type

No facilities for testing reverse polymorphism

Assignment of reverse polymorphic variables can be performed using a cast
RunTime Type Identification, a reflection mechanism, was added; now we can do:
b = dynamic_cast <A*>(a) -- this is a downward cast
Java

All variables know their dynamic type

Reverse polymorphism is alright with a cast  dynamically checked

Reflection mechanism is available

Data fields can be overwritten (not like in C++, etc.)

Subtype hierarchy formed using interfaces

Interfaces (subtypes) are like abstract classes; supports multiple inheritance
73
Replacement vs Refinement
Replacement: child class method replaces parent class method
Refinement:

merge methods with the same name in child and parent classes (e.g. if hierarchy has Parent
and Employee; both classes might have a print method)

guarantees behavior in the parent class

Simula has this; in other languages users can call parent method
e.g. in C++: class A: public B { … int f() { B::f(); … } }
in Smalltalk we use the super pseudo-variable
If the language uses replacement semantics (Java) then refinement semantics can be simulated
(the print method in the subclass can call the print method in the superclass).
If the language uses refinement semantics then we cannot simulate replacement semantics.
Replacement semantics voids the is_a property

Define different behaviors

Other methods that depend upon behavior from parent class may fail

prone to errors
C++

use the virtual declaration in the parent class to indicate replacement

the virtual declaration introduces polymorphic methods (binding based on dynamic class)

without virtual declaration - binding based on static class
Smalltalk
74

no syntax provided to indicate replacement

constructors use refinement; all other methods use replacement (method in child with same
name as parent indicates replacement)
Java

can replace data fields

can use the final declarator to prohibit method replacement or subclassing
75
Implementation Issues
is_a
class Person {
public:
virtual void f();
private:
Name n;
}
class Professor : public Person {
public:
virtual void f();
private:
Classes c;
}
Professor is_a Person  variable of type Person should be able to hold values of type
Professor
Person per;
Professor prof;
Options for allocating memory for per:
1. Allocate only for the base: C++ uses this policy; what if we want to assign per = prof? The
extra fields are sliced off. Slicing information can be dangerous (C++ uses slicing - the c
data field of prof would be sliced
Member function binding: use static type (whether virtual or not); for virtuals with refs or
pointers use dynamic information
e.g. per = prof; per.f(); // Person
Person* pper;
Professor* pprof;
pper = &prof; pper->f(); // Professor
pprof = &prof; pprof->f();
// Professor
76
2. Allocate memory that can accommodate the largest subclass
This solves the slicing problem but often allocate too much space; must know all classes
before allocating
3. Allocate space for pointers
The data is on the heap; Smalltalk and Java use this option; the user explicitly allocates
space (e.g. Person p = new Person();)
Leads to pointer semantics for assignment and equality testing
Copy vs pointer semantics - make new copies of object or refer to the same copy
If x = y and change x then y is changed (they both refer to the same object)
If you free x then what happens to y????
C++ : overload the equal operator for any desired meaning
Smalltalk/Java : use pointer semantics and perhaps construct clones
Equality testing  recursively test fields
77
Covariance and Contravariance (Overriding methods)
Covariance: argument(s) are made more general  redefine method with an argument as e.g.
Person rather than Female
Contravariance: argument(s) are made more restrictive  redefine method with an argument
as e.g. Male rather than Person
Both of these can destroy the is_a relation; semantics are tricky; most languages forbid both
Covariant Return Types
You cannot have two methods in the same class with signatures that only differ by return type.
Until the J2SE 5.0 release, it was also true that a class could not override the return type of the
methods it inherits from a super class. This feature removes the need for excessive type
checking and casting. Consider the following examples, which help to describe the advantage
of this change to the Java language.
public class Tree {
...
public Tree getKid(int i) {...}
...
}
public class BinaryTree extends Tree {
public BinaryTree getKid(int i) {...}
public BinaryTree getLeftKid() {...} // getLeftKid is not defined in Tree
...
}
If you use a version of the JDK prior to J2SE 5.0, BinaryTree will not compile. However, with
J2SE 5.0 BinaryTree will compile. In fact, you would be able to do something like
...
BinaryTree bt = new BinaryTree(t1, t2);
...
BinaryTree btt = bt.getKid(2).getLeftKid();
Note that this avoids casting bt.getKid(2) to a BinaryTree prior to invoking getLeftKid
78
Multiple Inheritance
Consider: class Animal { … } class EndangeredSpecies { … }
class Leopard: public Animal, public EndangeredSpecies { … }
Classes Animal and EndangeredSpecies are orthogonal (their features/behaviors don’t overlap)
Problems:

Name ambiguity: class A{ .. void move(); … } class B { … void move(); … } class C: A,
B{…}
C c;
c.move(); // which move will be executed??

Common ancestor
Employee
Programmer
Manager
VicePresident
how many Employee subparts does a VicePresident possess??
Interfaces were included in Java to support multiple inheritance while avoiding the above
problems
79
Polymorphism (deals with types)
Variable polymorphism: variables hold values of different types.
Names might be associated with different function bodies
Functions with arguments that are polymorphic
Forms of Polymorphism

Variable: variables hold values from subclass type; the dynamic type must be a subclass of
the static type; C++ only allows pointer and reference values with polymorphic variables;
this is the basis for much of the power of OOP

Pure polymorphism: functions with polymorphic arguments
Consider the provision of looping mechanism with Smalltalk Collections
Collection
do: aBlock
LinkedList: Collection
|i|
i = self first.
(i nonNil) ifTrue:
[aBlock value: i.
first
Set: Collection
first
…
…
next
next
…
…
i = self next]
the argument aBlock is really any object that responds to the value: message (with one
argument)
80

Overloading: function names that can denote two or more function bodies
e.g. note + can denote integer addition or float addition or cout << “a string” << 5;
The type signature (number, ordering and type of arguments)determine the body.
The language can use either the static type signature or dynamic type signature.

Overriding: when child class changes the meaning of the function in the parent class
Different child classes can override in different ways
Parent class can have default behavior
Contributes to code sharing
Ensures common interfaces
e.g. in Java: public abstract class Component … { … public void addnotify(); … }
public class Button extends Component { … public void addnotify(); … }

Deferred methods: generalization of overriding; parent doesn’t have the body of the
method; child must have the body (else it’s an abstract class, too)
e.g. in C++
class Shape {
class Circle: public Shape {
…
…
void virtual draw() = 0;
…
}
void draw() { drawCircle(); }
…
}
Forces operating on polymorphism: execution efficiency vs ease of development
81
Visibility and Dependence
Visibility: attribute of names
Dependency: the degree to which one software component relies on another to perform its
responsibilities (limits code reuse)
Intentional Dependency: the dependee doesn’t know the dependent - e.g. consider the Model
View Controller design pattern where a dependency manager is used so when an object
changes, the manager is notified; the manager then notifies the registered dependents.
Coupling and Cohesion
Modules (Classes) should exhibit internal cohesion and minimize external coupling
Coupling: relationships between modules
least desirable
Internal Data Coupling
Global Data Coupling
Control (or sequence) Coupling
Parameter Coupling
Subclass Coupling
most desirable

Internal Data Coupling - One module modifies local data (e.g. instance variables) of
another ...BAD. Hard to understand and reason about.
82

Global Data Coupling - Modules use common global data structures...BAD. Should make a
class to manage data
e.g.
double temperature;
class A {
public modulateDevice() {
if (surfaceTemp > 50.5) temperature += 10.0;
}
}
class B {
public printTemp() {
System.out.println(“Current temperature: “ + temperature);
}
}
Note that with Global Data Coupling it is difficult to understand classes in isolation; each class
by itself is incomplete.

Control Coupling - Perform operations in a fixed order but the order is controlled by
another module. e.g. A database system does initializing, updating, etc. but other modules
call these routines (the database system should check sequencing of requests).

Parameter Coupling - the actual and formal parameters are coupled (not much problem
here)

Subclass Coupling - describes the sub/super-class relationship
83
Cohesion: relationships within a module
least desirable
Coincidental Cohesion
Logical Cohesion
Temporal Cohesion
Communication Cohesion
Sequential Cohesion
Functional Cohesion
Data Cohesion
most desirable

Coincidental Cohesion - no apparent reason for grouping elements -- poor design (e.g. a
class has unrelated methods)
Example:
public class Mailbox {
public addMessage(Message aMessage) {...}
public Message removeCurrentMessage() {...}
public Message getCurrentMessage() {...}
public void processCommand(String command) {...}
...
}
Note how processCommand seems to be quite different from the other methods – the other
methods deal with a single abstraction: a mailbox that holds messages. Since there are
many issues that must be dealt with when considering commands, it would be best to use
another class for that abstraction.

Logical Cohesion - a logical connection among elements but no data or control connection
(e.g. consider a math library containing functions for sin, cos, etc. )

Temporal Cohesion - elements are used at about the same time (e.g. program level
initializations -- maybe we should disburse the initialization activities to appropriate
modules)
84

Communication Cohesion - all elements access same input/output data or devices; the
class acts as a manager for the data (device)

Sequential Cohesion - to avoid sequential coupling; the elements must activate in a
particular order.

Functional Cohesion - elements are all related to performing a single function

Data Cohesion - e.g. when a class is used to implement a data abstraction
Closely associated with the notions of coupling and cohesion is the Law of Demeter (K.
Lieberherr, I. Holland, and A. Riel, Object-Oriented Programming: An Objective Sense of
Style, Proc. ACM OOPSLA 1988 conf., San Diego, California, September 1988). This is stated
as follows:
Law of Demeter
For all classes C, and for all methods M attached to C, all objects to which M sends a message
must be instances of classes associated with the following classes:
1. The argument classes of M (including C).
2. The instance variable classes of C.
(Objects created by M, or by functions or methods which M calls, and objects in global
variables are considered as arguments of M.)
Strong Law of Demeter
The Strong Law of Demeter defines instance variables as being ONLY the instance variables
which make up a given class. Inherited instance variable types may not be passed messages.
This Strong Law of Demeter means that a method should only use:

Instance fields of its class
85

Parameters

Objects that it constructs with new
Thus, the following situation, where an accessor is used to return a component upon which a
class is constructed, is not acceptable:
Consider the use of a MailSystem with a findMailbox method to yield a mailbox object, mbox.
Subsequently, the Connection method might request the mbox to add/delete messages. This
would break the encapsulation of the MailSystem. Perhaps a future version might use, e.g., a
database to hold messages. In this case, the developer would have to manufacture Mailbox
objects for backward compatibility!
A possible solution would be to have the Connection object request services of the MailSystem,
which in turn would delegate the message to the Mailbox object it maintains. Although this
would be a bit tedious and time-consuming, there are compilation techniques that can be used
to optimize the process.
Weak Law of Demeter
The Weak Law of Demeter defines instance variables as being BOTH the instance variables
which make up a given class AND any instance variables inherited from other classes.
Note that each of the above variants rules out one object directly manipulating internal data
values of another object  must use methods to effect change of state of other objects
Refer to the aforementioned article, as well as the article [M.Sakkinen, Comments on "The Law
of Demeter" and C++, SIGPLAN NOTICES,v.23 (12), December, 1988] for further discussion
on these issues of programming style.
86
Overriding versus Shadowing
Shadowing:
class A {
int x;
public void m(int x) { // this parameter ‘x’ shadows the instance variable x
int y = x+1;
// we can refer to the instance variable ‘x’ as this.x
while (y > 0) {
int x = 2;
// this ‘x’ shadows the parameter ‘x’
y -= 1;
}
}
}
class Parent {
int x = 1;
}
class Child extends Parent {
int x = 5; // this x shadows the x in the Parent
}
Parent p = new Parent();
System.out.println(p.x);
1
Child c = new Child();
System.out.println(c.x);
5
p = c;
System.out.println(p.x);
1
87
Overriding: Accessibility and Exceptions
When you override a method, you cannot make it less accessible – e.g., if a method in a
superclass is public then you cannot make the method in the subclass private. Consider the
following situation:
public class Parent {
...
public void print() {...}
...
}
public class Child extends Parent {
...
private void print() {...}
...
}
Parent p = new Parent();
p.print(); // this is fine
p = new Child(); // variable polymorphism allows this
p.print();
// since dynamic binding is used in Java, it expects the print method in
// the class associated with the object referred to by p!!
When you override a method, you cannot throw more checked exceptions than are already
declared in the superclass method.
public class Parent {
...
...
public void print() throws RuntimeException {...}
...
}
}
public class Child extends Parent {
...
public void print() throws RuntimeException, SecurityException {...}
...
}
Parent p = new Parent();
try {
p.print();
} catch (RuntimeException e) {} // this is fine
p = new Child();
try {
p.print();
} catch (RuntimeException e) {} // this would not compile!!
88
...
C++: Requires an explicit indication of overriding; it will perform a shadowing if the virtual
reserved word is not provided.
class Parent {
public:
void example() { cout << “Parent”<<endl; }
}
class Child: public Parent {
public:
void example() { cout << “Child”<<endl; }
}
Parent *p = new Parent();
p->example();
Parent
Child *c = new Child();
c->example();
Child
p = c;
p->example();
Parent
We can use overriding by declaring example as a virtual function in Parent.
Overriding: The type signatures are the same in both parent and child classes, and the method
is declared as virtual in the parent class.
Shadowing: The type signatures are the same in both parent and child classes, but the method
was not declared as virtual in the parent class.
Redefinition: The type signature in the child class differs from that given in the parent class.
89
A Note on Protected Variables and Methods
Consider a Shape hierarchy:
public class CompoundShape {
private GeneralPath path;
public CompoundShape() { path = nwe GeneralPath(); }
protected void add(Shape s) { path.append(s); }
...
}
public class HouseShape extends CompoundShape {
public HouseShape(...) {
Rectangle2d.Double base = ...;
add(base);
...
}
...
}
Note that the CompoundShape class provides the add method as part of its interface provided
to subclasses. Thus, we distinguish two kinds of interfaces – the public interface (allowing
any client to access the resource) and the protected interface (allowing subclasses access to
resource).
However, it would not be desirable for malicious subclasses to misuse the protected interface.
With this in mind, the following security measure applies: methods can use protected features
only on objects of their own class. This is to prevent the following attack:
public Attacker extends CompoundShape { // tries to call protected add method
void uglify(HouseShape house) {
...
house.add(aShape); // won’t work – can only call add on other Attacker objects
}
}
It would not be advisable to change the modifier for the path instance variable to protected
from private. This would allow other classes in the same Java package to access the variable!
90
Frameworks and Patterns
Use groups of classes already organized for various purposes.
Application framework

series of classes that describe a skeleton application

provides structure, not content

subclass and override to provide content

extensive use of polymorphism
e.g. Microsoft Foundation Class Framework includes a DropDownList class; the user can
subclass this class (e.g. MyDropDownList) to add new behavior.
Contrast a Framework with a library:
Main
program
User Code
Framewor
k
Library code
Framework
Library
User Code
The Framework has real code; just add code to change and embellish its behavior
Design patterns describe how to organize classes to solve problems - e.g. MVC
91
Another Look at Classes

They are templates for making clones of some object

They are generalizations of records

It’s the mechanism used for encapsulation

It’s a special kind of type

It’s a special kind of object:
As an object it has the following responsibilities:

Maintains information about the class (e.g. instance variables, methods)

Generates instances

If a class is an object and every object is an instance of a class  what is the class of a
class?
A metaclass is the class of a class; this suggests another class hierarchy, which includes
metaclasses, which help describe more aspects of an Object Oriented Programming
Language. The use of metaclasses provides the capability to implement a more fluid
language - the behavior of classes is provided by the user (most likely, still the
implementer) of the language rather than the implementers of the language. This allows
users to augment the basic behavior of classes for language experimentation. Implementers
will be presented with a less complex task (e.g. for porting). Finally, the documentation of
these aspects is provided in the language of interest, rather than externally by the
implementor.
92
OOP AND SOFTWARE ENGINEERING
ABSTRACTION





extract essential details
omit inessential details
each level of decomposition represents an abstraction
each level must be completely understood as a unit
our view of the world forms ladders of abstraction
INFORMATION HIDING
(used to support the management of abstractions)




make details of an implementation inaccessible
enforce defined interfaces
focus on the abstraction of an object by suppressing the details
prevent high level decisions from being based on low level characteristics
93
ABSTRACTION AND INFORMATION HIDING


supported by OOP's CLASS mechanism
directly supports the management of complexity
LOCALIZATION



physical proximity
collection of logically related entities
definitions of program entities should appear local (close) to the uses of the entities
94
Software Engineering Concepts
SURFACE AREA
The SURFACE AREA of a programmer's code is the amount of information
that must be understood and properly dealt with for another programmer
to use it correctly.
SMALL IS GOOD; LARGE IS BAD
FACTORS INFLUENCING SURFACE AREA
1. Information hiding -- minimize the number of names that a consumer
must know to use a supplier's code correctly.
2. Function/procedure profiles -- users of functions/procedures must
know number/types/order of parameters, return type(s)
3. Timing -- if a consumer must remember to allocate object then
initialize then use then dispose
4. Concurrency -- consumer must be aware of any timing problems.
HARDWARE IC's:

the chip manufacturers can deliver a tightly encapsulated unit of functionality;
independent of any particular application; it's reusable
SOFTWARE IC's:



objects are tightly encapsulated; designed, developed, documented independent of any
particular application; it's reusable
consider a SET IC or SYMBOL TABLE IC or OFFICE MAILBOX IC.
consumers can shop for objects with the best features for their applications.
95
SOFTWARE QUALITY
External factors: qualities observed by users
- correctness
perform the tasks as defined by requirements and specifications
- robustness
performance under abnormal conditions
- extensibility
ease with which it can be adapted to changes in specs
need:
design simplicity
decentralization
minimize module interdependence
- reusability
reuse for new applications
- compatibility
easy to combine with other software
(e.g. that's why file formats, etc. are standardized)
- efficiency (of resource usage)
- portability
to new hardware/software environments
- integrity
protection against unauthorized access/modification
- ease of use
96
MODULES
1. The program design language must have a strong relation to the language used to
implement the design
2. Every module should communicate with as few others as possible
3. If the modules communicate, they should exchange as little info as possible
4. If modules communicate ==> it should be obvious from text
5. All info should be private unless specifically declared to be public
97
TOP-DOWN VS OBJECT-ORIENTED DESIGN
TOP-DOWN:
 stepwise refinement e.g. translate a C++ program into VAX code
 faults
 doesn't support extensibility
 addresses tasks rather than data structures
 does not promote reusability
 subtasks are too strongly coupled - e.g. to do the main task we must do subtask
A then subtask B then ... Clearly, changes to A will require a rippling effect of
changes to B, etc.
OBJECT-ORIENTED:





most real systems (e.g. operating systems) don't have a single top level function; rather
they are a collection of subsystems working together
systems are modularized on the basis of their data structures
a class may be defined as an extension or restriction of another
program entities should be permitted to refer to objects of more than one class, and
operations should be permitted to have different realizations in different classes
(polymorphism/dynamic binding support these requirements)
it should be possible to declare a class as heir to more than one class, and more than
once to the same class (multiple inheritance)
98
Unit Testing
Quality Assurance experts prior to release test entire applications. It is also important for
developers to develop test harnasses to test smaller units of code such as classes. It is critical
for the development environment to provide support for this unit testing. The support includes
the provision of a test infrastructure that is integrated in the develoment environment. A
popular Java OpenSource project that has been developed for unit testing is the JUnit project
(http://www.junit.org/index.htm). JUnit is available as a plugin to eclipse. Refer to the section
in the Help menu for information on running JUnit (JUnit Cookbook).
The following unit test was provided to check the Lexical Analyzer module included in a
compiler project:
/*
* Created on Dec 1, 2003
*
* To change the template for this generated file go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
package lexer;
import junit.framework.TestCase;
/**
* @author Dr. Barry Levine
*
* To change the template for this generated type comment go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
public class LexerTest extends TestCase {
Lexer lex;
/**
* Constructor for LexerTest.
* @param arg0
*/
public LexerTest(String arg0) {
super(arg0);
}
public static void main(String[] args) {
junit.swingui.TestRunner.run(LexerTest.class);
}
/*
* @see TestCase#setUp()
*/
protected void setUp() throws Exception {
super.setUp();
try {
lex = new Lexer
("D:\\compiler\\simple.x");
}catch (Exception e) {fail();
}
}
99
/*
* @see TestCase#tearDown()
*/
protected void tearDown() throws Exception {
super.tearDown();
}
/*
* Main test harnass – only used when running test suites
*/
public void testLexer() {
Token tok = null;
try {
while (true) {
tok = lex.nextToken();
String p =
"L: "
+ tok.getLeftPosition()
+ "R: "
+ tok.getRightPosition()
+ " "
+ TokenType.tokens[tok.getKind()];
if (tok.getKind() == Sym.Identifier) {
p += tok.toString();
} else if (tok.getKind() == Sym.INTeger) {
p += tok.toString();
}
System.out.println(p + ": " + lex.getSourceLineNumber());
}
} catch (Exception e) {
assertEquals(tok, null);
}
}
}
}
Note that the test harnass is
project with/without the test
changes affecting the classes
convenient to re-run the test
the eclipse documentation for
included in the project. Eclipse can run the
harnass. Therefore, when there are code
associated with the test harnass it is very
prior to building the project. Please refer to
details on using JUnit.
100
Squeak/Smalltalk - 80
<<<CH2-TOUR.PPT>>>
<<<ch3-joe-sum02.ppt>>>
EVERYTHING is an object.
A COMPUTATION == MESSAGE passing to objects to cause execution of METHODS.
PROTOCOL for a class == set of messages for the class.
101
Identifiers:
- instance variables
- Class names (begin with a cap letter)
- pseudo variables (self, super, nil, smalltalk, args for a method
Message Expressiongs
Message expr == receiver + message selector + args
Unary: no args, parse left to right
3 factorial == 6
3 factorial sqrt == 2.4..
Binary: 1 arg, 1 or 2 adjacent non-alpha chars, parse left to right
7 + 4 == 11 (7 is receiver, + is message, 4 is arg)
7+4*3 == 33
Unary has greater precedence than Binary
7+4 sqrt == 9.0
Keyword: 1 or more keywords (id:) ; 1 arg per key, lowest precedence
7 max:5 == 7
7 max: 4 max: 3 ==> must use parens: (7 max:4)max:3
8 between: 4 sqrt and: 6 + 8
==> 8 between: (4 sqrt) and: (6+8)
Blocks: [ statementsSeparatedByPeriod ] --like a function body
--use static scoping for variables
--a procedural abstraction
[i<-i+1. i print]
BLOCKS ARE OBJECTS and can be used wherever objects are needed
[...] value ==> evaluate block
the value of the block is the last value computed
[:p1 :p2 ...:pn | statements] value:a1 ... value:an
102
SELF
"self" ALWAYS refers to the ORIGINAL receiver of the message. The justification for this
follows:
A--->B--->C
Consider the class hierarchy where A is a superclass of B and B is
a superclass of C. With respect to the "is-a" relation a C is-a B
is-a A. Thus, C inherits the behaviour (protocol, methods) of B and A.
Suppose a C object (objC) is sent a message and somehow a method that
belongs to B (methB) starts executing. Since C "owns" all of its
inherited behaviour, it owns methB. Therefore, if methB references
"self" it is clearly talking about objC, the ORIGINAL receiver of
the message.
103
Class Variables
Object subclass: #ClassVariableTest
instanceVariableNames: ''
classVariableNames: ' cl '
poolDictionaries: ''
category: nil.
!
!ClassVariableTest class methodsFor: 'set class variable'!
cl: val
cl _ val
!
cl
^cl
!!
!ClassVariableTest methodsFor: 'all'!
cl
^cl
!!
ClassVariableTest cl: 5.
'class variable set to 5...it is: ' print.
ClassVariableTest cl printNl.
'class variable set to 1...it is: ' print.
ClassVariableTest cl: 1.
ClassVariableTest cl printNl.
" use ! following all Smalltalk messages "
Smalltalk at: #t put: ClassVariableTest new!
'instance accessing class variable...value: ' print.
t cl printNl
!
104
Generators
- yield elements in a collection
- messages are first, next -- return nil (undefined) when done;
once we start generating items DON'T modify the collection.
Consider the following (modified) Interval class drawn from the Gnu Smalltalk-80 system:
SequenceableCollection subclass: #Interval
instanceVariableNames: 'start stop step
iterVar'
classVariableNames: ''
poolDictionaries: ''
category: nil.
Interval comment:
'My instances represent ranges of objects, typically Magnitude type objects. I provide
iteration/enumeration messages for producing all the members that my instance represents.' !
!Interval class methodsFor: 'instance creation'!
from: startInteger to: stopInteger by: stepInteger
| int |
^self new initializeFrom: startInteger to: stopInteger by: stepInteger
!
from: startInteger to: stopInteger
^self from: startInteger to: stopInteger by: 1
!!
!Interval methodsFor: 'basic'!
first
iterVar _ start.
^ iterVar
!
next
iterVar _ iterVar + step.
^(step > 0) ifTrue: [ (iterVar > stop) ifFalse: [ iterVar ] ]
ifFalse: [ (iterVar < stop) ifFalse: [iterVar] ]
!
105
do: aBlock
|i|
i _ self first.
(i notNil) whileTrue: [ aBlock value: i. i _ self next ]
!
size
step > 0
ifTrue: [
stop >= start ifTrue: [ ^(stop - start) // step + 1 ]
ifFalse: [ ^0 ]
]
ifFalse: [
start >= stop ifTrue: [ ^(start - stop) // step + 1 ]
ifFalse: [ ^0 ]
]
!
add: newObject
self error: 'elements cannot be added to an Interval'
!!
!Interval methodsFor: 'testing'!
= anInterval
^(start = anInterval start) &
(stop = anInterval stop) &
(step = anInterval step)
!!
!Interval methodsFor: 'private methods'!
initializeFrom: startInteger to: stopInteger by: stepInteger
start _ startInteger.
stop _ stopInteger.
step _ stepInteger
!
start
^start
!
stop
^stop
!
step
^step
!!
106
CLASSES AND METACLASSES
Classes are used to describe the behaviour of similar objects (the instances) while metaclasses
are used to describe the behaviour of a single object -- the class. Each class designation will
include the description of the behaviour of the instances (the class description) and the
description of the behaviour of the class object, itself (the metaclass description). In Smalltalk
80 the behaviour of the instances is described using the following form (consider the Date
class as an example):
Magnitude subclass: #Date
instanceVariableNames: ‘days’
classVariableNames: ‘’
poolDictionaries: ‘’
category: nil.
!
!Date methodsFor: ‘basic’!
addDays: dayCount
days _ days + dayCount
!
subtractDate: aDate
^days - aDate days
!!
Note that the methodsFor: message is sent to the Date class. This instructs the interpreter to
include the basic category as part of the class description and add the methods below (until the
double bang....!!) as instance methods (the methods to be added are addDays: and
subtractDate:). Again, the class description describes the behaviour of instances of the class.
On the other hand, the behaviour of the class is described using the following form:
!Date class methodsFor: ‘instance creation’!
today
| now date |
now _ Time secondClock.
date _ now / (24 * 60 * 60).
^self new setDays: date + 25202 "(69 * 365 + 17)"
!!
107
This time the Date class message yields the class in which Date belongs (its metaclass). Then
we pass the methodsFor: message to include the category instance creation as part of the
metaclass description and add the method today. Again, the metaclass description describes the
behaviour of the only instance of the metaclass -- the Date class (indicating how Date instances
should be created)
Note that the inclusion of metaclasses indicates the necessity of parallel hierarchies; the class
hierarchy and the metaclass hierarchy. Now we will consider partial descriptions of the Date
and Magnitude (Date’s superclass) classes. All source code descriptions of system classes is
accessible in the blevine directory. You should peruse these descriptions to help you learn
about Smalltalk 80 programming.
"==============================================================
|
|
Magnitude Method Definitions
|
=============================================================="
"==============================================================
|
| Copyright (C) 1990, 1991 Free Software Foundation, Inc.
| Written by Steve Byrne.
|
| This file is part of GNU Smalltalk.
|
| GNU Smalltalk is free software; you can redistribute it
| and/or modify it
| under the terms of the GNU General Public License as
| published by the Free
| Software Foundation; either version 1, or (at your option)
| any later version.
|
| GNU Smalltalk is distributed in the hope that it will be
| useful,but WITHOUT
| ANY WARRANTY; without even the implied warranty of
| MERCHANTABILITY or FITNESS
| FOR A PARTICULAR PURPOSE. See the GNU General Public License
| for more details.
|
| You should have received a copy of the GNU General Public
| License along with
| GNU Smalltalk; see the file COPYING. If not, write to the
| Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
| USA.
=============================================================="
"
|
Change Log
108
|
===============================================================
| Author
Date
Change
| sbyrne
25 Apr 89
created.
|
"
Object subclass: #Magnitude
instanceVariableNames: ‘’
classVariableNames: ‘’
poolDictionaries: ‘’
category: nil.
Magnitude comment:
‘I am an abstract class. My objects represent things that are
discrete and map to a number line. My instances can be
compared with < and >.’ !
!Magnitude methodsFor: ‘basic’!
"Relational operators.
Object"
‘==’, ‘~=’, ‘~~’ are inherited from
= aMagnitude
self subclassResponsibility
!
< aMagnitude
self subclassResponsibility
!
> aMagnitude
self subclassResponsibility
!
<= aMagnitude
^(self < aMagnitude) | (self = aMagnitude)
!
>= aMagnitude
^(self > aMagnitude) | (self = aMagnitude)
!!
!Magnitude methodsFor: ‘misc methods’!
between: min and: max
"Returns true if object is inclusively between min and
109
max."
^(self >= min) and: [ self <= max ]
!
min: aMagnitude
self < aMagnitude ifTrue: [ ^self ]
ifFalse: [ ^aMagnitude ]
!
max: aMagnitude
self > aMagnitude ifTrue: [ ^self ]
ifFalse: [ ^aMagnitude ]
!!
"==============================================================
|
|
Date Method Definitions
|
=============================================================="
"
"
|
Change Log
|
===============================================================
| Author
Date
Change
| sbyrne
25 Apr 89
created.
|
"
Magnitude subclass: #Date
instanceVariableNames: ‘days’
classVariableNames: ‘‘
poolDictionaries: ‘‘
category: nil.
Date comment:
‘My instances represent dates. My base date is defined to be
Jan 1, 1901.I provide methods for instance creation (including
via "symbolic" dates, such as "Date newDay: 14 month: #Feb
year: 1990"‘ !
Smalltalk at: #DayNameDict put: Dictionary new!
Smalltalk at: #MonthNameDict put: Dictionary new!
!Date class methodsFor: ‘basic’!
initialize
self initDayNameDict.
110
self initMonthNameDict
!
initDayNameDict
| dayNames |
dayNames _ #(
(monday mon)
"1"
(tuesday tue)
"2"
(wednesday wed) "3"
(thursday thu) "4"
(friday fri)
"5"
(saturday sat) "6"
(sunday sun)
"7"
).
1 to: dayNames size do:
[ :dayIndex | (dayNames at: dayIndex) do:
[ :name | DayNameDict at: name put: dayIndex ] ].
!
initMonthNameDict
| monthNames |
monthNames _ #(
(january
jan)
"1"
(february feb)
"2"
(march
mar)
"3"
(april
apr)
"4"
(may)
"5"
(june
jun)
"6"
(july
jul) "7"
(august
aug)
"8"
(september sep)
"9"
(october
oct)
"10"
(november nov)
"11"
(december dec)
"12"
).
1 to: monthNames size do:
[ :monthIndex | (monthNames at: monthIndex) do:
[ :name | MonthNameDict at: name put: monthIndex
] ].
!
dayOfWeek: dayName
^DayNameDict at: dayName asLowercase asSymbol
!
nameOfDay: dayIndex
^#(Monday Tuesday Wednesday Thursday Friday Saturday
Sunday) at: dayIndex
!
111
nameOfMonth: monthIndex
^#(January February
April
May
July
August
October November
!
March
June
September
December) at: monthIndex
daysInMonth: monthName forYear: yearInteger
| monthIndex |
monthIndex _ self indexOfMonth: monthName.
^self daysInMonthIndex: monthIndex forYear: yearInteger
!
daysInYear: yearInteger
^365 + (self leapYear: yearInteger)
!
leapYear: yearInteger
(yearInteger \\ 4 = 0
and: [ yearInteger \\ 100 ~= 0
or: [ yearInteger \\ 400 = 0 ] ])
ifTrue: [ ^1 ]
ifFalse: [ ^0 ]
!!
!Date class methodsFor: ‘instance creation’!
today
| now date |
now _ Time secondClock.
date _ now / (24 * 60 * 60).
^self new setDays: date + 25202 "(69 * 365 + 17)"
!!
!Date class methodsFor: ‘private methods’!
yearAsDays: yearInteger
"Returns the number of days since Jan 1, 1901."
yearInteger _ yearInteger - 1900.
^(yearInteger - 1) * 365
+ (yearInteger // 4)
- (yearInteger // 100)
+ (yearInteger // 400)
112
!
daysInMonthIndex: monthIndex forYear: yearInteger
| days |
days _ #(31 28 31
"Jan Feb Mar"
30 31 30
"Apr May Jun"
31 31 30
"Jul Aug Sep"
31 30 31
"Oct Nov Dec"
) at: monthIndex.
monthIndex = 2
ifTrue: [ ^days + (self leapYear: yearInteger) ]
ifFalse: [ ^days ]
!!
!Date methodsFor: ‘basic’!
addDays: dayCount
days _ days + dayCount
!
subtractDate: aDate
^days - aDate days
!!
!Date methodsFor: ‘private methods’!
days
^days
!
setDays: dayCount
days _ dayCount
!!
113
Lets consider the hierarchy including Date, Magnitude and Object (partial protocols are
included to aid in distinguishing between classes and their metaclasses). Note that gray arrows
indicate the instance relation, whereas, black arrows indicate the subclass/superclass relation.
Everything is an Object. We need to extend the hierarchy to include classes that describe the
behaviour of all classes. This enables us to deal with the behaviour in common with all classes.
The following hierarchy extends the one given above to include classes/metaclassses to deal
with behaviour in common with all classes. Page 272 of the text shows how the entire
hierarchy is put together.
114
Note the circularity between Metaclass and Metaclass Metaclass. Since every metaclass is an
instance of the Metaclass class we must include the arrow to the left. But we also have every
class is the single instance of its metaclass so we must include the arrow to the right.
115
Reflection and Persistence (Appendix 5)
Reflection
Abstraction principle:
The implementation of a module should be independent of its
interface.
Next to modularity, abstraction is probably the most important principle in engineering for
controlling the complexity of large systems. A module that hides its implementation from its
clients is called a black box module - easier to use and easier to replace.
Reflective module - clients can inspect and alter some aspects of its implementation.
Reflective Systems
A reflective or open implementation (OI) system:
1. Base level - objects provide application services through the system's normal user
interface.
2. Meta level – encapsulate policies, structures, and strategies used by a base-level object
to implement these services
116
A meta interface or meta object protocol (MOP) allows clients - users and base-level objects
- to access meta-level objects. Through the meta interface a client can inspect, modify, or
replace a meta-level object, thus dynamically changing the behavior of the associated baselevel objects.
Example: Reflective Interpreters and Compilers
LISP programs are executed directly by programs called interpreters. Executing a program
involves many policies that determine the semantics of the language. These policies
determine things such as how parameters are passed (reference, value, name, etc.), how
assignments are made (shallow or deep copy), how non-local variables are located (static or
dynamic scoping), procedure call evaluation algorithms (lazy or eager), etc. Adding reflection
(i.e., a meta level) to an interpreter allows programs and users to modify these policies to
optimize performance:
C++ keywords such as virtual, inline, and register can be seen as part of the C++ compiler's
meta interface.
Example: Reflective Operating Systems
117
Reflective operating systems can provide process meta interfaces to expose scheduling and
synchronization policies, memory management meta interfaces like Mach's External
Memory-Management Interface (EMMI) to expose page fetch and replacement policies, and
file system meta interfaces like AFS to expose buffer management and disk head scheduling
policies.
Example: Database Systems
All database systems are reflective systems - database includes a system catalog (also called
a data dictionary or a data directory) that contains meta-data, or data about the data in the
database. For example, the data in a relational database is organized into tables. The system
catalog is a meta-level table that contains a row describing each base-level table. The
information in this row might include:
1. Names, types, and sizes of each column of the base-level
table.
2. Names of relationships to other base-level tables.
3. Names of users authorized to access the base-level table.
4. Constraints on the data in the base-level table.
5. Usage statistics.
For example, the system catalog might include a table of the form:
In addition, the system catalog might also tell us that salaries must be non-negative, etc.
118
Applications and users query a database through a database management system (DBMS). The
DBMS uses a component called the catalog manager to consult the database's system catalog
to determine how to answer the query.
Strategies
Strategy - encapsulates some application-wide policy or algorithm. For example, a strategy
object for a network might encapsulate a buffering policy, a routing algorithm, or a congestion
control algorithm.
Strategy [Go4], [WWW 5]
Other Names
Policy
Problem
An application domain policy may affect the behavior of application objects belonging to many different
classes. For example, a government taxation policy affects the behavior of consumers, investors, and
firms. The policy may change often, or the policy may be different for different instances of the same
class.
Solution
Encapsulate the policy in a separate meta-level object. Each application (i.e., base-level) object maintains
a reference to a policy object. Changing the policy object changes the behaviors of all associated
application objects.
Contrast the strategy pattern with the decorator pattern:
119
Gamma, et. al. [Go4] express the comparison more colorfully:
"A decorator lets you change the skin of an object; a strategy
lets you change the guts."
Static Structure
Strategies are instances of classes derived from an abstract strategy base class, which specifies
the meta interface.
Java example: layout containers.
class GUI extends JFrame {
public GUI() {
Container contentPane = getContentPane();
contentPane.add(new Button("STOP")):
contentPane.add(new Button("GO"));
// etc.
}
}
How does the add() method know where to add the controls? Layout manager is associated
with awt's Container class:
setLayout(new FlowLayout());
Example - financial application value() computes the future value; depends on the risk and
yield of the selected investment strategy:
120
Runtime Classes
Refer to “CLASSES AND METACLASSES” section in the Smalltalk chapter of the course
reader.
Runtime class: Meta-level object that represents a class.
class Class { ... }
// instances are runtime classes
Class rtc = new Class(...);
or
Class rtc = Class.forName(...);
Ex.
Appliance app = new Refrigerator(...)
Class rtc = app.getClass();
rtc points to an instance of Class that represents the Refrigerator class.
String name;
System.out.println("Enter a class name: ");
String name = System.in.readLine();
Class rtc = Class.forName(name); // allows for more dynamic
// activities
What is the runtime class of a runtime class?
Class rtc2 = rtc.getClass(); // rtc2 = ???
rtc2 points to a runtime class that represents the Class class, as does rtc2.getClass()!
Services of a runtime class include runtime type information.
Class rtc = app.getClass();
if (rtc.getName() == "Refrigerator")
((Refrigerator)app).getTemperature();
Class rtc2 = rtc.getBaseClass();
System.out.println(rtc.getName()); // prints "Appliance"
rtc2 = rtc.getClass();
System.out.println(rtc2.getName()); // prints "Class"
Dynamic instantiation:
Appliance app2 = rtc.newInstance();
This is useful for framework factory methods that must construct objects without knowing their
identity in advance.
class Method { ... };
class Field { ... };
121
Method[] funs = rtc.getMethods();
Field[] vars = rtc.getFields();
This information, combined with dynamic creation, can be used to automate the process of
reading and writing base-level objects to files and databases.
Runtime Type Information in Java
For example:
Document x = new Report();
Class c = x.getClass();
System.out.println("class of x = " + c.getName());
c = c.getSuperclass();
System.out.println("base class of x = " + c.getName());
c = c.getSuperclass();
System.out.println("base of base class of x = " + c.getName());
x = new Memo();
c = x.getClass();
System.out.println("now class of x = " + c.getName());
produces the output:
class of x = Report
base class of x = Document
122
base of base class of x = java.lang.Object
now class of x = Memo
class Document
{
public int wordCount = 0;
public void addWord(String s) { wordCount++; }
public int getWordCount() { return wordCount; }
}
Document x = new Document();
Class c = x.getClass();
x.addWord("The");
x.addWord("End");
Method methods[] = c.getMethods();
for(int i = 0; i < methods.length; i++)
System.out.println(methods[i].getName() + "()");
Field fields[] = c.getFields();
try
{
System.out.print(fields[0].getName() + " = ");
System.out.println(fields[0].getInt(x));
}
catch(Exception e)
{
System.out.println("fields[0] not an int");
}
produces the output:
getClass
hashCode
equals
toString
notify
notifyAll
wait
wait
wait
addWord
getWordCount
wordCount = 2
Notice that the methods inherited from the Object base class were included in the array of
Document methods. (There are three overloaded variants of the wait() function in the Object
class.) If wordCount is declared private, as it normally would be, then its value doesn't
appear in the last line:
wordCount =
Dynamic Instantiation
Frequently occurs when we are developing frameworks that must create instances of classes
that will be specified in customizations of the framework.:
Factory Method [Go4]
123
Other Names
Virtual constructor.
Problem
A "factory" class can't anticipate the class of "product" objects it must create.
Solution
Provide the factory class with an ordinary member function that creates product objects. This is called a
factory method. The factory method can be a virtual function implemented in a derived class, or a
template function parameterized by a product constructor.
Dynamic Instantiation in Java
String cName; // holds a class name
Object x;
Class c;
try
{
cName= "Horn";
c = Class.forName(cName); // find & load a class
x = c.newInstance();
c = x.getClass();
System.out.println("class of x = " + c.getName());
cName= "Drum";
c = Class.forName(cName);
x = c.newInstance();
c = x.getClass();
System.out.println("now class of x = " + c.getName());
}
catch(Exception e)
{
System.out.println("Error: " + e);
}
produces the output:
class of x = Horn
now class of x = Drum
Calling Class.forName("Horn") locates the file containing the declaration of the Horn class,
compiles it if necessary, then dynamically links it into the program.
Dynamic Instantiation in C++ (The Prototype Pattern)
Unfortunately, C++ doesn't have built-in support for dynamic instantiation, but the prototype
pattern provides a standard way to add this feature to our C++ programs:
Prototype [Go4]
Problem
A "factory" class can't anticipate the class of "product" objects it must create.
124
Solution
Derive all product classes from an abstract product base class that provides a factory method that uses a
type description parameter to determine the type of product to create. The type description parameter is
used to locate a prototype, which is then copied, customized, and returned by the factory method.
Prototype Table
class Product {
String getClass() const;
abstract Product clone();
static Product makeProduct(String type); // factory function
static Product addPrototype(Product p);
private static Table protoTable;
};
Product makeProduct(string type) {
Product proto;
if (find(type, proto, protoTable))
return proto->clone();
else
return null;
}
125
Persistence
An object that can be saved to and restored from a file or database is called a persistent object.
Objects that can't be saved and restored are called transient objects (e.g., windows and
menus).
Databases
meta-data or the system catalog - information about its own structure.
Database management system (DBMS), first determines if the user is authorized to access the
database and if the request is consistent. If so, then the meta-data is used to devise a strategy for
retrieving the required data, the data is retrieved and sent back to the user.
Find all calculus students.
Eliminate those who are not taking calculus from Professor
Smith.
Eliminate all information about these students except name and
GPA.
Relational Databases
Relational databases - organized into tables or relations
INGRES, ORACLE, Access, FoxPro, Informix, Sybase, and DB2 are examples of relational
database management systems (RDBMS).
Records (rows), represent objects
Attributes (columns)
Example
Three tables represent Student, Teacher, and Course. Each student takes exactly three courses
per term, and each teacher teaches exactly three courses per term:
126
Rows represent students; entry in a given row and column is called an attribute value:
Candidate key - attribute that is unique for each record (e.g., ID)
Primary key -a selected candidate key
Foreign key - a candidate key in another table; used to express links between records, hence
associations between classes.
Instructor column - foreign keys (ID numbers of teachers in the Teacher table)
SQL
"Structured Query Language" - ISO standard language for defining and manipulating relational
databases.
SELECT
INSERT
UPDATE
DELETE
...
...
...
...
To
To
To
To
query data in a database
insert rows into a table
update rows in a table
delete rows from a table
Select is the most common command. Its basic format is:
SELECT column, column, ...
FROM table, table, ...
WHERE condition
127
For example, the query:
"What are the names of all students who have at least a 2.0
grade point average and who take calculus during first period?"
can be expressed in SQL by:
SELECT LastName, FirstName
FROM Students
WHERE Period1.Title = "Calculus" AND 2.0 <= GPA
Result is a new table created from the tables listed in the FROM clause. Columns of the result
table are those listed in the SELECT clause. The rows of the result table are those meeting the
condition specified in the WHERE clause. If there is no WHERE clause, then no rows are
filtered from the result table.
Tables that are explicitly stored in the database are called base tables. Tables constructed from
executing queries are called virtual tables or views. The tables listed in the FROM clause
might be base tables or virtual tables.
Interfaces to Relational Databases
Open Database Connectivity (ODBC) is a standard RDBMS API being proposed by Microsoft.
To implement ODBC, RDBMS vendors provide a driver in the form of a DLL (Dynamic Link
Library) that interfaces with the RDBMS API and interprets ODBC function calls.
Another way DBMS vendor-dependency can be eliminated is by using embedded SQL.
Embedded SQL allows programmers to embed SQL statements directly into their source code.
Subsequently, a preprocessor replaces them with calls to vendor-specific API functions that
directly access the RDBMS.
Object Oriented Databases and ODMG 2.0
Object databases are collections of objects organized into classes that are related by
association and specialization.
Object Database Management Group (ODMG) - define standards for object oriented
database management systems (OODBMS). Version 2 of the standard (ODMG 2.0)
appeared in 1997.
Object fault - transparently locates the requested object using the object's unique object
identifier number (OID), translates the object into a C++ (or Java or Smalltalk) object, then
loads the object into main memory. If the program updates an object (and commits to the
update), the procedure is reversed: the ODBMS translates the C++ (or Java or Smalltalk) object
into an ODMG object, then writes the translated object back to the database.
128
Data Streams
Output Streams
There are many classes of output streams derived from the abstract OutputStream base class. A
file output stream is associated with an output file. A filter output stream is associated with the
output stream that it filters:
System.out and System.err are instances of the PrintStream class. They should only be used for
debugging.
129
Input Streams
Object Streams
Reading and writing objects presents three problems:
1. An object may contain references to other objects. When
saving the object, these other objects must also be saved.
Object digraph, where nodes represent objects and arrows represent references:
2. An object digraph may contain cycles or multiple paths to a
single object. We want to avoid duplicating objects in the
object stream and we want to avoid non terminating recursions.
Don't want to have two copies of c or x in the object stream.
3. The reading program must know how much memory to allocate to
hold the object digraph.
Using its run time type identification features, Java automatically solves all three problems.
130
interface Serializable {}
Most pre-defined classes implement this interface.
Example
Address Book
class AddressBook implements Serializable
{
private Person[] people;
private int size, maxSize;
AddressBook()
{
maxSize = 50;
size = 0;
people = new Person[maxSize];
}
public void addPerson(Person p)
{
if (size < maxSize)
people[size++] = p;
else
Tools.error("Address book is full");
}
131
public Person findPerson(String name)
{
boolean found = false;
Person result = null;
int i = 0;
while (!found && i < size)
found = name.equals(people[i++].getName());
if (found)
result = people[i - 1];
return result;
}
public void println()
{
for(int i = 0; i < size; i++)
{
people[i].println();
System.out.println("++++++++++++++++++");
}
}
} // AddressBook
Person
class Person implements Serializable
{
private String name;
private Address address;
private String phone;
public Person(String nm, Address addr, String ph)
{
name = nm;
address = addr;
phone = ph;
}
public String getName() { return name; }
public Address getAddress() { return address; }
public String getPhone() { return phone; }
public void println()
{
System.out.println("Name = " + name);
address.println();
System.out.println("
" + phone);
}
} // Person
Address
class Address implements Serializable
{
private int number;
private String street;
private String city;
private String state;
public Address(int n, String st, String c, String s)
132
{
number = n;
street = st;
city = c;
state = s;
}
public void println()
{
System.out.println("
System.out.println("
}
} // Address
" + number + " " + street);
" + city + ", " + state);
An Object Writer
class ObjectWriter
{
public static void test(String outFile)
{
Address addr1 =
new Address(123, "Sesame St.", "New York", "N.Y.");
Address addr2 =
new Address(1600, "Pennsylvania Ave.",
"Washington", "D.C.");
String ph1 = "(212) 653-9875";
String ph2 = "(105) 953-5555";
Person george = new Person("George Jetson", addr1, ph1);
Person spock = new Person("Spock", addr2, ph2);
Person judy = new Person("Judy Jetson", addr1, ph1);
// make an address book
AddressBook book = new AddressBook();
book.addPerson(george);
book.addPerson(spock);
book.addPerson(judy);
book.println();
// do judy and george have the same address?
System.out.print("george.getAddress() == judy.getAddress() = ");
System.out.println(george.getAddress() == judy.getAddress());
try
{
ObjectOutputStream bookStream =
new ObjectOutputStream(
new FileOutputStream(outFile));
bookStream.writeObject(book);
}
catch(IOException e)
{
Tools.error("Error: " + e);
}
}
} // ObjectWriter
Here's the object digraph created. Note that Judy and George both contain references to the
same address. This will be proved by comparing their addresses using ==.
133
Object Reader
class ObjectReader
{
public static void test(String inFile)
{
AddressBook book; // just a reference
try
{
ObjectInputStream bookStream =
new ObjectInputStream(
new FileInputStream(inFile));
book = (AddressBook) bookStream.readObject();
book.println();
Person x = book.findPerson("George Jetson");
Person y = book.findPerson("Judy Jetson");
if (x != null && y != null)
{
// do judy and george have the same address?
System.out.print("george.getAddress() ==
judy.getAddress() = ");
System.out.println(x.getAddress() == y.getAddress());
}
else
Tools.cout.println("Find failed!");
} // try
catch(IOException e)
{
Tools.error("Error: " + e);
}
catch(ClassNotFoundException e)
{
Tools.error("Error: " + e);
}
} // test
} // ObjectReader
Sample output
134
public class Demo
{
public static void main(String[] args)
{
if (args.length != 1)
Tools.error("usage: java Demo File");
System.out.println("calling ObjectWriter.test()");
ObjectWriter.test(args[0]);
System.out.println("\n\ncalling ObjectReader.test()");
ObjectReader.test(args[0]);
System.out.println("done");
}
}
Here's the output produced:
C:\Pearce\Java\Projects\streams>java Demo book1
calling ObjectWriter.test()
Name = George Jetson
123 Sesame St.
New York, N.Y.
(212) 653-9875
++++++++++++++++++
Name = Spock
1600 Pennsylvania Ave.
Washington, D.C.
(105) 953-5555
++++++++++++++++++
Name = Judy Jetson
123 Sesame St.
New York, N.Y.
(212) 653-9875
++++++++++++++++++
george.getAddress() == judy.getAddress() = true
calling ObjectReader.test()
Name = George Jetson
123 Sesame St.
New York, N.Y.
(212) 653-9875
++++++++++++++++++
Name = Spock
1600 Pennsylvania Ave.
Washington, D.C.
(105) 953-5555
++++++++++++++++++
Name = Judy Jetson
123 Sesame St.
New York, N.Y.
(212) 653-9875
++++++++++++++++++
george.getAddress() == judy.getAddress() = true
done
Are Java Programs Clairvoyant?
What if the object reader ran in a separate program from the object writer? Everything would
work as above provided the class loader could find AddressBook.class, Person.class and
135
Address.class. This would happen if these files were in the current directory or in a directory
listed in the CLASSPATH. Otherwise, a "class not found" exception would be thrown.
Databases
Java Database Connectivity (JDBC) is a package consisting of eight interfaces:
java.sql.Driver
java.sql.Connection
java.sql.Statement
java.sql.PreparedStatement
java.sql.CallableStatement
java.sql.ResultSet
java.sql.ResultSetMetaData
java.sql.DatabaseMetaData
java.sql.DriverManager. - maintains a list of all available drivers
driver knows how to create a connection to a particular database. JDBC drivers for most well
known databases are available for a price. If a database has an ODBC driver, then one can use a
JDBC-ODBC bridge, which is provided free on Windows NT, but must be purchased for Sun
platforms. Consult the javasoft web site for a list of drivers and bridges.
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); // load driver manager
String url = "jdbc:odbc:ODBC_Addresses";
String user = "smith";
String pswd = "foobar";
Connection myConn = DriverManager.getConnection(url, user, pswd);
class
connection - stores information about database session and provides statement (or prepared or
callable statement) object to the application:
136
Statement myStmt = myConn.createStatement();
statement - template for an SQL statement (UPDATE, DELETE, INSERT, or SELECT);
provides executeQuery() and executeUpdate() methods. The executeQuery() method returns a
result set representing a list of rows:
String sql = "SELECT columns FROM table WHERE column = xxx;
ResultSet rs = stmt.executeQuery(sql);
Connection
import java.sql.*;
class Database {
private Connection myConn;
private Statement myStmt;
public Database(String dm, String url, String user, String pswd) {
try {
Class.forName(dm); // loads a DriverManager class
myConn = DriverManager.getConnection(url, user, pswd);
myStmt = myConn.getStatement();
} //try
catch(SQLException sqlexp) {
Tools.error("connection failed: " + sqlexp);
}
catch (ClassNotFoundException cnfe) {
Tools.error("Failed to load driver; " + cnfe);
}
}
// etc.
} // Database
Statements
class Database {
private Connection myConn;
private Statement myStmt;
public Database(String dm, String url, String user, String pswd) {
...
}
public String execute(String sql) {
try {
if (isSelect(sql)) {
ResultSet rs = myStmt.executeQuery(sql);
return formatResultSet(rs);
}
else if (isUpdate(sql) || isInsert(sql)) {
int rs = myStmt.executeUpdate(sql);
return "# of rows affected = " + rs;
}
else
137
Tools.error("unrecognized SQL: " + sql);
} // try
catch (SQLException sqle) {
Tools.error("Dbase access failure; " + sqle);
} // catch
catch(IOException ioe) {
Tools.error("Request read failure; " + ioe);
} // catch
} // execute
// etc.
private boolean isSelect(String stmt) {
return (stmt.substring(0, 6).equalsIgnoreCase("SELECT"));
}
private boolean isUpdate(String stmt) {
return stmt.substring(0, 6).equalsIgnoreCase("UPDATE");
}
private boolean isInsert(String stmt) {
return stmt.substring(0, 6).equals("INSERT");
}
} // Database
Result Set
result set - list of rows extracted from a table. Each row is a list of columns. Each column is a
String.
Associated metadata object - tells us the number of columns and the name and type of each
column.
class Database {
private Connection myConn;
private Statement myStmt;
public Database(String dm, String url, String user, String pswd) {
...
}
public String execute(String sql) { ... }
private String formatResultSet(ResultSet rs)
throws SQLException
{
ResultSetMetaData rsmd = rs.getMetaData();
int cc = rsmd.getColumnCount(); // = # of columns
String result = " table: ";
// iterate through each row
while(rs.next()) {
result = result + " row: ";
// iterate through each column
for(int i = 1; i <= cc; i++) {
String colName = " name: " + rsmd.getColumnName(i);
String colType = " type: " + rsmd.getColumnType(i);
138
String colVal = " value: " + rs.getString(i);
result = result + colName + colType + colVal;
} // for
} // while
return result;
} // formatResultSet
// etc.
} // Database
Example
class DbaseClient {
public static void main(String[] args) {
String sql =
"SELECT FNAME, LNAME, ADDRESS, CITY, STATE, ZIP, PHONE " +
"FROM People " +
"WHERE PHONE = " +
"'" + args[0] + "'\n";
Database db =
new Database("sun.jdbc.odbc.JdbcOdbcDriver",
"jdbc:odbc:ODBC_Addresses",
"smith", // owner
"foobar"); // password
Tools.cout.println(db.execute(sql));
}
JDBC can also be used to create databases.
139
Programming and Design Principles
Producing a large software system is the most challenging technical activity that the human
race attempts to carry out.
Darrel Ince
Software is changed far more frequently than computers, bridges, and skyscrapers.
Challenges in modern programming
Coupling and variabilities
 Coupling - if there is a high probability that changing one piece will require changing
another.
 Software is highly susceptible to coupling.
 Studies indicate that coupling correlates with the cost of maintenance.
 Reduce coupling by distributing knowledge on a "need-to-know" basis.
That is, module A doesn't know about module B unless B has an immediate role in A's
job. This is the main reason to avoid sprawling global namespaces. Furthermore, what
A knows about B is limited to the role that B has to A. This is the motivation behind
encapsulation and multiple interfaces..
 Places of likely change are called variabilities.
Designer should ensure that encapsulation and variabilities are preserved through changes.
Encapsulation says "these modules should not interact", while a variability says "this
module should be replaceable.".
Patterns are designed to express policies, and so form a valuable addition to the designer's
vocabulary.
What is a good design?
 Loosely-coupled components with clearly demarcated variabilities.
 Meet the functional requirements within the allotted resources.
 Understandable - basing the design on a common pattern.
 Amenable to change.
Documenting policy
Use patterns as documentation, to ensure that components stay decoupled and variabilities
remain flexible.
140
Object Oriented Analysis and Design
Analysis - document the behavior, requirements constraints
Develop use-case scenarios to ensure your understanding of the desired system
and features
Design - KISS (Keep It Simple Stupid); your designs should be as simple as possible
Develop some artifacts to help understand the nature of your system:
Class/package diagrams with associations/roles to help understand the
relationships of the classes/packages
The Open/Closed Principle (Bertrand Meyer)
"Modules should be open for extension but closed for modification"
This means:
Use abstraction (interfaces) to minimize dependence on implementation details!
Consider the following to help understand this principle:
Class Shape {
public static final Circle = 0;
public static final Square = 1;
private int myShape;
void drawShapes(ShapeArray shapes[]) {
for (int i = 0; i < shapes.size(); i++) {
switch (shapes[i].getShape()) {
case Circle: drawCircle(shapes[i]); break;
case Square: drawSquare(shapes[i]); break;
…
default: "*****"
}
}
…
}
The above code is open - if we want to add another shape we must change the switch (and
perhaps many other switches). It's quite easy to miss one of the switches. Also note that this
code will be modified - it's open;
Contrast this with the code given below.
interface Shape {
public draw();
}
class Square implements Shape {
public draw() { ... }
141
}
class Circle implements Shape {
public draw() { ... }
}
class OtherClass {
void drawShapes(ShapeArray shapes[]) {
for (int i = 0; i < shapes.size(); i++) {
shapes[i].draw();
}
}
}
OtherClass is closed; it will not change if we add more shapes - there are no switch statements!
Object Oriented Analysis: document the behavior, requirements and constraints of the system
to be developed - the important artifact to use is Use Case Scenarios which describe how
external actors (users) interact with the system. These scenarios will raise many issues that
need answering prior to attempting any design efforts.
Object Oriented Design: Aspects of the design process are described below.
KISS - Keep It Simple Stupid
1. Determine Design Patterns (described later in this reader)
2. Packages:
The relationships between packages can be depicted as a diagram where each package is
drawn as a block and directed edges describe dependencies between packages. This
diagram should be a Directed Acyclic Graph. It should not be too confusing to understand
when considering edge crossovers. A simple diagram with few edges implies there is not
too much strong coupling between packages. If the packages are separated into layers
(preferable for large systems) then each layer should demonstrate cohesion and there should
be loose coupling between layers (perhaps higher layers should depend on lower layers but
not vice versa)
3. Inheritance
Be careful when making decisions of when to inherit vs compose (containment). Strive to
use the is-a relationship for inheritance. If classes relate via the has-a relationship then be
sure to use composition. If the hierarchy becomes complex then attempt to refactor classes
by using composition (refer to the Decorator design pattern).
4. Interfaces
142
These are used to specify contracts for services between the client desiring to use services
and the server who will provide the services. The use of interfaces helps ensure the
provision of closed modules (see Meyer's quote above).
5. Classes
Minimize coupling and maximize cohesion! Be careful when constructing instances sometimes you might want to distinguish between construction and initialization (e.g.
Applets separate these two issues)
6. Methods
Keep method size small to increase understanding and minimize coupling. Some thought
should go into assuring correctness. These thoughts can be implemented by providing preand post-conditions along with assertions for constraint checking. Along with these pieces
of code you might throw exceptions if appropriate. Remember to design by contract
7. Variables
Almost all variables should be private (use getters/setters, if necessary). Garbage collection
issues should be dealt with (e.g. if you no longer need to refer to an object then be sure to
reset the variable to null). Be careful with regards to finalizers. Finally, when serializing
objects consider whether the variable should be transient - you might serialize private
information or information only relevant to the current state of an object.
8. Robustness
The importance of this final category for consideration cannot be minimized. Again, be sure
to provide constraint checking within the code via pre- and post-condition checking.
Exceptions should be used with many of these checks. Therefore, carefully consider the
provision of appropriate exception hierarchies. Note that the use of exceptions will force
consumers of your code to write robust code, too.
143
Event Notification (Appendix 3)
Overview
Broadcasters and receivers in many forms are common in event-driven programs. A
broadcaster is an object that randomly broadcasts messages to unknown numbers and types of
receivers. Receivers are objects that are either always on-perpetually listening or polling for
broadcasts-or usually off-expecting to be awakened when a broadcast occurs.
Because broadcast messages are un-addressed and occur randomly, they are often called events.
In this case receivers are called event handlers, broadcasters are called event sources, and
broadcasting is called event firing.
To avoid polling, which consumes too much time and bandwidth, many platforms provide
several types of event notification mechanisms.
These mechanisms can be viewed as instances of various event notification design
patterns e.g., Publisher-Subscriber pattern
Design Patterns
A design pattern is a recurring design problem together with a generic, reusable solution.
A pattern catalog collects, classifies, and names design patterns mined from well designed
structures.
Traditional Neighborhood Development [KUN]
Other Names
TND, New Urbanism, Neo-traditional Planning, Low-density Urbanism, Transit Oriented Development
(TOD), civic art.
Problem
Post World War II zoning laws and property taxation policies, the rise of nation-wide chain stores, and
increased reliance on automobiles has led to suburban sprawl, urban decay, and the death of traditional
neighborhoods and downtown districts. These problems contribute to social and environmental problems
such as crime, racism, poverty, pollution, and alienation.
Solution
1. Neighborhoods should be the basic unit of city planning. Networks of neighborhoods connected and
bounded by corridors (rivers, parks, boulevards) form towns and cities.
2. A neighborhood has a well defined boundary, which is no more than a five minute walk from a focused
center. Neighborhoods are mixed-use and mixed-income.
3. The primary function of the street is to define a public space. Buildings must be disciplined to honor
and embellish this public space. Cars are not given precedence over people.
etc.
144
Example: Monitoring Devices
Assume we are in the early design phase of a project to provide software that will control and
monitor a nuclear reactor. Here's a description of the application domain:
The most important attribute of a nuclear reactor is the
temperature of its core. Reactors provide computerized controls
for decrementing and incrementing the temperature. These
controls are manipulated from a remote console, which is manned
by our most competent employee, Homer Simpson. Sensors are
scattered throughout the power plant. These include
thermometers, and various types of alarms and thermostats that
go off when the reactor's temperature rises above the critical
level: 1500 degrees. We need to have the ability to add more and
new types of sensors and consoles to the system without
modifying the reactor control code.
Here's our initial model of the domain:
class Reactor {
private double temperature = 0;
private final double critical = 1500;
public boolean tooHot() {
return critical <= temperature;
}
public double getTemperature() {
return temperature;
}
public void inc(double amt) {
temperature += amt;
}
public void dec(double amt) {
temperature -= amt;
}
}
// console and sensors:
class Console { Reactor myReactor; ... }
class Alarm { Reactor myReactor; ... }
class Thermometer { Reactor myReactor; .. }
class Thermostat { Reactor myReactor; ... }
// etc.
Use polling:
145
double lastTemp = myReactor.getTemperature();
while(true)
{
double temp = myReactor.getTemperature();
if (lastTemp != temp) { /* do something */ }
lastTemp = temp;
}
Problem:

Unacceptable level of network traffic (changes in the temperature of the reactor's core
will probably be relatively infrequent compared to the polling frequency)..
Solution:

Add a sequence of statements to Reactor.inc() and Reactor.dec() that call specific
methods of each sensor:
void inc(double amt)
{
temperature += amt;
// there are different types of thermometers:
thermometer1.setTemp(temperature);
thermometer2.adjust(temperature);
thermometer3.increment(amt);
// etc.
if (critical <= temperature) // yikes!
{
// there are different types of alarms:
alarm1.ring();
alarm2.flash();
alarm3.buzz();
// etc.
}
}
- Each time a new sensor is added to the system, we will have to add statements to inc() and
dec(). This is an example of tight coupling. It creates dependencies between the reactor and the
sensors, and it is exactly what the last sentence in the domain description asks us not to do.
Why? The program that actually controls the reactor is probably complicated, very specialized,
and, more importantly, it must never fail! Each time we allow people to modify this program
when a new type of sensor is added to the system, we open the door for introducing bugs (and
melt downs).
The Publisher-Subscriber Pattern
It's time to reach for a pattern catalog. The Publisher-Subscriber pattern is listed in several:
Publisher-Subscriber [Go4], [POSA], [LAR], [ROG]
Other Names
Publishers are also called senders, observables, subjects, broadcasters, and notifiers. Subscribers are
also called receivers, listeners, observers and callbacks.
146
Problem
Various monitors need to be notified when a device changes state, but the number and types of monitors
can vary dynamically. We want to avoid polling, and we don't want to make assumptions in the device
code about the numbers and types of monitors.
Solution
The device should maintain a list of references to registered monitors. Monitors can be different, but each
must implement the same interface, which includes an update() function that the device will call when it
changes state. The list of monitor references can be managed by a reusable Publisher base class. An
abstract Subscriber base class defines the interface monitors must implement.
Static Structure
Note: update() is a pure virtual function that will be implemented differently in different
derived classes. Alternatively, we could have stereotyped Subscriber as an interface.
Dynamic Structure
- Sequence diagram
147
The Publisher-Subscriber Pattern in Java
Publisher and subscriber classes are provided in the Java utility package - Observable and
Observer.
Observers are subscribers:
interface Observer { // from java.util
public void update(Observable o, Object arg);
}
Observables are publishers:
public class Observable { // from java.util
private boolean changed = false; // = state changed?
public void addObserver(Observer o) { ... }
public void deleteObserver(Observer o) { ... }
public void notifyObservers() { ... } // pull variant
public void notifyObservers(Object arg) { ... } // push variant
protected void setChanged() { changed = true; }
protected void clearChanged() { changed = false; }
public boolean hasChanged() { return changed; }
// etc.
}
Example: Monitoring Devices (continued)
148
"inc" button - increments the temperature of the reactor by 500 degrees
"dec" button - decrements the reactor's temperature by 50 degrees.
thermometer displays the reactor's temperature - when the reactor's temperature exceeds 1500
degrees, several beeps can be heard and the message:
Warning: reactor too hot!
appears several times in the DOS/Unix console window.
Reactor
Revised Reactor class
class Reactor extends Observable {
private double temperature = 0;
private final double critical = 1500;
public boolean tooHot() {
return critical <= temperature;
}
public double getTemperature() {
return temperature;
}
public void inc(double amt) {
temperature += amt;
setChanged();
notifyObservers();
clearChanged();
}
public void dec(double amt) {
temperature -= amt;
setChanged();
notifyObservers();
clearChanged();
}
}
Alarms
class BeepingAlarm implements Observer {
public void update(Observable o, Object arg) {
if (o instanceof Reactor) {
Reactor r = (Reactor) o;
if (r.tooHot()) {
System.out.println('\u0007'); // beep
}
}
}
}
class PrintingAlarm implements Observer {
public void update(Observable o, Object arg) {
if (o instanceof Reactor) {
Reactor r = (Reactor) o;
if (r.tooHot()) {
System.out.println("Warning: reactor too hot!");
}
149
}
}
}
Thermometers
class Thermometer extends JLabel implements Observer {
private Reactor myReactor;
public Thermometer(Reactor r) {
super("" + r.getTemperature());
myReactor = r;
myReactor.addObserver(this);
}
public void update(Observable o, Object arg) {
setText("" + myReactor.getTemperature());
}
}
Reactor Console
class ReactorConsole extends MainJFrame {
private Reactor myReactor;
// button listeners:
class IncAction implements ActionListener { ... }
class DecAction implements ActionListener { ... }
// constructor:
public ReactorConsole() { ... }
// fire it up:
public static void main(String[] args) {
ReactorConsole console = new ReactorConsole();
console.setVisible(true);
}
}
public ReactorConsole() {
setTitle("Reactor Console");
myReactor = new Reactor();
BeepingAlarm ba1 = new BeepingAlarm();
BeepingAlarm ba2 = new BeepingAlarm();
PrintingAlarm pa1 = new PrintingAlarm();
PrintingAlarm pa2 = new PrintingAlarm();
myReactor.addObserver(ba1);
myReactor.addObserver(ba2);
myReactor.addObserver(pa1);
myReactor.addObserver(pa2);
// make & add inc button panel:
JPanel incPanel = new JPanel();
JButton incButton = new JButton("inc");
incButton.addActionListener(new IncAction());
incPanel.add(incButton);
// make & add dec button panel:
JPanel decPanel = new JPanel();
JButton decButton = new JButton("dec");
decButton.addActionListener(new DecAction());
decPanel.add(decButton);
// make & add thermometer panel:
150
JPanel thermPanel = new JPanel();
Thermometer thermometer = new Thermometer(myReactor);
thermPanel.add(thermometer);
// add panels:
Container contentPane = getContentPane();
contentPane.add(incPanel, "West");
contentPane.add(thermPanel, "Center");
contentPane.add(decPanel, "East");
}
Java Foundation Classes (JFC)
Most GUI components fire events when manipulated by users. Here are a few JFC event
classes:
151
For example, the reactor console indirectly extends JFrame and contains two JButtons and a
JLabel:
Event Delegation
When the user presses either the "inc" or "dec" button, an action event is fired and the
corresponding reactor method is invoked. , the event is routed to registered listeners which
realize one or more listener interfaces:
152
incButton.addActionListener(new IncAction());
decButton.addActionListener(new DecAction());
IncAction and DecAction are declared as inner classes of the ReactorConsole class
Commonly done because these classes are seldom reusable outside of a particular GUI, but also
because inner class instances have access to the private variables of the outer class instance that
creates them. Thus, our listeners can access the myReactor reference of the reactor console that
creates them:
class IncAction implements ActionListener {
public void actionPerformed(ActionEvent a) {
myReactor.inc(500);
}
}
class DecAction implements ActionListener {
public void actionPerformed(ActionEvent a) {
myReactor.dec(50);
}
}
Components are publishers. They publish events.
Listeners are subscribers. They must implement methods prescribed by appropriate listener
interfaces and they must register with the components they listen to.
Constraint Networks
A constraint network represents a mathematical relationship between several variables, and is
able to compute the value of any one of these variables given the values of all the others.
There are two types of nodes in a constraint network: cells and constraints. Cells represent
variables (read-only cells represent constants) and constraints represent primitive mathematical
relationships such as z = x + y and z = x * y. The neighbors of a constraint are the cells that it
constrains. The neighbors of a cell are the constraints that constrain it.
153
A number can be saved to or erased from a cell. In either case, the cell's neighboring constraints
will be notified. Upon notification that a neighboring cell has been erased, a constraint erases
all of its neighbors. Upon notification that a number has been stored in a neighboring cell, a
constraint attempts to compute new values for its remaining neighbors. In either case, cells
updated by a constraint notify the other constraints they are connected to. In this way cell
updates cascade through the network.
For example, assume x, y, and z are cells connected to an addition constraint. If a number is
stored in x, and if y already holds a number, then the adder constraint stores x + y in z.
z = x + y;
Consider the relationship:
z = 3x + 4y
We can represent this as a constraint network consisting of three constraints and seven cells:
In this network the cells labeled 3 and 4 are constant or read-only cells. The cells labeled 3x
and 4y hold 3 * x and 4 * y. The nodes labeled + and * are constraints.
Assume x = 2 and y is undefined. In this case 3x = 6 and 4y is undefined. Assume 26 is stored
in z. The adder constraint stores 26 - 6 = 20 in 4y. At this point the right multiplier constraint
stores 20/4 = 5 in y.
ConNet
Our constraint network is called ConNet::
-> help
help (displays this message)
quit (terminates session)
set NAME VALUE (update a variable cell)
addVar NAME (add a variable cell)
154
addConst NAME VALUE (add a constant cell)
display NAME (display a cell)
undisplay NAME (undisplay a cell)
addAdder ARG1 ARG2 RESULT (add an adder constraint)
addMult ARG1 ARG2 RESULT (add a multiplier constraint)
clear (get rid of all cells & constraints)
forgetAll (forget all cell values)
forget NAME (forget a cell value)
Let's program ConNet to solve the equation:
z = 3x + 4y
We begin by creating three cells representing the variables x, y, and z:
-> addVar x
done
-> addVar y
done
-> addVar z
done
Next, we create two cells that hold the constants 3 and 4, as well as two cells to hold the
intermediate values of 3x and 4y:
-> addConst 3 3
done
-> addConst 4 4
done
-> addVar 3x
done
-> addVar 4y
done
-> addMult y 4 4y
done
-> addMult x 3 3x
done
-> addAdder 3x 4y z
done
-> display x
done
-> display y
done
-> display z
done
-- displays x when it is updated
Ex:
-> set x 3.14
x = 3.14
done
-> set z 9
z = 9
y = -0.105
done
Next, let's compute z assuming x = 5 and y = 3. In this case:
155
z = 3x + 4y = 3 * 5 + 4 * 3 = 27
-> forgetAll
forgetting x
forgetting y
forgetting z
done
-> set x 5
x = 5
done
-> set y 3
y = 3
z = 27
done
Design
Each constraint maintains a collection of links to its neighboring cells and controls the values
those cells may hold. However, a cell has no direct knowledge of its neighboring constraints.
Instead, the Publisher-Subscriber pattern is used. Cells are publishers and neighboring
constraints are subscribers. The user interface is provided through a command line console
linked to the constraint network:
For example, a constraint network representing the equation:
z = x + y
would look like this in memory:
156
The following sequence diagram shows the constraint network setting the value of x then the
value of y:
After the value of x is set, the adder is updated, but the adder does nothing because neither y
nor z holds a value. After y is set, the adder is again updated. Now two of its three neighbors
holds values, so the adder sets the value of z to x + y.
Programming Note
Programming Note 3.1
package.util;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class MainJFrame extends JFrame {
protected int screenHeight;
157
protected int screenWidth;
protected String title = "Main Window";
private class Terminator extends WindowAdapter {
public void WindowClosing(WindowEvent e) {
System.exit(0);
}
}
public MainJFrame() {
addWindowListener(new Terminator());
setTitle(title);
Toolkit tk = Toolkit.getDefaultToolkit();
Dimension d = tk.getScreenSize();
screenHeight = d.height;
screenWidth = d.width;
setSize(screenWidth/2, screenHeight/2);
setLocation(screenWidth/4, screenHeight/4);
}
}
Event Managers
The Publisher-Subscriber pattern is an instance of a general class of architectures called Event
Notification Systems
Event managers are used when multiple devices need to be monitored.
Event Manager [LAR]
Other Names
Event managers are closely related to view managers, application coordinators, and message dispatchers.
Problem
Various listeners need to be notified when a certain type of event occurs. These events are "fired" by
various event sources. We want to avoid polling or tight coupling between listeners and sources.
Solution
The event manager maintains a table of associations between event types and lists of registered listeners
interested in occurrences of the corresponding event type. Usually there is just one event manager. When
an event of type t occurs, the event manager passes the event to the handleEvent() function of each
listener registered for type t events. Each event source maintains a reference to the event manager, and
calls its notify() function when it fires an event.
Static Structure
158
159
Some Important Design Patterns
Model-View-Controller
Model - main object/data structure
View - visual presentation of the model
Controller - (visual) components (e.g. buttons, text fields) that control interactions with
user -event occurs and controller dispatches event to view and/or model.
Swing components, Excel spreadsheets
Adaptor Pattern
Convert the interface of a class into another interface clients expect. Adapter lets classes work
together that couldn't otherwise because of incompatible interfaces.
Distributed object databases like CORBA use Adaptors to integrate native language
objects into the database. Adaptors are also used to make non-objects, like text files,
look like objects
Chain of Responsibility Pattern
Avoid coupling the sender of a request to its receiver by giving more than one object a chance
to handle the request. Chain the receiving objects and pass the request along the chain until an
object handles it. (MFC)
The X Window System, MacApp, Windows, and ET++ use the Chain of Responsibility
pattern to handle input, redraw, and modification events. It can also be used for contextsensitive help events. A window which isn't interested in the event passes it up to its
enclosing window.
Decorator Pattern
Attach (wrap) additional responsibilities to an object dynamically. Decorators provide a
flexible alternative to subclassing for extended functionality.
X Window and ET++ use Decorators to add a title bar, border, and scroll bars to a
window (where the name "decorator" comes from). Java’s streams use decorators
extensively.
Observer Pattern
Define a one-to-many dependency between objects so that when one object changes state, all
its dependents are notified and updated automatically.
160
Proxy Pattern
Provide a surrogate or placeholder for another object which cannot be accessed by normal
means.
Proxies are commonly used to make objects appear local in distributed systems. For
example, the client stub involved in CORBA/RMI is a Proxy for the server function.
Strategy Pattern
Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy
lets the algorithms vary independently from clients that use it. A Decorator lets you change the
skin of an object; a Strategy lets you change the guts.
Java uses the Strategy pattern to encapsulate layout algorithms for text viewers.
Streams Pattern (Pipes and Filters, Dataflow)
Process a stream of information by feeding it through a network of independent and reusable
processing units.
The Unix shell uses eager parallel processes to evaluate a pipeline command. The interface
between processes is exceedingly simple: characters go from stdin to stdout. Processes
are so independent that they can be written in any programming language. Unix manages
the buffers and suspends processes automatically, making the buffering transparent. So it is
equally fair to call this a push or a pull design.
References
Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. Design Patterns: Elements
of Reusable Object-Oriented Software. Addison-Wesley, Reading, MA, 1994.
"Software Patterns" in Communications of the ACM, October, 1996.
161
Refactoring to Patterns
(from Refactoring to Patterns by Joshua Kertevsky, Addison-Wesley 2005)
1. Compose Method Refactoring
Transform the logic into a small number of intention-revealing steps at the same level of detail.
Example
/**
* @author Dr. Barry Levine
*
* Compose Method Refactoring Example
*/
public class MyList {
private final static int initSize = 10;
private Object elements[] = new Object[initSize];
private int currentSize = 0, maxSize = initSize, incrAmount = 10;
private boolean readOnly = false;
public static void main(String[] args) {
new MyList().addElement("First");
}
public MyList() {}
public Object deleteElement() { return "bogus";}
public MyList addElement(Object newElement) {
if (!readOnly) {
if (currentSize == maxSize) {
Object tempElmts [] = new Object[maxSize + incrAmount];
for (int i = 0; i< maxSize; i++) {
tempElmts[i] = elements[i];
}
elements = tempElmts;
maxSize += incrAmount;
}
elements[currentSize++] = newElement;
}
return this;
}
}
162
1. Restructure if
public MyList addElement(Object newElement) {
if (readOnly) {
return this;
}
if (currentSize == maxSize) {
Object tempElmts[] = new Object[maxSize + incrAmount];
for (int i = 0; i < maxSize; i++) {
tempElmts[i] = elements[i];
}
elements = tempElmts;
maxSize += incrAmount;
}
elements[currentSize++] = newElement;
return this;
}
2. Extract Methods – highlight code to extract to method and click on extract method menu
item.
public MyList addElement(Object newElement) {
if (readOnly) {
return this;
}
if (currentSize == maxSize) {
grow();
}
addAnElement(newElement);
return this;
}
/**
* @param newElement
*/
private void addAnElement(Object newElement) {
elements[currentSize++] = newElement;
}
/**
*
*/
private void grow() {
163
Object tempElmts[] = new Object[maxSize + incrAmount];
for (int i = 0; i < maxSize; i++) {
tempElmts[i] = elements[i];
}
elements = tempElmts;
maxSize += incrAmount;
}
Benefits and Liabilities
 Efficiently communicates what a method does and how it does what it does
 Simplifies a method by breaking it up into well-named chunks of behavior at the same
level of detail
 Can lead to an overabundance of small methods
 Can make debugging difficult because logic is spread out across many small
methods
164
2. Replace Conditional Logic with Strategy
Conditional logic in a method controls which of several variants of a calculation are executed.
Create a Strategy for each variant and make the method delegate the calculation to a Strategy
instance.
Example
import java.util.*;
public class Checkout {
public final static String CashPayment = "Cash";
public final static String CreditPayment = "Credit";
public final static String CheckPayment = "Check";
private final static int badCreditPurchase = 4; // 40 percent of credit purchases will fail
private final static int badCheckPurchase = 2; // 20 percent of credit purchases will fail
private String typeOfPurchase;
private double amountOfPurchase;
public Payment(double amountOfPurchase, String typeOfPurchase) {
this.amountOfPurchase = amountOfPurchase;
this.typeOfPurchase = typeOfPurchase;
}
// make payment and return change
public double makePayment(double amountOfPayment) throws Exception {
// Note that the logic for each payment type can get much more
// complicated
if (typeOfPurchase.equals(CashPayment)) {;
return computeChange(amountOfPayment);
} else if (typeOfPurchase.equals(CreditPayment)) {
int randInt = (new Random()).nextInt(10);
if (randInt < badCreditPurchase) {
throw new Exception("bad credit report - payment failed");
}
return 0.0; // no change with credit purchases
}
// check purchase
int randInt = (new Random()).nextInt(10);
if (randInt < badCheckPurchase) {
throw new Exception("bad check - payment failed");
165
}
return 0.0; // no change with check purchases
}
double computeChange(double pay) { return 0.0; }
}
1. Create a concrete class for each strategy. Move the method that implements the strategy to
each subclass, as appropriate.
import java.util.*;
public abstract class PaymentStrategy {
protected double amountOfPurchase;
public PaymentStrategy(double amountOfPurchase) {
this.amountOfPurchase = amountOfPurchase;
};
// make payment and return change
public abstract double makePayment(double amountOfPayment) throws Exception;
double computeChange(double pay) {
return 0.0;
}
}
class CashPaymentStrategy extends PaymentStrategy {
public CashPaymentStrategy(double amountOfPurchase) {
super(amountOfPurchase);
}
public double makePayment(double amountOfPayment) throws Exception {
return computeChange(amountOfPayment);
}
}
166
class CheckPaymentStrategy extends PaymentStrategy {
private final static int badCheckPurchase = 2;
// 20 percent of credit
// purchases will fail
public CheckPaymentStrategy(double amountOfPurchase) {
super(amountOfPurchase);
}
public double makePayment(double amountOfPayment) throws Exception {
int randInt = (new Random()).nextInt(10);
if (randInt < badCheckPurchase) {
throw new Exception("bad check - payment failed");
}
return 0.0; // no change with check purchases
}
}
class CreditPaymentStrategy extends PaymentStrategy {
private final static int badCreditPurchase = 4; // 40 percent of credit
// purchases will fail
public CreditPaymentStrategy (double amountOfPurchase) {
super(amountOfPurchase);
}
public double makePayment(double amountOfPayment) throws Exception {
int randInt = (new Random()).nextInt(10);
if (randInt < badCreditPurchase) {
throw new Exception("bad credit report - payment failed");
}
return 0.0; // no change with credit purchases
}
}
public class Checkout {
private PaymentStrategy paymentStrategy;
public CheckoutCash(double amountOfPurchase) {
paymentStrategy = new CashPaymentStrategy(amountOfPurchase);
}
public CheckoutCheck(double amountOfPurchase) {
167
paymentStrategy = new CheckPaymentStrategy(amountOfPurchase);
}
public CheckoutCredit(double amountOfPurchase) {
paymentStrategy = new CreditPaymentStrategy(amountOfPurchase);
}
// make payment and return change
public double makePayment(double amountOfPayment) throws Exception {
return paymentStrategy.makePayment(amountOfPayment);
}
}
Benefits and Liabilities
 Clarifies algorithms by decreasing or removing conditional logic
 Simplifies a class by moving variations on an algorithm to a hierarchy
 Enables one algorithm to be swapped for another at runtime
 Complicates how algorithms obtain or receive data from their context class
168
3. Replace Conditional Dispatcher with Command
Create a Command for each action. Store the Commands in a collection and replace the
conditional logic with code to fetch and execute Commands.
Example
package before;
import java.util.*;
public class Program {
private Vector program = new Vector();
private java.util.Dictionary labeltable = new java.util.Hashtable();;
public void addCode(String code) {
program.addElement(code);
}
public String getCode(int i) {
return (String)program.elementAt(i);
}
}
package before;
import java.util.*;
/**
* The VirtualMachine is the virtual hardware that will execute
* the bytecodes; its operation is emulated by the vm object created;
* It contains a runtime stack, program counter, function return
* address stack, the bytecode program and an indication whether it's
* running
*/
public class VirtualMachine {
private Stack runStack = new Stack();
private int pc; // program counter
private boolean isRunning; // is the vm running?
private Program program; // the bytecode program
/**
* Construct a new virtual machine
169
* @param program - the bytecode program to execute
*/
public VirtualMachine(Program program) {
this.program = program;
}
/**
* begin execution of the vm; init the pc to 0; create new stacks;
* set the machine running and include the main execution loop
*/
public void executeProgram() {
pc = 0;
isRunning = true;
while (isRunning) {
String code = program.getCode(pc);
if (code.equals("pop")) {
runStack.pop();
} else if (code.equals("Halt")) {
isRunning = false;
} else if (code.equals("add")) {
int op1 = pop().intValue();
int op2 = pop().intValue();
runStack.push(new Integer(op1+op2));
}
// and so on for the other operators
pc++;
}
}
public Integer pop() {return (Integer)runStack.pop();}
}
1. Create Bytecode abstract class to describe behavior of commands. Create concrete classes for
each Bytecode; store Bytecodes in Program. Include an execute method in each concrete
Bytecode class.
/**
* ByteCode class is the abstract class describing the behaviour for
* all bytecodes; used for typing information when processing bytecodes
*/
public abstract class ByteCode {
public abstract void execute(VirtualMachine vm);
}
170
public class PopCode extends ByteCode {
private int value;
public PopCode() {}
/**
* POP <i>n</i> <br>
* Pop the top <i>n</i> items from the runtime stack
*/
public void execute(VirtualMachine vm) {
for (int i = 0; i < value; i++) {
vm.pop();
}
}
}
2. Now, Program stores Bytecodes rather than Strings
import java.util.*;
public class Program {
private Vector program = new Vector();
private java.util.Dictionary labeltable = new java.util.Hashtable();;
public void addCode(ByteCode code) {
program.addElement(code);
}
public ByteCode getCode(int i) {
return (ByteCode)program.elementAt(i);
}
}
3. Sequence through the collection of objects in a generic fashion.
import java.util.*;
/**
* The VirtualMachine is the virtual hardware that will execute
* the bytecodes; its operation is emulated by the vm object created;
* It contains a runtime stack, program counter, function return
* address stack, the bytecode program and an indication whether it's
171
* running
*/
public class VirtualMachine {
private Stack runStack = new Stack();
private int pc; // program counter
private boolean isRunning; // is the vm running?
private Program program; // the bytecode program
/**
* Construct a new virtual machine
* @param program - the bytecode program to execute
*/
public VirtualMachine(Program program) {
this.program = program;
}
/**
* begin execution of the vm; init the pc to 0; create new stacks;
* set the machine running and include the main execution loop
*/
public void executeProgram() {
pc = 0;
isRunning = true;
while (isRunning) {
ByteCode code = program.getCode(pc);
code.execute(this);
pc++;
}
}
public Integer pop() {return (Integer)runStack.pop();}
}
Note that the Bytecode variabilities have been factored into a hierarchy of Bytecodes to
encapsulate the Bytecode abstraction.
Benefits and Liabilities
 Provides a simple mechanism for executing diverse behavior in a uniform way
 Enables runtime changes regarding which requests are handled and how
 Requires trivial code to implement
172
 Complicates a design when a conditional dispatcher is sufficient
Note that in the Strategy and Command refactorings we made improvements based on
recognizing and making explicit the variabilities found in the problems.
4. Replace Type Code with Class
Constrain the assignments and equality comparisons by making the type of the field a class.
Example
Recall the example used in Replace Conditional Logic with Strategy
import java.util.*;
public class Checkout {
public final static String CashPayment = "Cash";
public final static String CreditPayment = "Credit";
public final static String CheckPayment = "Check";
private final static int badCreditPurchase = 4;
private final static int badCheckPurchase = 2;
private String typeOfPurchase;
private double amountOfPurchase;
public Checkout(double amountOfPurchase, String typeOfPurchase) {
this.amountOfPurchase = amountOfPurchase;
this.typeOfPurchase = typeOfPurchase;
}
// make payment and return change
public double makePayment(double amountOfPayment) throws Exception {
// Note that the logic for each payment type can get much more
// complicated
if (typeOfPurchase.equals(CashPayment)) {;
return computeChange(amountOfPayment);
} else if (typeOfPurchase.equals(CreditPayment)) {
int randInt = (new Random()).nextInt(10);
if (randInt < badCreditPurchase) {
throw new Exception("bad credit report - payment failed");
}
return 0.0; // no change with credit purchases
}
173
// check purchase
int randInt = (new Random()).nextInt(10);
if (randInt < badCheckPurchase) {
throw new Exception("bad check - payment failed");
}
return 0.0; // no change with check purchases
}
double computeChange(double pay) { return 0.0; }
}
What if we used
new Checkout(20.03,” Cedit”) rather than
new Checkout(20.03,” Credit”)
This logic error might be difficult to determine.
We might also consider using an enum type which is available in JDK 1.5.
Benefits and Liabilities
 Provides better protection from invalid assignments and comparisons
 Requires more code than using unsafe type does
174
Frameworks. Toolkits, and Polymorphism
(Appendix 4)
Overview
Polymorphism allows programmers to declare partially complete classes and functions. The
idea being to complete the declaration with a subclass or a template instantiation when
more application-specific information is available. Of course this is also the idea behind
frameworks, toolkits, and many design patterns. In each case application-independent logic is
captured by one part of the program, while application-dependent logic is concentrated in
another.
Frameworks
A framework is a library of collaborating base classes that capture the logic, architecture,
and interfaces common to a family of similar applications. Frameworks are customized into
specific applications by deriving classes from framework classes. Consider MFC - provides
generic graphical user interfaces with windows, menus, toolbars, dialog boxes, and other
common user interface components. JFC provides generic graphical user interfaces with
windows, menus, toolbars, dialog boxes, and other common user interface components.
175
Example: Application Frameworks
Example: Client-Server Frameworks
Data is maintained by the server
Presented and manipulated by clients. Often machine or network boundaries separate clients
and servers. The World Wide Web (WWW) is a typical client server application. Web servers
maintain collections of web pages that are delivered upon request to web clients (i.e., web
browsers).
Java – applets server as clients in this framework
176
Object-Oriented Concepts
Polymorphism
A type system for a language L is a set of primitive types together with a set of rules for
constructing, comparing, and naming types, as well as rules for binding types to the expressions
and values of L. For example, the primitive types of Java include int, float, char, and boolean.
Arrays and classes are examples of constructed types.
Instances of a monomorphic or uniform type all have the same representation; e.g., Real type
using IEEE floating point standard representation,
Polymorphic or multi-form type can have a variety of representations; e.g., Java classes
because instances of an extension can be treated as instances of the super class
Ex:
class Shape {
public void draw(Graphics g) { /* no op */ }
// etc.
}
class Circle extends Shape {
public void draw(Graphics g) { g.drawOval(...); }
// etc.
}
177
Reference to a Shape can actually refer to any instance of a Shape extension
Shape[] shapes = new Shape[3];
shapes[0] = new Circle();
shapes[1] = new Rectangle();
shapes[2] = new Triangle();
The following statement draws a circle, rectangle, and triangle in the graphical context g:
Graphics g = myShapeWindow.getGraphics();
for(int i = 0; i < 3; i++) shape[i].draw(g);
Abstraction
Interfaces
An interface is a collection of operator specifications. An operator specification may include a
name, return type, parameter list, exception list, pre-conditions, and post-conditions. A class
implements an interface if it implements the specified operators. A software component is an
object that is known to its clients only through the interfaces it implements. Often, the client of
a component is called a container.
Interfaces and Components in UML
Modelers can represent interfaces in UML class diagrams using class icons stereotyped as
interfaces. The relationship between an interface and a class that realizes or implements it is
indicated by a dashed generalization arrow:
Notice that the container doesn't know the type of components it uses. It only knows that its
components realize or implement the IComponent interface.
For example, imagine that a pilot flies an aircraft by remote control from inside of a
windowless hangar. The pilot holds a controller with three controls labeled: TAKEOFF,
FLY, and LAND, but he has no idea what type of aircraft the controller controls
178
The pilot only knows about the operations that are specifically declared in the Aircraft
interface.
Can create new interfaces from existing interfaces using generalization.
Interfaces and Components in Java
Java:
interface Aircraft {
public void takeoff();
public void fly();
179
public void land();
}
Notice that the interface declaration lacks private and protected members. There are no
attributes, and no implementation information is provided.
class Pilot {
private Aircraft myAircraft;
public void fly() {
myAircraft.takeoff();
myAircraft.fly();
myAircraft.land();
}
public void setAircraft(Aircraft a) {
myAircraft = a;
}
// etc.
}
Java:
class Airplane
public void
public void
public void
public void
// etc.
}
implements Aircraft {
takeoff() { /* Airplane takeoff algorithm */ }
fly() { /* Airplane fly algorithm */ }
land() { /* Airplane land algorithm */ }
bank(int degrees) { /* only airplanes can do this */ }
Ex.
Pilot p = new Pilot("Charlie");
p.setAircraft(new Blimp());
p.fly(); // Charlie flies a blimp!
p.setAircraft(new Helicopter());
p.fly(); // now Charlie flies a helicopter!
p.setAircraft(new Aircraft()); // error!
Java interface extension:
interface Airliner extends Aircraft, Carrier {
public void serveCocktails();
}
class PassengerPlane extends Airplane
public void add(Passenger p) { ...
public void rem(Passenger p) { ...
public void serveCocktails() { ...
// etc.
}
implements Airliner {
}
}
}
Abstract Classes
Classes only inherit obligations from the interfaces they implement-obligations to implement
the specified member functions.
180
Abstract class is a partially defined class. Classes derived from an abstract class inherit both
features and obligations.:
abstract public class Aircraft {
protected double altitude = 0, speed = 0;
public Aircraft(double a, double s) {
altitude = a;
speed = s;
}
public Aircraft() {
altitude = speed = 0;
}
public double getSpeed() { return speed; }
public double getAltitude() { return altitude; }
public void setSpeed(double s) { speed = s; }
public double setAltitude(double a) { altitude = a; }
abstract public void takeoff();
abstract public void fly();
abstract public void land();
}
Names of abstract classes and virtual functions are italicized in UML:
Working with Unknown Classes
A framework is a polymorphic application. A framework is an application that can take on
many forms, depending on how it is customized. As such, framework programmers often
need to refer to classes that won't be defined until the framework is customized, and of course
different customizations may define these classes in different ways. Polymorphism allows
framework programmers to work around these critical pieces of missing information.
181
MFW: A Framework for Music Applications
MFW is a music framework that can be customized into various musical applications such
as score editors, virtual recording studios, virtual instruments, expert systems for composers,
and interfaces to computer controlled instruments such as synthesizers.
One part of MFW defines various ensembles of musical instruments: bands, orchestras, trios,
quartets, etc. Unfortunately, the types of musical instruments will be defined much later in the
various customizations of the framework.
How can MFW form ensembles without knowing the identity of their constituent instruments?
There are two solutions: make Instrument an abstract class in MFW, or make Instrument an
MFW interface.
interface Instrument {
public void play();
}
class Trio {
Instrument first, second, third;
Trio(Instrument a, Instrument b, Instrument c) {
first = a;
second = b;
third = c;
}
public void play() {
if (first != null) first.play();
if (second != null) second.play();
if (third != null) third.play();
}
}
Problem: create and play trios consisting of different combinations of horns, harps, and drums.
class Harp implements Instrument {
public void play() {
System.out.println("plink, plink, plink");
}
}
Trio t1 = new Trio(new Harp(), new Horn(), new Drum());
Trio t2 = new Trio(new Drum(), new Drum(), new Harp());
t1.play();
t2.play();
Heterogeneous Collections
Orchestra can be regarded as a container of different types of instruments:
class Orchestra {
private Instrument[] instruments = new Instrument[100];
private int size = 0;
public void add(Instrument i) throws MFWError {
if (100 <= size) throw new MFWError("orchestra full");
instruments[size++] = i;
182
}
public void play() {
for(int i = 0; i < size; i++)
instruments[i].play();
}
}
Clients of the orchestra class can add a variety of instruments to orchestra instances:
Orchestra orch = new Orchestra();
orch.add(new Horn());
orch.add(new Harp());
orch.add(new Horn());
orch.add(new Harp());
orch.add(new Drum());
orch.play();
Virtual Factory Methods
Each note encapsulates a frequency and duration:
abstract class Note {
protected double freq; // in Hz
protected double duration; // in mSec
public Note(double f, double d) { freq = f; duration = d; }
public abstract void play(); // quality? timbre?
}
class HornNote extends Note {
public HornNote(double f, double d) { super(f, d); }
public void play() {
System.out.println("honk");
}
}
Replace the Instrument interface by a concrete class:
class Instrument {
public void play() {
for(int i = 0; i < 3; i++) {
Note note = makeNote(); // virtual factory method
note.play();
}
}
}
But how can the Instrument class know what type of Notes to create?
Factory Method [Go4]
Other Names
Virtual constructor.
Problem
A "factory" class can't anticipate the type of "product" objects it must create.
183
Solution
Provide the factory class with an ordinary member function that creates product objects. This is called a
factory method. The factory method can be a virtual function implemented in a derived class, or a
template function parameterized by a product constructor.
An ordinary member function that creates and returns new objects is called a factory method.
abstract class Instrument {
public void play() {
for(int i = 0; i < 3; i++) {
Note note = makeNote();
note.play();
}
}
abstract public Note makeNote();
}
The obligation to implement the virtual factory method falls on the shoulders of extension
classes:
class Horn extends Instrument {
public Note makeNote() {
return new HornNote(100, 100);
}
}
Problem: programmers must maintain two parallel inheritance hierarchies, one for
factories, and one for products:
If a programmer creates a new type of note, he must remember to create the corresponding
factory instrument that creates the note.
184
Factories in Java
Inner Classes
abstract class Instrument {
abstract class Note {
protected double freq; // in Hz
protected double duration; // in mSec
public Note(double f, double d) { freq = f; duration = d; }
public abstract void play(); // quality? timbre?
}
// virtual factory method:
abstract public Note makeNote();
public void play() {
for(int i = 0; i < 3; i++) {
Note note = makeNote();
note.play();
}
}
}
class Horn extends Instrument {
class HornNote extends Note {
public HornNote(double f, double d) { super(f, d); }
public void play() {
System.out.println("honk");
}
}
public Note makeNote() {
return new HornNote(100, 100);
}
}
Horn factory = new Horn();
Instrument.Note product = factory.new HornNote(100, 100);
product.play();
This technique for constructing notes fails if HornNote is declared as a private inner class. In
this case clients are forced to use our makeNote() factory method.
Inner class objects can access the public, protected, and private fields and methods of
their factories, but the syntax is weird:
class Horn extends Instrument {
class HornNote extends Note {
public HornNote(double f, double d) { super(f, d); }
public void play() {
System.out.print("my instrument = ");
Horn.this.show();
System.out.println("honk");
}
}
public Note makeNote() {
185
return new HornNote(100, 100);
}
private void show() { System.out.println("Horn"); }
}
Notes:
1. The inner class instance only exists when an instance of the enclosing class is created.
2. If the inner class is static then it cannot reference any instance variables/methods of an
enclosing class since an instance does not exist.
UML doesn't have a notation for inner classes; we can use a stereotyped association:
Local Classes
Classes can even be defined inside of a method:
class Horn extends Instrument {
public Note makeNote() {
class HornNote extends Note {
public HornNote(double f, double d) { super(f, d); }
public void play() {
System.out.println("honk");
}
}
return new HornNote(100, 100);
}
}
Anonymous Classes
class Horn extends Instrument {
public Note makeNote() {
return new Note(100, 100) {
public void play() {
System.out.println("honk");
}
};
}
}
The general syntax is:
new SuperType(constructor parameters) {
inner class methods and data
}
186
Closures, Functors, and Thunks
A functor (also called functional or function object) is an object that behaves like a method. A
thunk (also called a promise) is a special type of functor. It's special because a thunk invocation
can be frozen (delayed). Later, the frozen thunk can be thawed (forced), causing it to compute
and return a value. The interesting thing is that a thunk remembers its freezing environment,
which it uses to produce its value when thawed, even if the thawing environment is different.
(Thunks are closely related to closures. A closure is a method that remembers its defining
environment.)
Every thunk must implement a thaw() method that returns the computed value as an Object:
interface Thunk {
public Object thaw();
}
In this demo we want to convert the method ThunkDemo.foo() into a thunk. The foo() method
simply computes the sum of two parameters and a local variable, but in reality, when called,
foo() creates and returns an anonymous thunk that computes this value when thawed:
class ThunkDemo {
public Thunk foo(final int x, final int y) {
final int z = 10;
return new Thunk() {
public Object thaw() {
return new Integer(x + y + z);
}
};
}
public static void main(String[] args) {
ThunkDemo demo = new ThunkDemo();
int a = 3, b = 4;
Thunk thunk = demo.foo(a, b); // freeze demo.foo(a, b) call
a = 12; b = 15; // a and b change
System.out.println("tic toc tic toc ..."); // much time passes
System.out.println("result = " + (Integer)thunk.thaw()); // = 17
}
}
A memoization thunk caches its computed result so subsequent thaws don't need to recompute anything. In this case Thunk becomes an abstract base class that provides the cache:
abstract class Thunk {
protected Object cache = null;
abstract public Object thaw();
}
public class ThunkDemo {
public Thunk foo(final int x, final int y) {
final int z = 10;
return new Thunk() {
public Object thaw() {
if (cache == null) // only computed once:
187
cache = new Integer(x + y + z);
return cache;
}
};
}
public static void main(String[] args) {
ThunkDemo demo = new ThunkDemo();
int a = 3, b = 4;
Thunk thunk = demo.foo(a, b); // freeze demo.foo(a, b) call
a = 12; b = 15; // a and b change
System.out.println("tic toc tic toc ..."); // much time passes
System.out.println("result = " + (Integer)thunk.thaw()); // = 17
a = 100; b = -40; // a and b change again
System.out.println("tic toc tic toc ..."); // more time passes
System.out.println("result = " + (Integer)thunk.thaw()); // = 17
}
}
Clearly this is an advantage if computing the value requires a lot of time.
Thunks are useful for representing streams. A stream is a potentially infinite sequence:
(2 3 5 7 11 13 17 19 ... )
A stream is represented as a pair:
class Stream {
private Object car;
private Thunk cdr;
public Stream(Object a, Thunk b) {
car = a;
cdr = b;
}
public Object head() { return car; }
public Stream tail() {
return new Stream(cdr.thaw(), cdr);
}
}
In the following demo, the tail of a stream representing the infinite sequence of integers always
produces the next integer by incrementing the cache:
public class StreamDemo {
public Thunk next() {
final int x = 0;
return new Thunk() {
public Object thaw() {
if (cache == null)
cache = new Integer(0);
else
cache = new Integer(((Integer)cache).intValue() + 1);
return cache;
}
};
}
Here is a test harness that prints out the first 20 values of a potentially infinite stream, s:
188
public static void main(String[] args) {
StreamDemo demo = new StreamDemo();
Stream s = new Stream(new Integer(0), demo.next());
System.out.print('(');
for(int i = 0; i < 20; i++) {
System.out.print("" + (Integer)s.head() + ' ');
s = s.tail();
}
System.out.println(')');
}
}
Here's the program output:
(0 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18)
Note: I'm not sure this example is very interesting, because one could more easily accomplish
the same thing in Java without using thunks. However, we note that the use of inner classes
provides the capability of easily constructing infinite streams.
Consider using thunks/memoization for providing more interesting infinite streams, such
as a stream of primes. Also, consider combining streams, such as the stream of every
other prime.
Power Types as Factories
Meta-classes and power-classes allow modelers to separate class from type. Instead of
representing types implicitly as classes with no runtime existence, we can represent types
as objects that instantiate meta or power classes.
For example:
Alternatively, we could define a single Aircraft class and represent the different types of
aircraft by different instances of an AircraftType power class with a factory method:
Insure that users won't bypass the factory and directly create aircraft?
interface IAircraft {
public AircraftType getType();
public void takeoff();
189
public void fly();
public void land();
}
class AircraftType {
private String name;
public AircraftType(String n) { name = n; }
public String toString() { return name; }
// factory method:
public IAircraft makeAircraft() {
return new Aircraft();
}
private class Aircraft implements IAircraft {
public void takeoff() {
System.out.println("An aircraft is taking off");
}
public void fly() {
System.out.println("An aircraft is flying");
}
public void land() {
System.out.println("An aircraft is landing");
}
public AircraftType getType() {
return AircraftType.this;
}
} // Aircraft
} // AircraftType
public class TheApp {
public static void main(String[] args) {
AircraftType blimp = new AircraftType("Blimp");
AircraftType helicopter = new AircraftType("Helicopter");
//IAircraft a = blimp.new Aircraft();
// ok if inner class is public
IAircraft a = blimp.makeAircraft();
a.takeoff();
a.fly();
a.land();
System.out.println("a.getType() = "+ a.getType()); //prints Blimp
}
}
Reflection in Java:
interface IAircraft {
// public AircraftType getType();
public void takeoff();
public void fly();
public void land();
}
class SpaceShip implements IAircraft {
public void takeoff() {
System.out.println("A spaceship is taking off");
}
public void fly() {
System.out.println("A spaceship is flying");
190
}
public void land() {
System.out.println("A spaceship is landing");
}
}
import java.lang.reflect.*;
public class Test {
public static void main(String[] args) {
try {
String className = "SpaceShip";
Class c = Class.forName(className);
IntfAircraft a = (IntfAircraft)c.newInstance();
Method[] meths = c.getMethods();
for(int i = 0; i < meths.length; i++)
if (meths[i].getName().equals("takeoff"))
meths[i].invoke(a, null);
}
catch(Exception e) {
System.err.println("error = " + e);
}
}
}
Toolkits
A toolkit (also called an abstract factory or simply a kit) is a class or object that provides
factory methods that produce the components needed to construct all or part of an
application. In a sense, toolkits are precursors to frameworks. While a framework is already
assembled and only requires customization, a toolkit only provides the components
programmers will need to assemble an application. The advantage of a toolkit is that the entire
assembly process can be described relative to the toolkit. Thus, the toolkit decouples the
assembly process from the details of how the components are created (see Java’s AWT).
Toolkits are formalized by the abstract factory design pattern:
Abstract Factory [Go4]
Other Names
Kit, toolkit
Problem
A system should be independent of how its components are implemented.
Solution
Provide the system with an abstract factory parameter. An abstract factory is a class consisting of virtual
or template factory methods for making the system's components. Various derived classes or template
instances provide implementations of the factory methods.
As an example, let's consider building graphical user interfaces (GUIs). Unfortunately, the
standard C++ library doesn't provide GUI components such as windows, menus, and buttons.
191
GUI components are highly platform-dependent - these components are supplied by platformspecific libraries such as the X-Windows library.
We can partially relieve the pain of a port by using a toolkit to decouple the construction
of a GUI from the details of how the GUI components are constructed. We begin by
defining an abstract user interface toolkit:
interface UIToolkit {
Window makeWindow();
Button makeButton();
MenuItem makeMenuItem();
EditBox makeEditBox();
ListBox makeListBox();
// etc.
};
In addition, the toolkit includes a hierarchy of abstract component classes or interfaces:
abstract class UIComponent {
// draw this component in parent window:
public abstract virtual void draw();
// handle keyboard & mouse messages:
public void handle(Message msg) {}
// etc.
protected protected Point corner; // upper left corner
protected int height, width; // size
protected Window parent; // = null for desktop window
};
class MyApp {
static Window makeMyGUI(UIToolkit tk) {
Window w = tk.makeWindow();
Button b = tk.makeButton();
w.adopt(b);
EditBox e = tk.makeEditBox();
w.adopt(e);
// etc.
return w;
}
// etc.
}
192
For example, assume Z Windows is a library of GUI components for a particular platform (e.g.,
Z = X, MFC, MacApp2, etc.):
Use adapters (which will be discussed in Chapter 4). For example:
class ZButtonAdapter extends Button {
private ZButton peer; // the corresponding Z component
public ZButtonAdapter() { peer = new ZButton(); }
public void draw() { peer.paint(); }
public void handle(Message msg) { peer.onMsg(msg); }
}
We must also provide a Z Windows implementation of the abstract toolkit class:
class ZUIToolkit extends UIToolkit {
public Window makeWindow() { return new ZWindowAdapter(); }
public Button makeButton() { return new ZButtonAdapter(); }
public MenuItem makeMenuItem() { return new ZMenuItemAdapter(); }
public EditBox makeEditBox() { return new ZEditBoxAdapter(); }
public ListBox makeListBox() { return new ZListBoxAdapter(); }
// etc.
}
Here is how the programmer might start an application using the ZUIToolkit:
class MyApp {
public static void main(String[] args) {
Window appWindow = makeMyGUI(new ZUIToolkit());
appWindow.draw(); // draw app window & start msg loop
}
// etc.
}
Generic Methods
Generic Algorithm [Go4], [ROG]
Other Names
Template method
Problem
193
Parts of an algorithm are invariant across a family of framework customizations, while other parts are not.
Solution
Move the algorithm into the framework. Replace the non-invariant parts by calls to virtual functions that
can be implemented differently in different customizations.
Ex. Add a symphony class to MFW
abstract class Symphony {
public void play() { // a generic algorithm
doFirstMovement();
doSecondMovement();
doThirdMovement();
doFourthMovement();
}
protected abstract virtual void doFirstMovement() = 0;
protected abstract virtual void doSecondMovement() = 0;
protected abstract virtual void doThirdMovement() = 0;
protected abstract virtual void doFourthMovement() = 0;
}
Framework customizers will have to subclass Symphony and implement the do-functions. For
example:
class TheFifth extends Symphony {
protected void doFirstMovement()
System.out.println("dah, dah,
}
void doSecondMovement() {
System.out.println("duh, duh,
}
void doThirdMovement() {
System.out.println("dah, duh,
}
void doFourthMovement() {
System.out.println("duh, dah,
}
}
{
dah, duh ...");
duh, dah ...");
duh, dah ...");
dah, duh ...");
Singletons
In some situations, multiple instances might be illogical:
TheFifth s1, s2, s3; // how many Fifth Symphonies did he write?
The Singleton pattern solves this problem by making all constructors private. A public factory
method is provided that allows users to create instances, but the factory method always returns
pointers to the same hidden instance:
Singleton [Go4]
Problem
There must be at most one instance of a class.
Solution
194
Make all constructors private, including the default and copy constructors. Provide a static function that
initializes and returns a private, static pointer to the sole instance of the class.
For example, let's apply the singleton pattern to the TheFifth class from the previous example:
class TheFifth extends Symphony {
public static TheFifth makeTheFifth() {
if (theFifth == null)
theFifth = new TheFifth(); // constructor access ok here
return theFifth;
}
// etc.
private static TheFifth theFifth; // the one and only
private TheFifth() {} // hide default constructor
}
Only one instance is ever created:
TheFifth s1 = TheFifth.makeTheFifth();
TheFifth s2 = TheFifth.makeTheFifth();
TheFifth s3 = TheFifth.makeTheFifth();
s1.play();
s2.play();
s3.play();
Example: A Simple Application Framework
Java's platform independence means, among other things, that the Java VM doesn't care how
characters and numbers are represented on the host platform. This makes writing a simple
interactive program with a console user interface (CUI) a bit of a challenge. Since this is a
common task, we now present the first of two CUI-based application frameworks. The entire
framework consists of two classes: Console and AppError.
Note: This demonstrates the Generic Pattern.
Calculator
C:\Pearce\JPOP\console>java Calculator
type "help" for commands
-> help
Console Help Menu:
about: displays application information
help: displays this message
quit: terminate application
ARG1 + ARG2 = sum of ARG1 and ARG2
ARG1 * ARG2 = product of ARG1 and ARG2
ARG1 - ARG2 = difference of ARG1 and ARG2
ARG1 / ARG2 = quotient of ARG1 and ARG2
Note 1: Spaces between tokens are required
Note 2: ARG1 & ARG2 are numbers.
The about command displays information about the framework and the customization:
-> about
Console Framework
copyright (c) 2001, all rights reserved
195
A Simple Calculator
Copyright (c) 2001, all rights reserved
Of course we can do arithmetic and small errors don't break the calculator:
-> -13 + 51
result = 38.0
-> 12 / 0
Application error, can't divide by 0
-> 13.8 * 3.1416
result = 43.35408
-> x + 2
Application error, ARG1 & ARG2 must be numbers
-> quit
bye
The Console Framework
Design
Implementation
abstract public class Console {
protected BufferedReader stdin;
protected PrintWriter stdout;
protected PrintWriter stderr;
protected String prompt = "-> ";
abstract protected String execute(String cmmd) throws AppError;
public Console() {
stdout = new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(System.out)), true);
stderr = new PrintWriter(
new BufferedWriter(
196
new OutputStreamWriter(System.err)), true);
stdin = new BufferedReader(
new InputStreamReader(System.in));
}
public void controlLoop() { ... }
// overidables:
protected void help() {
stdout.println("Console Help Menu:");
stdout.println(" about: displays application information");
stdout.println(" help: displays this message");
stdout.println(" quit: terminate application");
}
protected void about() {
stdout.println("Console Framework");
stdout.println("copyright (c) 2001, all rights reserved\n");
}
protected boolean handle(AppError exp) {
stderr.println("Application error, " + exp);
return true;
}
}
The Control Loop
public void controlLoop() {
boolean more = true;
String cmmd = " ";
String result = " ";
String done = "done";
stdout.println("type \"help\" for commands");
while(more) {
try {
stdout.print(prompt);
stdout.flush(); // force the write
cmmd = stdin.readLine();
if (cmmd.equals("quit")) {
more = false;
} else if (cmmd.equals("help")) {
help();
} else if (cmmd.equals("about")) {
about();
} else { // an application-specific command?
result = execute(cmmd);
stdout.println(result);
}
} catch(AppError exp) {
more = handle(exp);
} catch (IOException exp) {
stderr.println("IO error, " + exp);
} catch (Exception exp) {
stderr.println("Serious error, " + exp);
more = false;
}
} // while
197
stdout.println("bye");
} // controlLoop
Application Errors
public class AppError extends Exception {
private String gripe;
public String toString() {
return gripe;
}
public AppError(String g) {
super(g);
gripe = g;
}
public AppError() {
super("unknown");
gripe = "unknown";
}
}
The Calculator Extension (just extend the CUI framework where needed)
class Calculator extends Console {
protected String execute(String cmmd) throws AppError { ... }
protected void help() {
super.help();
stdout.println(" ARG1 +
stdout.println(" ARG1 *
stdout.println(" ARG1 stdout.println(" ARG1 /
stdout.println("Note 1:
stdout.println("Note 2:
}
ARG2 =
ARG2 =
ARG2 =
ARG2 =
Spaces
ARG1 &
sum of ARG1 and ARG2");
product of ARG1 and ARG2");
diff of ARG1 and ARG2");
ratio of ARG1 and ARG2");
between tokens are required");
ARG2 are numbers.");
protected void about() {
super.about();
stdout.println("A Simple Calculator");
stdout.println("Copyright (c) 2001, all rights reserved");
}
public static void main(String[] args) {
Calculator calc = new Calculator();
calc.controlLoop();
}
} // Calculator
The execute() Method
protected String execute(String cmmd) throws AppError {
double arg1, arg2;
String op;
StringTokenizer tokens = new StringTokenizer(cmmd);
198
try {
String digits = tokens.nextToken();
arg1 = Double.valueOf(digits).doubleValue();
op = tokens.nextToken();
digits = tokens.nextToken();
arg2 = Double.valueOf(digits).doubleValue();
if (op.equals("+")) {
return "result = " + (arg1 + arg2);
} else if (op.equals("*")) {
return "result = " + (arg1 * arg2);
} else if (op.equals("-")) {
return "result = " + (arg1 - arg2);
} else if (op.equals("/")) {
if (arg2 == 0)
throw new AppError("can\'t divide by 0");
return "result = " + (arg1/arg2);
} else {
throw new AppError("operator must be +, -, *. or /");
}
} catch (NumberFormatException e) {
throw new AppError("ARG1 & ARG2 must be numbers");
} catch (NoSuchElementException e) {
throw new AppError("usage: ARG1 OP ARG2");
}
}
Example: Pipelines
A pipe is a message queue. A message can be anything. A filter is a process, thread, or other
component that perpetually reads messages from an input pipe, one at a time, processes each
message, then writes the result to an output pipe. Thus, it is possible to form pipelines of filters
connected by pipes:
UNIX:
% cat inFile | grep pattern | sort > outFile
In this case pipes (i.e., "|") are inter process communication channels provided by the operating
system, and filters are any programs that read messages from standard input, and write their
results to standard output.
Pipes and Filters [POSA]
Other Names
Pipelines
199
Problem
The steps of a system that processes streams of data must be reusable, re orderable, replaceable, and/or
independently developed.
Solution
Implement the system as a pipeline. Steps are implemented as objects called filters. Filters receive inputs
from, and write outputs to streams called pipes. A filter knows the identity of its input and output pipes,
but not its neighboring filters.
Filter Classification
Producers - producer of messages (no input pipe) Generates a message into its output pipe
Consumers - consumer of messages. It has no output pipe. It eats messages taken from its
input pipe
Transformers - reads a message from its input pipe, modulates it, and then writes the result to
its output pipe. (This is what DOS and UNIX programmers call filters.)
Testers - reads a message from its input pipe, and then tests it. If the message passes the test, it
is written, unaltered, to the output pipe; otherwise, it is discarded. (This is what signal
processing engineers call filters).
Filters can also be classified as active or passive. An active filter has a control loop that runs in
its own process or thread. It perpetually reads messages from its input pipe, processes them,
then writes the results to its output pipe. An active filter needs to be derived from a thread class
provided by the operating system:
class Filter extends Thread { ... }
void controlLoop() // for active filter
{
while(true)
{
Message val = inPipe.read();
val = transform(val); // do something to val
outPipe.write(val);
}
}
When activated, a passive filter reads a single message from its input pipe, processes it, and
then writes the result to its output pipe:
void activate()
{
Message val = inPipe.read();
val = transform(val); // do something to val
outPipe.write(val);
}
200
There are two types of passive filters. A data-driven filter is activated when another filter
writes a message into its input pipe. A demand-driven filter is activated when another filter
attempts to read a message from its empty output pipe.
Dynamic Structure: Data-Driven
Dynamic Structure: Demand-Driven
A Problem
How does the transformer know when to call pipe1.read()? How does the data-driven
consumer know when to call pipe2.read()? How does the demand-driven producer know
when to produce a message? Active filters solve this problem by polling their input pipes or
blocking when they read from an empty input pipe, but this is only feasible if each filter is
running in its own thread or process.
We could have the producer in the data-driven model signal the transformer after it writes a
message into pipe 1. The transformer could then signal the consumer after it writes a message
into pipe 2. In the demand-driven model the consumer could signal the transformer when it
201
needs data, and the transformer could signal the producer when it needs data. But this solution
creates dependencies between neighboring filters. The same transformer couldn't be used in a
different pipeline with different neighbors.
Pipes are publishers and filters are subscribers. In the data-driven model filters subscribe to
their input pipes. In the demand-driven model filters subscribe to their output pipes.
PIPES: A Pipeline Toolkit
PIPES is a package containing reusable declarations of pipes and filters. Programmers create
filters (transformers, testers, producers, and consumers) by creating extensions of PIPES
classes. Pipelines are constructed by instantiating these extension classes, then connecting them
with pipes. (Thus, PIPES is a toolkit, not a framework.)
For example, pipeline that sums the squares of odd integers read from the keyboard.
import pipes.*;
public class SOS {
class
class
class
class
Square extends Transformer { ... } // inner classes
OddTester extends Tester { ... }
Accum extends Consumer { ... }
NumReader extends Producer { ... }
public SOS() {
Pipe p1 = new Pipe(), p2 = new Pipe(), p3 = new Pipe();
NumReader f1 = new NumReader(p1);
OddTester f2 = new OddTester(p1, p2);
Square f3 = new Square(p2, p3);
Accum f4 = new Accum(p3);
f1.start();
}
public static void main(String[] args) {
SOS pipeline = new SOS();
}
}
Program Output
enter a number: 2
enter a number: 3
accum = 9
enter a number: 4
enter a number: 5
accum = 34
enter a number: 6
enter a number: 7
accum = 83
enter a number: q
PipelineError: java.lang.NumberFormatException: q
class Square extends Transformer {
public Square(Pipe ip, Pipe op) {
super(ip, op);
202
}
public Object transform(Object num) throws PipelineError {
if (!(num instanceof Integer))
throw new PipelineError("message must be an integer");
int n = ((Integer)num).intValue();
return new Integer(n * n);
}
}
class OddTester extends Tester {
public OddTester(Pipe ip, Pipe op) {
super(ip, op);
}
public boolean test(Object num) throws PipelineError {
if (!(num instanceof Integer))
throw new PipelineError("message must be an integer");
int n = ((Integer)num).intValue();
return (n%2 != 0);
}
}
class Accum extends Consumer {
private int accum = 0;
public Accum(Pipe ip) {
super(ip);
}
public void consume(Object num) throws PipelineError {
if (!(num instanceof Integer))
throw new PipelineError("message must be an integer");
int n = ((Integer)num).intValue();
accum += n;
System.out.println("accum = " + accum);
}
}
class NumReader extends Producer {
BufferedReader stdin;
public NumReader(Pipe op) {
super(op);
stdin = new BufferedReader(new InputStreamReader(System.in));
}
public Object produce() throws PipelineError {
try {
System.out.print("enter a number: ");
String s = stdin.readLine();
return Integer.valueOf(s);
} catch (IOException e) {
throw new PipelineError("" + e);
} catch (NumberFormatException e) {
throw new PipelineError("" + e);
}
}
}
Design of PIPES
Add and remove filters from pipelines without too much difficulty. Therefore, filters in a
pipeline should be independent of each other. In other words, a filter should only know the
identity of the pipes that connect it to its neighboring filters, not the neighbors themselves.
203
A pipe should be able to connect any filters. Therefore, although a filter may know the
identity of its input and output pipes, a pipe is loosely coupled to the filters it connects.
Pipes are publishers, and filters are subscribers that subscribe to their input pipes in datadrive pipelines.
Implementation of PIPES
Filters
abstract class Filter implements Observer {
protected Pipe inPipe = null, outPipe = null;
// redefine in Producer:
public void start() throws PipelineError {
throw new PipelineError("This is not a producer filter.");
}
}
Pipes
public class Pipe extends Observable {
private Object message; // the stored message
public Object read() {
return message;
}
204
public void write(Object val) {
message = val;
setChanged();
notifyObservers(); // data driven
clearChanged();
}
}
Error Handling
Exceptions should be handled by the pipeline driver (the producer in the case of a data-drive
pipeline), which can shut the pipeline down if necessary.
Our hack for solving this problem is to provide a global error flag that will be checked by every
update method. If the flag is not null, then an update method does nothing. If not null, and if an
exception is thrown while the update method is processing a message, then the update method
catches the exception and quietly assigns it to the global error flag.
public class PipelineError extends Exception {
public PipelineError(String gripe) {
super(gripe);
}
static PipelineError flag = null; // global error flag
}
Consumer
public class Consumer extends Filter {
public Consumer(Pipe ip) {
inPipe = ip;
ip.addObserver(this);
}
public void update(Observable p, Object what) {
if (PipelineError.flag == null) {
try {
Object val = inPipe.read();
consume(val);
} catch (PipelineError e) {
PipelineError.flag = e;
}
}
}
protected void consume(Object msg) throws PipelineError {}
}
Transformers
public class Transformer extends Filter {
public Transformer(Pipe ip, Pipe op) {
inPipe = ip;
outPipe = op;
ip.addObserver(this);
}
public void update(Observable p, Object what) {
if (PipelineError.flag == null) {
try {
Object val = inPipe.read();
val = transform(val);
205
outPipe.write(val);
} catch (PipelineError e) {
PipelineError.flag = e;
}
}
}
protected Object transform(Object msg) throws PipelineError {
return msg;
}
}
Testers
public class Tester extends Filter {
Tester(Pipe ip, Pipe op) {
inPipe = ip;
outPipe = op;
ip.addObserver(this);
}
public void update(Observable p, Object what) {
if (PipelineError.flag == null) {
try {
Object val = inPipe.read();
if (test(val)) outPipe.write(val);
} catch (PipelineError e) {
PipelineError.flag = e;
}
}
}
protected boolean test(Object msg) throws PipelineError {
return true;
}
}
Producer
public class Producer extends Filter {
public Producer(Pipe op) {
outPipe = op;
}
public void update(Observable p, Object what) {} // no op
public void start() {
boolean more = true;
Object msg = null;
while(more) {
try {
if (PipelineError.flag != null)
throw PipelineError.flag;
msg = produce();
outPipe.write(msg);
}
catch(PipelineError e) {
System.err.println( "" + e );
more = false;
}
catch(Exception e) {
System.err.println( "Unknown error! Shutting down" );
more = false;
}
206
}
}
protected Object produce() throws PipelineError {
return null;
}
}
Programming Notes
Stereotypes
UML can be extended using stereotypes. A stereotype is an existing UML icon with a
stereotype label of the form:
<<stereotype>>
Stereotypes can be used to indicate the role a class plays within the collaboration. For
example:
Report* Secretary::type(...) { return new Report(...); }
Note that type is a factory method. The class containing the factory method plays the role of a
"factory", the return type of the factory method plays the role of a "product", and the
association between the factory and product classes is that the factory class creates instances of
the product class.
Commonly used UML class stereotypes include:
<<powertype>> = Instances represent subclasses of another class
<<metatype>> = Instances represent other classes
<<active>> = Instances own their own thread of control
<<persistent>> = Instances can be saved to secondary memory
<<control>> = Instance represent system control objects
<<boundary>> = Instance represent system interface objects
<<entity>> = Instances represent application domain entities
<<actor>> = Instances represent external systems or users
In some cases a stereotype is so common that it earns its own icon. For example, in UML
actors are sometimes represented by stick figures:
207
The Java Collections Framework
Example:
class Fleet {
private Set members = new HashSet();
public void add(Airplane a) { members.add(a); }
public void rem(Airplane a) { members.remove(a); }
public Iterator iterator() { return members.iterator(); }
}
Fleet united = new Fleet();
united.add(new Airplane());
united.add(new Airplane());
united.add(new Airplane());
Iterator it = united.iterator();
while(it.hasNext()) {
Airplane a = (Airplane)it.next();
a.takeoff();
}
208
More on Design Patterns
(ref. Design Patterns Explained: A New Perspective on Object-Oriented Design by A.
Shalloway & J. Trott, Addison-Wesley, 2002)
Suppose we are confronted with the task of designing a program that works with graphical
widgets such as points, lines and squares. Furthermore, we have a drawing program (DP1) and
anticipate purchasing a second drawing program (or, we must modify the program to work with
a client’s drawing program). We’ll refer to these drawing programs as DP1 and DP2.
We must follow “best practices” in our software engineering – e.g. consider the “Open-Closed
Principle” (code that has been developed should be closed for modification and open for
extension).
We conclude it’s necessary to abstract on what is common amongst the various widgets – that
is Shape
Client
Shape
Point
Line
Square
We can add new types of Shapes without affecting Client’s code (it’s closed for modification)
Shape behavior:
get/set location
show/hide Shape
fill Shape
set color of Shape
209
Adaptor Pattern
Shape
+setLocation
+getLocation
+display
+unDisplay
+fill
+setColor
Point
+display
+unDisplay
+fill
Line
+display
+unDisplay
+fill
Square
+display
+unDisplay
+fill
What if client wants to add a circle Shape?
We note that a colleague has already developed a circle class
XCircle
+setLocation
+getLocation
+displayIt
+unDisplayIt
+fillIt
+setItsColor
We need to Adapt it to fit our hierarchy – we want to preserve polymorphism (that is, clients
still want to deal with Shapes so they must be able to use the circle wherever they want to deal
with generic shapes).
The Adaptor is actually the same as a wrapper.
210
Client
Shape
+setLocation
+getLocation
+display
+unDisplay
+fill
+setColor
Point
+display
+unDisplay
+fill
Line
+display
+unDisplay
+fill
Square
+display
+unDisplay
+fill
Circle
+display
+unDisplay
+fill
+setLocation
+getLocation
+setColor
XCircle
+displayIt
+unDisplayIt
+fillIt
+setLocation
+getLocation
+setItsColor
211
Façade Pattern
Suppose we have a Database used by a client
Operations:
1. Open Database and get a Model
2. Query the Model to get an Element
3. Request information from the Element
Client A
Database
Client B
Model
Element
Using this schema it’s difficult to deal with timing and coordination issues (sequencing
requests, etc.). We need to simplify the clients’ tasks and shield them from these difficult to
manage details.
Client A
Client B
Database facade
Database
Model
Element
This pattern greatly reduces the number of objects that clients must communicate with. Clients
will provide the façade with the necessary information and the façade will then deal with
timing/coordination issues. This pattern is used extensively in e-commerce applications
utilizing Enterprise JavaBeans.
212
Comparison between Façade and Adaptor Patterns
On the surface they seem to be very similar
Are there preexisting classes?
Is there an interface we must design to?
Does an object need to behave polymorphically?
Is a simpler interface needed?
213
Facade
Yes
No
No
Yes
Adaptor
Yes
Yes
Probably
No
Encapsulation
Encapsulation really refers to ANY kind of hiding:




data members
method members
objects
subclasses
The following exhibits object encapsulation:
Circle
+display
+unDisplay
+fill
+setLocation
+getLocation
+setColor
XCircle
+displayIt
+unDisplayIt
+fillIt
+setLocation
+getLocation
+setItsColor
The following quote is taken from the GOF text:
Consider what should be variable in your design. This approach is the opposite of
focusing on the cause of redesign. Instead of considering what might force a change to a
design, consider what you want to be able to change without redesign. The focus here is
on encapsulating the concept that varies, a theme of many design patterns.
Note that a user program using Shapes only sees Shapes  her/his code doesn’t care about the
kinds of Shapes  this abstract Shape class served to encapsulate the hierarchy of Shape
subclasses.
The client code is closed to modification and open to extension (we can easily add new Shapes
– subclass Shape – without requiring changes to the client code)
214
Abstract classes represent Concepts.
Their operations represent an API, a specification.
The Concrete classes (subclasses of the Abstract classes) represent the implementation.
Consider the following simple diagram depicting the relationship between Commonality and
Variability analysis:
By looking at what these objects must do (conceptual
perspective), we determine how to call them
(specification perspective)
Commonality
analysis
Conceptual
perspective
AbstractClass
+Operations
Specification
perspective
Variability
analysis
ConcreteClass
+Operations
Implementation
perspective
ConcreteClass
+Operations
When implementing these classes, ensure that the API
provides sufficient information to enable proper
implementation and decoupling
215
Bridge Pattern
Let’s extend the prior hierarchy to allow for 2 drawing programs, DP1 and DP2
Shape
+draw
Client
Rectangle
+draw
#drawLine
V1Rectangle
#drawLine
Circle
+draw
#drawCircle
V2Rectangle
#drawLine
V1Circle
#drawCircle
DP1
+draw_a_line
+draw_a_circle
V2Circle
#drawCircle
DP2
+drawline
+drawcircle
Note #drawLine is a protected method while +drawLine is a public method
Note that we have 4 kinds of Shapes: V1Rectangle, V2Rectangle, V1Circle and V2Circle
What if we include a third drawing program? We’ll need two more Shapes, yielding 6 kinds of
Shapes.
What if we add a new Shape (e.g. a pentagon)? With the 3 drawing programs will need 3 more
kinds of Shapes, yielding 9 kinds of shapes.
The size of the hierarchy is dramatically increasing!
The problem is that the abstraction (Shapes) is tightly coupled to the implementation
(drawing programs).
216
We perform Commonality Analysis amongst the concepts and note that we have two major
concepts – Shapes and Drawing Programs – with several variations in each category. The
Commonality Analysis identifies structures that are unlikely to change over time. Given the
commonality structures we can speak about what varies.
Strategy:


Find what varies and encapsulate it
Favor composition over inheritance (e.g. refer to the Adaptor Pattern example
described above)
Drawing
+drawLine
+drawCircle
Shape
+draw
#drawLine
#drawCircle
Rectangle
+draw
Circle
+draw
V1Drawing
+drawLine
+drawCircle
V2Drawing
+drawLine
+drawCircle
This design is much simpler. The cohesion is better – the shapes don’t deal with
implementation issues, in addition to the basic drawing functions; drawings don’t know about
shapes (low coupling). The hierarchy grows linearly as we add new shapes or drawing
programs.
217
Composite Patterns
Drawing
+drawLine
+drawCircle
Adaptors:
V1Drawing
+drawLine
+drawCircle
V2Drawing
+drawLine
+drawCircle
DP1
+draw_a_line
+draw_a_circle
DP2
+drawline
+drawcircle
We want to provide a common drawing interface to all of the drawing programs. Thus, we are
using V1Drawing and V2Drawing as Adaptors.
In this case, we are using the Adaptor Pattern along with the Bridge Pattern. This is a
Composite Pattern. These Composite Patterns are found frequently in practice.
218
Delegation (Appendix 6)
Consider using specialization:
class Handyman extends Carpenter { ... }
class Houskeeper extends Cook { ... }
What if same handyman gets a job as a plumber or electrician,
Cannot dynamically change the base class of a handyman:
class Handyman extends Plumber {... }
Multiple inheritance is allowed in C++ (but not Java):
class Handyman:
public Carpenter,
public Plumber,
public Electrician { ... };
may be inefficient because every handyman object now carries the attributes of all three base
classes:
.
But some handymen may never get jobs as plumbers or electricians, while others may find
work at jobs we haven't anticipated, such as gardening or painting.
Objects as States
State [Go4]
Other Names
Objects as States
Problem
219
The attributes and behavior of an object, x, may depend on its state, but its state may change dynamically.
This can lead to member function implementations based on awkward multi-way conditionals that are
difficult to maintain:
switch (state) {
case state1: ...
case state2: ...
// etc.
}
Solution
Implement each branch of the multi-way conditional as a member function in a separate class derived
from an abstract state base class. The member functions of x simply forward client requests to the
corresponding member functions of the associated state object, which may be changed dynamically.
Static Structure
Here's how the State class might be declared:
abstract class State {
abstract void serviceA(Context c);
abstract void serviceB(Context c);
abstract void serviceC(Context c);
}
class State1 extends State { ... }
class State2 extends State { ... }
class State3 extends State { ... }
class Context {
void serviceA() { if (state !=
void serviceB() { if (state !=
void serviceC() { if (state !=
void setState(State s) { state
// etc.
private State state = null; //
// etc.
}
null) state.serviceA(this); }
null) state.serviceB(this); }
null) state.serviceC(this); }
= s; }
current state
Instantly change the behavior of the context's services.
220
class State1 extends State {
void serviceC(Context c) {
if (condition2)
c.setState(new State2());
else if (condition3)
c.setState(new State3());
// etc.
}
// etc.
}
class Handyman {
void getToWork() { if (job != null) job.getToWork(this); }
void setJob(Job j) { job = j; }
private Job job = null;
}
abstract class Job {
abstract void getToWork(Handyman h);
}
class Electrician extends Job {
void getToWork(Handyman h) { /* change light bulbs, etc. */ }
}
class Plumber extends Job {
void getToWork(Handyman h) { /* plunge toilets, etc. */ }
}
class Carpenter extends Job {
void getToWork(Handyman h) { /* mend fences, etc. */ }
}
State pattern - only use when the nature of a state is too complex to be characterized by a single
variable containing a simple value such as an int, string, or float. Also, frequent state
construction and destruction can be inefficient if they are complex.
Delegation Defined
State pattern - one of many design patterns that use delegation.
In the case of the state pattern, the context object is entrusting the power and responsibility of
implementing its services to its agent or delegate, the associated state object. More concretely,
delegation means that an object transparently forwards client requests to a hidden delegate
object, which may be dynamically changed:
class Context {
void serviceA() { if (state != null) state.serviceA(this); }
// etc.
}
Delegation is an inheritance mechanism at the level of individual objects, not of classes.
Adapters
Adapter [Go4]
221
Other Names
Wrapper
Problem
An existing server class implements the functions required by a client, but the interface doesn't match the
required interface.
Solution
Insert an adapter between the client and the server. The adapter implements the interface required by the
client by calling the corresponding server functions. The adapter and server can be related by
specialization or delegation.
Static Structures
Alternatively, the adapter can access the adaptee services by delegation:
Implementations
interface Server {
void serviceA();
void serviceB();
// etc.
}
222
class Adaptee {
void serviceX() { ... }
void serviceY() { ... }
// etc.
}
class Client {
void doSomething() {
myServer.serviceA();
myServer.serviceB();
// etc.
}
// etc.
private Server myServer;
}
class Adapter extends Adaptee implements Server {
void serviceA() { serviceX(); }
void serviceB() { serviceY(); }
// etc.
}
OR
class Adapter extends implements Server {
void serviceA() { adaptee.serviceX(); }
void serviceB() { adaptee.serviceY(); }
// etc.
private Server adaptee;
}
Handle-Body Idioms
Generally speaking, a handle-body pair is a pair of objects collaborating to appear to the client
as a single object. One object, called the handle, manages the interface with the client, while
another object, called the body, provides the application logic.
class Body { // no client access, handles only!
void serviceA();
void serviceB();
// etc.
}
class Handle {
void serviceA() { if (myBody != null) myBody.serviceA(); }
void serviceB() { if (myBody != null) myBody.serviceB(); }
// etc.
private Body* myBody;
}
Applications
223
Adapter-Adaptee and Context-State are two examples of Handle-Body pairs. Also,
RMI - The Body is a Remote Object.
Client-side proxy (the handle) encapsulates the low-level communication details.
Shared Bodies
Decorators
Specialization allows us to add features to a class, but in some situations we need to add
different combinations of features to individual objects. For example, a grid encapsulates and
manages a dynamic two dimensional array of characters that provides "poor man's" graphics:
Suppose we will also need a shaded grid (i.e., a grid with a shaded background), a bordered
grid (i.e., a grid with a border around it), a read-only grid (i.e., a grid with disabled plot
functions), a shaded-read-only grid, a bordered-read-only grid, a shaded-bordered grid, and a
shaded-bordered-read-only grid. If we create a class for each combination of these features we
will end up with a complicated hierarchy of seven classes:
224
Adding a new feature, such as resizable grid, will add six new derived classes. In general, if we
are contemplating n features, there will be 2n-1 combinations of these features.
Decorator - implements a feature, then delegates to its body. The body may be the original
component or another decorator.
Decorator [Go4]
Other Names
Wrapper
Problem
In some situations we may want to add features to an individual object rather than an entire class.
Solution
For each additional feature create a new "decorator" class that implements the feature. The decorator
class also implements the interface of the original object by delegating to the original object or perhaps to
an instance of another decorator class. In this way decorators (i.e. instances of decorator classes) can be
chained together.
Static Structure
225
interface IComponent {
void serviceA();
void serviceB();
// etc.
}
class Component implements IComponent {
void serviceA() { ... }
void serviceB() { ... }
// etc.
}
class Decorator implements IComponent {
Decorator(IComponent c) { myBody = c; }
void serviceA() { myBody.serviceA(); } // default implementation
void serviceB() { myBody.serviceB(); }
// etc.
private IComponent myBody;
}
class Decorator1 extends Decorator {
Decorator1(IComponent c) { super(c); }
void serviceA() { // only service with more functionality
// added behavior goes here
super.serviceA(); // delegates to body
}
}
IComponent x = new Component();
IComponent y = new Decorator1(x);
IComponent z = new Decorator2(y);
OR
226
IComponent z = new Decorator2(new Decorator1(new Component()));
Streams
Ex.
static public PrintWriter makeWriter(OutputStream os) {
return new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(os)), true);
}
A call to this method constructs the decorator chain:
227
Presentation and Control (Appendix 7)
The Model-View-Controller Architecture
Instances of the Model class often represent the application domain itself. Model aggregates
or is composed of application domain objects (proxies). The model controls how its proxies are
created, manipulated, and destroyed. The model is also responsible for saving and restoring its
proxies.
Controller and View classes - application's user interface.
Controller class - responsible for handling messages sent by the user or by other objects.
Typically, a message is a command to update the model in some way. For example, a flight
simulator might provide users with a controller associated with the mouse and a controller
associated with an on-screen control panel filled with buttons. These controllers translate
mouse movements and button clicks into commands that control the altitude, attitude, and
speed of the airplane model:
View class - responsible for displaying the model or its components.
228
Model - relatively stable component
User interface - very volatile (changes to the user interface are more common than changes in
the application data and logic) Keep the coupling between the model and the user interface as
loose as possible. This prevents changes in the user interface from propagating to the model.
Model-View-Controller [POSE]
Other Names
MVC, Model-View-Controller architecture, Model-View architecture, Model-View Separation pattern,
Document-View architecture.
Problem
The user interface is the component most susceptible to change. These changes shouldn't propagate to
application logic and data.
Solution
Encapsulate application logic and data in a model component. View components are responsible for
displaying application data, while controller components are responsible for updating application data.
There are no direct links from the model to the view-controllers.
Static Structure
Ex.
229
Spreadsheet for Budget
Views
Model
Controllers
To take another example, a model component for a word processor might be a document
containing a chapter of a book. The user can view the document as an outline (outline view), as
separate pages (page layout view), or as a continuous page (normal view):
Word processor Views
Model
Controllers
GUI Toolkits
Library of components for building GUIs: JFC, AWT
Views and View Notification
public class MyView extends AppView {
public void update(Observable o, Object m) {
repaint();
230
}
public void drawComponent(Graphics g) { ... }
// etc.
}
Scenario
Example: MFC's Document-View Architecture (Develop word processor)
Microsoft Foundation Classes (MFC) is an application framework for building desktop
applications for the MS Windows platforms (Windows NT, Windows 9x, Windows CE,
Windows 2000). All MFC applications are based on a variant of the Model-View-Controller
architecture called the Document-View architecture. (The document is the model.)
The skeleton of an MFC application is created by a code generator called the App Wizard.
App Wizard asks questions about the application to be built then generates several header and
source files.
class CWPDoc : public CDocument { ... }; // Model in WPDoc.h
class CWPView : public CView { ... }; // View in WPView.h
231
Key member functions in the derived classes are stubs that must be filled in by the
programmer. (Code entered by the programmer will be shown in boldface type.)
class CWPDoc : public CDocument
{
private:
CString m_text;
public:
CString GetText() { return m_text; }
void Erase(int start, int end);
void Insert(int pos, CString text);
void Append(CString text);
// etc.
};,
void CWPDoc::Append(CString text)
{
m_text += text; // append text
SetModifiedFlag(true); // used to save info when quitting
UpdateAllViews(0);
}
Serialize
automatically called when "Open" or "Save" is selected from the application's File
menu.:
void CWPDoc::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
ar << m_text;
}
else
{
ar >> m_text;
}
}
232
CView is an abstract class containing virtual member functions called OnUpdate() and
OnDraw(). These functions are analogous to our update() and draw() functions.
void CWPView::OnDraw(CDC* pDC)
{
CWPDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
CRect region; // = some rectangular region
GetClientRect(&region); // region = client rectangle
CString text = pDoc->GetText(); // = text to draw
pDC->DrawText(text, &region, DT_WORDBREAK);
}
Document UpdateAllViews() - calls each view's OnUpdate() function.
void CWPView::OnUpdate(CView* pSender, LPARAM lHint, CObject*
pHint)
{
Invalidate(); // calls OnDraw(new CDC())
}
Controller - keyboard handler when user presses a key
void CWPView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
CWPDoc* pDoc = GetDocument();
pDoc->Append(nChar);
}
ClassWizard used to generate handler stubs
233
Building an Application Framework (AFW)
Can be customized into any desktop application that employs the services of the underlying
platform.
Version 1.0 provides a console user interfaces. Subsequent versions provide graphical user
interfaces.
AFW 1.0: A CUI Application Framework
Separate user interface from application data and logic.
Design
Console class - combines control and view responsibilities
234
Implementation
public class
protected
protected
protected
protected
protected
Console {
BufferedReader stdin;
PrintWriter stdout;
PrintWriter stderr;
String prompt = "-> ";
ConsoleModel model;
public Console(ConsoleModel m) { ... }
public void controlLoop() { ... }
protected void help() { ... }
protected void about() { ... }
protected boolean handle(AppError exp) { ... }
private void save() throws IOException { ... }
private void saveChanges() throws IOException { ... }
private void newModel() throws Exception { ... }
private void load() throws IOException, ClassNotFoundException {
...
}
}
public Console(ConsoleModel m) {
stdout = new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(System.out)), true);
stderr = new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(System.out)), true);
stdin = new BufferedReader(
new InputStreamReader(System.in));
model = m;
}
235
public void controlLoop() {
boolean more = true;
String cmmd = " ";
String result = " ";
String done = "done";
stdout.println("type \"help\" for commands");
while(more) {
try {
stdout.print(prompt);
stdout.flush(); // force the write
cmmd = stdin.readLine();
if (cmmd.equals("quit")) {
saveChanges();
more = false;
} else if (cmmd.equals("help")) {
help(); // override in subclass for app specific help
} else if (cmmd.equals("about")) {
about();
} else if (cmmd.equals("save")) {
save();
stdout.println(done);
} else if (cmmd.equals("load")) {
load();
stdout.println(done);
} else if (cmmd.equals("new")) {
newModel();
stdout.println(done);
} else if (cmmd.equals("save as")) {
saveChanges();
model.setUnsavedChanges(true);
stdout.println(done);
} else { // an application-specific command?
result = model.execute(cmmd);
stdout.println(result);
}
} catch(AppError exp) {
more = handle(exp);
} catch (IOException exp) {
stderr.println("IO error, " + exp);
} catch (Exception exp) {
stderr.println("Serious error, " + exp);
more = false;
}
} // while
stdout.println("bye");
} // controlLoop
protected void help() { // app independent help – override if needed
stdout.println("Console Help Menu:");
stdout.println(" about: displays application information");
stdout.println(" help: displays this message");
stdout.println(" load: load a saved model");
stdout.println(" new: create a new model");
stdout.println(" quit: terminate application");
stdout.println(" save: save model");
stdout.println(" save as: save model with a new name");
stdout.println("Application-specific Help Menu:");
model.help(stdout);
}
236
protected void about() {
stdout.println("Console Framework");
stdout.println("copyright (c) 2001, all rights reserved\n");
model.about(stdout);
}
protected boolean handle(AppError exp) {
// override if needed
stderr.println("Application error, " + exp);
return true;
}
AppError.java
AppError - base class for all application-specific errors.
public class AppError extends Exception {
private String gripe;
public String toString() {
return gripe;
}
public AppError(String g) {
super(g);
gripe = g;
}
public AppError() {
super("unknown");
gripe = "unknown";
}
}
private void save() throws IOException {
String fname = model.getFname();
if (fname == null) { // first save
stdout.print("enter file name: ");
stdout.flush(); // force the write
fname = stdin.readLine();
model.setFname(fname);
}
if (model.getUnsavedChanges()) {
ObjectOutputStream obstream =
new ObjectOutputStream(
new FileOutputStream(fname));
model.setUnsavedChanges(false);
obstream.writeObject(model);
obstream.flush();
obstream.close();
}
}
private void saveChanges() throws IOException {
if (model.getUnsavedChanges()) {
stdout.print("Save changes?(y/n): ");
stdout.flush(); // force the write
String response = stdin.readLine();
if (response.equals("y"))
save();
else
stdout.println("changes discarded");
}
}
237
private void load() throws IOException, ClassNotFoundException {
saveChanges();
stdout.print("enter file name: ");
stdout.flush(); // force the write
String fname = stdin.readLine();
ObjectInputStream ois =
new ObjectInputStream(new FileInputStream(fname));
model = (ConsoleModel) ois.readObject();
ois.close();
model.setUnsavedChanges(false); // necessary?
model.setFname(fname); // necessary?
}
private void newModel() throws Exception {
saveChanges();
stdout.print("enter class name: ");
stdout.flush(); // force the write
String cname = stdin.readLine();
Class c = Class.forName(cname);
model = (ConsoleModel)c.newInstance();
}
Developer must extend the generic ConsoleModel class to provide intended behavior.
public class ConsoleModel implements Serializable {
protected String unrecognized = "Unrecognized command: ";
private String fname = null;
private boolean unsavedChanges = false;
public boolean getUnsavedChanges() { return unsavedChanges; }
public void setUnsavedChanges(boolean flag) {
unsavedChanges = flag;
}
public String getFname() { return fname; }
public void setFname(String name) { fname = name; }
public String execute(String cmmd) throws AppError { ... }
public void help(PrintWriter str) {
str.println(
" Sorry, no application-specific help available");
}
public void about(PrintWriter str) {
str.println(
"Sorry, no application-specific info available");
}
// a test harness
public static void main(String[] args) {
Console cui = new Console(new ConsoleModel());
cui.controlLoop();
}
}
Override the execute() method to extend framework with app specific commands
public String execute(String cmmd) throws AppError {
if (cmmd.equals("throw")) {
throw new AppError(unrecognized + cmmd);
}
StringTokenizer tokens = new StringTokenizer(cmmd);
String result = "You entered " +
238
tokens.countTokens() + " token(s): ";
while (tokens.hasMoreTokens()) {
result += tokens.nextToken() + ' ';
}
return result;
}
Test Harness Demo
-> help
Console Help Menu:
about: displays application information
help: displays this message
load: load a saved model
new: create a new model
quit: terminate application
save: save model
save as: save model with a new name
Application-specific Help Menu:
Sorry, no application-specific help available
-> about
Console Framework
copyright (c) 2001, all rights reserved
Sorry, no application-specific info available
-> throw
Application error, Unrecognized command: throw
-> 46 + 19 * 23
You entered 5 token(s): 46 + 19 * 23
->
Example: Account Manager
Continue the session begun with the ConsoleModel test harness, but change models
dynamically using the new command:
-> new
enter class name: Account
done
-> help
Console Help Menu:
about: displays application information
help: displays this message
load: load a saved model
new: create a new model
quit: terminate application
save: save model
save as: save model with a new name
Application-specific Help Menu:
deposit AMT balance += AMT
withdraw AMT balance -= AMT
balance display balance
->
-> deposit 250
done
-> balance
balance = $250.0
-> withdraw 900
Application error, insufficient funds
239
-> save
enter file name: acct1
done
->
-> deposit 50
done
-> balance
balance = $300.0
-> load
Save changes?(y/n): n
changes discarded
enter file name: acct1
done
-> balance
balance = $250.0
->
Note how little work is involved for the customizer.
public class Account extends ConsoleModel {
private double balance = 0.0;
public String execute(String cmmd) throws AppError { ... }
public void help(PrintWriter str) {
str.println(" deposit AMT balance += AMT");
str.println(" withdraw AMT balance -= AMT");
str.println(" balance display balance");
}
// test harness:
public static void main(String[] args) {
Console cui = new Console(new Account());
cui.controlLoop();
}
}
public String execute(String cmmd) throws AppError {
try {
StringTokenizer tokens = new StringTokenizer(cmmd);
String op = tokens.nextToken();
if (op.equals("deposit")) {
double amt =
Double.valueOf(tokens.nextToken()).doubleValue();
if (amt < 0) {
throw new AppError("negative deposit: " + amt);
}
balance += amt;
setUnsavedChanges(true);
return "done";
} else if (op.equals("withdraw")) {
double amt =
Double.valueOf(tokens.nextToken()).doubleValue();
if (balance < amt) {
throw new AppError("insufficient funds");
}
balance -= amt;
setUnsavedChanges(true);
return "done";
} else if (op.equals("balance")) {
return "balance = $" + balance;
}
else {
240
throw new AppError(unrecognized + cmmd);
}
} catch (NumberFormatException e) {
throw new AppError("Amount must be a number");
} catch (NoSuchElementException e) {
throw new AppError("usage: deposit/withdraw AMOUNT");
}
}
AFW 2.0: A GUI Application Framework with Multiple Views
Design
The framework provides an application window with a menu bar. Users could also place other
controls in this window, which makes this window the application controller. Views are
modeless dialog boxes that connect to the model.
Implementation
public class AppWindow extends MainJFrame
implements ActionListener, MenuListener {
protected AppModel model;
private File currentDirectory = new File(".");
protected JMenu fileMenu, editMenu, viewMenu, helpMenu;
public JMenu makeMenu(String name, String[] items) { ... }
public AppWindow(AppModel m) { ... }
public void menuSelected(MenuEvent e) { ... }
public void menuDeselected(MenuEvent e) { }
public void menuCanceled(MenuEvent e) { }
public void actionPerformed(ActionEvent e) { ... }
protected void error(String gripe) { ... }
protected void handleCreateView() throws Exception { ... }
241
protected void handleNew() throws Exception { ... }
protected void handleLoad()
throws IOException, ClassNotFoundException { ... }
protected void handleSave() throws IOException { ... }
protected void handleSaveAs() throws IOException { ... }
protected void handleQuit() throws IOException { ... }
protected void handleCopy() { ... }
protected void handleCut() { ... }
protected void handlePaste() { ... }
protected void handleUndo() { ... }
protected void handleRedo() { ... }
protected void handleAbout() { ... }
protected void handleHelp() { ... }
private void saveChanges() throws IOException { ... }
}
public AppWindow(AppModel m) {
model = m;
setTitle(model.getClass().getName() + " Control");
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
fileMenu = makeMenu("&File", new String[]
{"&New", "&Save", "Sa&ve As", "&Load", "&Quit"});
editMenu = makeMenu("&Edit", new String[]
{"&Copy", "Cu&t", "&Paste", null, "&Undo", "&Redo"});
viewMenu = makeMenu("&View", new String[] {"Create"});
helpMenu = makeMenu("&Help", new String[] {"&About", "&Help"});
menuBar.add(fileMenu);
menuBar.add(editMenu);
menuBar.add(viewMenu);
menuBar.add(helpMenu);
}
public JMenu makeMenu(String name, String[] items) { //
factory method
JMenu result;
int j = name.indexOf('&'); // used as the keyboard shortcut
if ( j != -1) {
char c = name.charAt(j + 1);
String s = name.substring(0, j) + name.substring(j + 1);
result = new JMenu(s);
result.setMnemonic(c);
} else {
result = new JMenu(name);
}
for(int i = 0; i < items.length; i++) {
if (items[i] == null) {
result.addSeparator();
} else {
j = items[i].indexOf('&');
JMenuItem item;
if ( j != -1) {
char c = items[i].charAt(j + 1);
String s = items[i].substring(0, j) +
items[i].substring(j + 1);
item = new JMenuItem(s, items[i].charAt(j + 1));
item.setAccelerator(
242
KeyStroke.getKeyStroke(c, InputEvent.CTRL_MASK));
} else { // no accelerator or shortcut key
item = new JMenuItem(items[i]);
}
item.addActionListener(this);
result.add(item);
}
result.addMenuListener(this);
}
return result;
}
public void menuSelected(MenuEvent e) {
int j = editMenu.getItemCount();
for(int i = 0; i < j; i++) {
JMenuItem mi = editMenu.getItem(i);
String arg2 = null;
if (mi != null)
arg2 = mi.getActionCommand();
if (arg2 != null &&
(arg2.equals("Copy") ||
arg2.equals("Cut") ||
arg2.equals("Paste"))) {
mi.setEnabled(false); // disable menu item
}
}
}
public void actionPerformed(ActionEvent e) {
// it would be better to provide a handler for each menu item
// rather than a multiway switch; override these handle methods
try {
if (e.getSource() instanceof JMenuItem) {
String arg = e.getActionCommand();
if (arg.equals("New")) handleNew();
else if (arg.equals("Load")) handleLoad();
else if (arg.equals("Save")) handleSave();
else if (arg.equals("Save As")) handleSaveAs();
else if (arg.equals("Quit")) handleQuit();
else if (arg.equals("Copy")) handleCopy();
else if (arg.equals("Cut")) handleCut();
else if (arg.equals("Paste")) handlePaste();
else if (arg.equals("Undo")) handleUndo();
else if (arg.equals("Redo")) handleRedo();
else if (arg.equals("Create")) handleCreateView();
else if (arg.equals("About")) handleAbout();
else if (arg.equals("Help")) handleHelp();
}
repaint(); // make the menu disappear
} catch (Exception x) {
error("Exception thrown: " + x);
}
}
protected void error(String gripe) {
JOptionPane.showMessageDialog(null,
gripe,
"OOPS!",
JOptionPane.ERROR_MESSAGE);
}
243
private void saveChanges() throws IOException {
if (model.getUnsavedChanges()) {
int response =
JOptionPane.showConfirmDialog(
null,
"Save changes?",
"Save Changes?",
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE);
if (response == 0) handleSave();
}
}
protected void handleNew() throws Exception {
saveChanges();
String cname =
(String) JOptionPane.showInputDialog(null,
"Enter class name",
"New Model",
JOptionPane.QUESTION_MESSAGE);
if (cname != null) {
Class c = Class.forName(cname);
model = (AppModel)c.newInstance();
setTitle(model.getClass().getName() + " Control");
}
}
protected void handleLoad()
throws IOException, ClassNotFoundException {
saveChanges();
JFileChooser fd = new JFileChooser();
fd.setCurrentDirectory(currentDirectory);
fd.showOpenDialog(this);
currentDirectory = fd.getCurrentDirectory();
File ff = fd.getSelectedFile();
String fname = null;
if (ff != null)
fname = ff.getName();
if (fname != null) {
ObjectInputStream ois =
new ObjectInputStream(new FileInputStream(fname));
model = (AppModel) ois.readObject();
ois.close();
model.setUnsavedChanges(false); // necessary?
model.setFname(fname); // necessary?
}
}
protected void handleSave() throws IOException {
String fname = model.getFname();
if (fname == null) { // first save
JFileChooser fd = new JFileChooser();
fd.setCurrentDirectory(currentDirectory);
fd.showSaveDialog(this);
currentDirectory = fd.getCurrentDirectory();
File ff = fd.getSelectedFile();
if (ff != null)
fname = ff.getName();
else
fname = null;
model.setFname(fname);
244
}
if (fname != null && model.getUnsavedChanges()) {
ObjectOutputStream obstream =
new ObjectOutputStream(
new FileOutputStream(fname));
model.setUnsavedChanges(false);
obstream.writeObject(model);
obstream.flush();
obstream.close();
}
}
protected void handleSaveAs() throws IOException {
saveChanges();
model.setUnsavedChanges(true);
model.setFname(null);
handleSave();
}
protected void handleQuit() throws IOException {
saveChanges();
System.exit(0);
}
protected void handleCopy() { // override if functionality is needed
error("Sorry, this item isn't implemented");
}
protected void handleAbout() {
JOptionPane.showMessageDialog(null,
new String[] {"Application Framework",
"Copyright(c) 2001",
"All rights reserved"},
"About",
JOptionPane.INFORMATION_MESSAGE);
}
protected void handleHelp() {
error("Sorry, this item isn't implemented");
}
protected void handleCreateView() throws Exception {
AppView av = null;
String cname =
(String) JOptionPane.showInputDialog(null,
"Enter class name",
"New Model",
JOptionPane.QUESTION_MESSAGE);
if (cname != null) {
Class c = Class.forName(cname);
Constructor cons = c.getConstructor(
new Class[] {this.getClass(), model.getClass()});
av = (AppView) cons.newInstance(new Object[] {this, model});
}
av.show();
}
A more usable View menu would list each known type of view as a menu item. In this fashion
users wouldn't need to remember the names of the types of views.
245
abstract public class AppView extends JDialog implements Observer {
protected AppModel model;
protected AppWindow window;
public AppView(AppWindow parent, AppModel m) {
super(parent, m.getClass().getName() + " View", false);
window = parent; // necessary?
setSize(250, 250);
model = m;
model.addObserver(this);
addWindowListener(new Terminator());
}
private class Terminator extends WindowAdapter {
public void WindowClosing(WindowEvent e) {
setVisible(false);
AppWindow ap = (AppWindow)getOwner();
//ap.remView(this);
model.deleteObserver(AppView.this);
}
}
}
public class AppModel extends Observable implements Serializable {
private String fname = null;
private boolean unsavedChanges = false;
public boolean getUnsavedChanges() { return unsavedChanges; }
public void setUnsavedChanges(boolean flag) {
unsavedChanges = flag;
}
public String getFname() { return fname; }
public void setFname(String name) { fname = name; }
public String help(PrintWriter str) { return
"Sorry, no application-specific help available";
}
public String about(PrintWriter str) { return
"Sorry, no application-specific info available";
}
// a test harness
public static void main(String[] args) {
AppWindow w = new AppWindow(new AppModel());
w.setVisible(true);
}
}
Commands and Command Processors
Instead of directly modifying the model in response to user inputs, controllers create
commands and forward them to a centralized command processor. It is the job of the
command processor to execute the commands.
One advantage of this arrangement is that it makes it easy to provide several types of
controllers that do the same thing. For example, most menu selections have corresponding
tool bar buttons and hot key combinations that perform the same action. More advanced
users prefer the tool bar or keyboard because they are faster to use, while beginners can make
the GUI simpler by hiding the tool bar. We can avoid coding redundancy by having multiple
controllers create the same type of command. Thus a menu selection and its corresponding tool
bar button can create the same type of command object:
246
Command Processor [POSA] [Go4]
Other Names
Commands are also called actions and transactions.
Problem
A framework wishes to provide an undo/redo mechanism, and perhaps other features such as scheduling,
concurrency, roll back, history mechanisms, or the ability to associate the same action to different types
of controllers (e.g. for novice and advanced user).
Solution
Create an abstract base class for all commands in the framework. This base class specifies the interface all
concrete command types must implement.
Commands are created by controllers such as menu selections, buttons, text boxes, and consoles. The
commands are forwarded to a command processor, which can store, execute, undo, redo, and schedule
them.
In the smart command variant of the pattern, commands know how to execute themselves. In the dumb
command variant commands are simply tokens, and the command processor must know how to execute
them.
Scenario
In the smart command variant, the commands are objects that know how to execute and undo
themselves:
247
AFW 3.0: A GUI Application Framework with a Command Processor
Design
Assign command processor duties to the AppWindow. The AppWindow maintains two
stacks of commands: the stack of commands that can be undone, and the stack of
commands that can be redone.
The AFW Command Hierarchy
248
Implementation
The AppView and AppModel classes are unchanged from AFW 2.0.
The Command Class
public abstract class Command extends AbstractAction {
protected AppModel model;
protected AppWindow window;
private boolean undoable; // e.g., Print is not undoable
public ActionEvent ae = null;
public boolean getUndoable() { return undoable; }
public void setUndoable(boolean b) { undoable = b; }
public Command(AppWindow w) {
undoable = true;
window = w;
model = window.getModel();
}
protected void error(String gripe) { // used?
JOptionPane.showMessageDialog(null,
gripe,
"OOPS!",
JOptionPane.ERROR_MESSAGE);
}
public void undo() throws AppError {
throw new AppError("this command can't be undone");
}
public void execute() throws AppError {
throw new AppError(
"Sorry, this command isn't implemented yet");
249
}
public void actionPerformed(ActionEvent evt) {
ae = evt;
window.execute(this);
}
}
Swing components can fire abstract actions which provide their own actionPerformed()
methods. We can think of abstract actions as a general case of our more specific notion of an
AFW 3.0 command. To preserve compatibility with Swing's abstract actions, we subclass the
AbstractAction class and provide an actionPerformed() method that asks the command
processor to execute this command.
The AppWindow Class (Controller)
AFW 3.0 application windows are different from their AFW 2.0 ancestors in several ways.
First, they provide the command processor machinery: an undo stack, a redo stack, and undo(),
redo(), and execute() methods. Second, menu items create commands and pass them to the
execute() method. Third, an AFW 3.0 application window has a toolbar with buttons that
duplicate popular menu items. These buttons simply create the same command objects their
menu item counterparts create.
public class AppWindow extends MainJFrame
implements MenuListener {
protected AppModel model;
private File currentDirectory = new File(".");
protected JMenu fileMenu, editMenu, viewMenu, helpMenu;
private Stack undo = new Stack();
private Stack redo = new Stack();
public AppWindow(AppModel m) { ... }
protected void error(String gripe) { ... }
public AppModel getModel() { return model; }
// utilities for making menus & toolbars:
public JMenu makeMenu(String name, Command[] actions) { ... }
public JToolBar makeToolBar(Command[] actions) { ... }
// command processor methods:
public void undo() { ... }
public void redo() { ... }
public void execute(Command c) { ... }
// MenuListener methods:
public void menuSelected(MenuEvent e) { ... }
public void menuDeselected(MenuEvent e) { }
public void menuCanceled(MenuEvent e) { }
}
public AppWindow(AppModel m) {
model = m;
setTitle(model.getClass().getName() + " Control");
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
Command[] fileCmmds = new Command[] {
new FileNewCommand(this),
new FileSaveCommand(this, false),
250
new FileSaveCommand(this, true),
new FileLoadCommand(this),
new FileQuitCommand(this)
};
Command[] editCommands = new Command[] {
new EditCommand(this, "Copy"),
new EditCommand(this, "Cut"),
new EditCommand(this, "Paste"),
null,
new EditUndoRedoCommand(this, true),
new EditUndoRedoCommand(this, false)
};
Command[] helpCommands = new Command[] {
new AboutCommand(this),
new HelpCommand(this, "help")
};
Command[] viewCommands = new Command[] {
new ViewCommand(this)
};
Command[] toolBarCommands = new Command[] {
new FileSaveCommand(this, false),
new FileLoadCommand(this),
null,
new EditUndoRedoCommand(this, true),
new EditUndoRedoCommand(this, false)
};
fileMenu = makeMenu("&File",
editMenu = makeMenu("&Edit",
viewMenu = makeMenu("&View",
helpMenu = makeMenu("&Help",
menuBar.add(fileMenu);
menuBar.add(editMenu);
menuBar.add(viewMenu);
menuBar.add(helpMenu);
fileCmmds);
editCommands);
viewCommands);
helpCommands);
JToolBar toolBar = makeToolBar(toolBarCommands);
Container pane = getContentPane();
pane.setLayout(new BorderLayout());
pane.add(toolBar, BorderLayout.NORTH);
} // AppWindow()
public JMenu makeMenu(String name, Command[] actions) {
JMenu result = null;
String s = name;
int j = name.indexOf('&');
if ( j != -1) {
char c = name.charAt(j + 1);
s = name.substring(0, j) + name.substring(j + 1);
result = new JMenu(s);
result.setMnemonic(c);
} else {
result = new JMenu(s);
}
for(int i = 0; i < actions.length; i++) {
251
if (actions[i] == null) {
result.addSeparator();
} else {
result.add(actions[i]);
}
}
return result;
}
public JToolBar makeToolBar(Command[] actions) {
// Toolbar Factory Method
JToolBar result = new JToolBar(JToolBar.HORIZONTAL);
result.setFloatable(true);
for(int i = 0; i < actions.length; i++) {
if (actions[i] == null) {
result.addSeparator();
} else {
result.add(actions[i]);
}
}
return result;
}
Both factory methods work because our Command class extends the AbstractAction class.
public void undo() {
try {
Command cmmd = (Command)undo.pop();
cmmd.undo();
redo.push(cmmd);
} catch(Exception e) {
error(e.toString());
}
}
public void redo() {
try {
Command cmmd = (Command)redo.pop();
cmmd.execute();
undo.push(cmmd);
} catch(AppError e) {
error(e.toString());
} catch (Exception e) {
error(e.toString());
}
}
public void execute(Command c) {
try {
c.execute();
if (c.getUndoable())undo.push(c);
repaint();
} catch(AppError e) {
error(e.toString());
} catch (Exception e) {
error(e.toString());
}
}
252
The commands fired by all File menu items extend the FileCommand base class.
class FileCommand extends Command {
static protected File currentDirectory = new File(".");
public FileCommand(AppWindow w) {
super(w);
setUndoable(false);
}
protected void saveChanges() throws IOException { ... }
protected void handleSave() throws IOException { ... }
public void execute() {
try {
saveChanges();
} catch(Exception e) {
throw new AppError(e.toString());
}
}
}
protected void saveChanges() throws IOException {
if (model.getUnsavedChanges()) {
int response =
JOptionPane.showConfirmDialog(
null,
"Save changes?",
"Save Changes?",
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE);
if (response == 0) handleSave();
}
}
protected void handleSave() throws IOException {
String fname = model.getFname();
if (fname == null) { // first save
JFileChooser fd = new JFileChooser();
fd.setCurrentDirectory(currentDirectory);
fd.showSaveDialog(null);
currentDirectory = fd.getCurrentDirectory();
File ff = fd.getSelectedFile();
if (ff != null)
fname = ff.getName();
else
fname = null;
model.setFname(fname);
}
if (fname != null && model.getUnsavedChanges()) {
ObjectOutputStream obstream =
new ObjectOutputStream(
new FileOutputStream(fname));
model.setUnsavedChanges(false);
obstream.writeObject(model);
obstream.flush();
obstream.close();
}
}
class FileLoadCommand extends FileCommand {
public FileLoadCommand(AppWindow w) {
super(w);
putValue(Action.NAME, "Load");
253
putValue(Action.SHORT_DESCRIPTION, "Load model file");
setUndoable(false);
}
protected void handleLoad()
throws IOException, ClassNotFoundException {
saveChanges();
JFileChooser fd = new JFileChooser();
fd.setCurrentDirectory(currentDirectory);
fd.showOpenDialog(null);
currentDirectory = fd.getCurrentDirectory();
File ff = fd.getSelectedFile();
String fname = null;
if (ff != null)
fname = ff.getName();
if (fname != null) {
ObjectInputStream ois =
new ObjectInputStream(new FileInputStream(fname));
model = (AppModel) ois.readObject();
ois.close();
model.setUnsavedChanges(false); // necessary?
model.setFname(fname); // necessary?
}
}
public void execute() {
try {
System.out.println("Load selected");
handleLoad();
} catch (Exception e) {
throw new AppError(e.toString());
}
}
}
class FileSaveCommand extends FileCommand {
private boolean saveAsFlag = false;
public FileSaveCommand(AppWindow w, boolean sa) {
super(w);
saveAsFlag = sa;
if (!saveAsFlag) {
putValue(Action.NAME, "Save");
putValue(Action.SHORT_DESCRIPTION, "Save model to a file");
} else {
putValue(Action.NAME, "Save As");
putValue(Action.SHORT_DESCRIPTION,
"Save model to a new file");
}
setUndoable(false);
}
public void execute() {
try {
if (!saveAsFlag) {
handleSave();
} else {
saveChanges();
model.setUnsavedChanges(true);
model.setFname(null);
handleSave();
}
} catch (Exception e) {
throw new AppError(e.toString());
}
254
}
}
class FileNewCommand extends FileCommand {
public FileNewCommand(AppWindow w) {
super(w);
putValue(Action.NAME, "New");
putValue(Action.SHORT_DESCRIPTION, "Create a new model");
setUndoable(false);
}
protected void handleNew() throws Exception {
saveChanges();
String cname =
(String) JOptionPane.showInputDialog(null,
"Enter class name",
"New Model",
JOptionPane.QUESTION_MESSAGE);
if (cname != null) {
Class c = Class.forName(cname);
model = (AppModel)c.newInstance();
window.setTitle(model.getClass().getName() + " Control");
}
}
public void execute() {
try {
handleNew();
} catch (Exception e) {
throw new AppError(e.toString());
}
}
}
class FileQuitCommand extends FileCommand {
public FileQuitCommand(AppWindow w) {
super(w);
putValue(Action.NAME, "Quit");
putValue(Action.SHORT_DESCRIPTION, "Quit application");
setUndoable(false);
}
public void execute() {
try {
saveChanges();
System.exit(0);
} catch (Exception e) {
throw new AppError(e.toString());
}
}
}
The commands fired by all Edit menu items extend the EditCommand base class:
class EditCommand extends Command {
static protected Object clipBoard;
public EditCommand(AppWindow w) {
super(w);
setUndoable(false);
}
public EditCommand(AppWindow w, String name) {
super(w);
255
setUndoable(false);
putValue(Action.NAME, name);
}
}
class EditUndoRedoCommand extends EditCommand {
private boolean redoCommand;
public EditUndoRedoCommand(AppWindow w, boolean redoFlag) {
super(w);
redoCommand = redoFlag;
if (!redoCommand) {
putValue(Action.NAME, "Undo");
putValue(Action.SHORT_DESCRIPTION, "Undoes last undoable");
} else {
putValue(Action.NAME, "Redo");
putValue(Action.SHORT_DESCRIPTION,
"Redoes last undone command");
}
setUndoable(false);
}
public void execute() {
if (redoCommand)
window.redo();
else
window.undo();
}
}
class HelpCommand extends Command {
public HelpCommand(AppWindow w) {
super(w);
setUndoable(false);
}
public HelpCommand(AppWindow w, String name) {
super(w);
setUndoable(false);
putValue(Action.NAME, name);
}
}
class AboutCommand extends HelpCommand {
public AboutCommand(AppWindow w) {
super(w);
putValue(Action.NAME, "About");
putValue(Action.SHORT_DESCRIPTION, "About this application");
}
protected void handleAbout() {
JOptionPane.showMessageDialog(null,
new String[] {"Application Framework",
"Copyright(c) 2001",
"All rights reserved"},
"About",
JOptionPane.INFORMATION_MESSAGE);
}
public void execute() {
handleAbout();
}
}
class ViewCommand extends Command {
public ViewCommand(AppWindow w) {
256
super(w);
putValue(Action.NAME, "Create");
putValue(Action.SHORT_DESCRIPTION,
"Create a new view of the model");
setUndoable(false);
}
protected void handleCreateView() throws Exception {
AppView av = null;
String cname =
(String) JOptionPane.showInputDialog(null,
"Enter class name",
"New Model",
JOptionPane.QUESTION_MESSAGE);
if (cname != null) {
Class c = Class.forName(cname);
Constructor cons = c.getConstructor(
new Class[] {window.getClass(), model.getClass()});
av = (AppView) cons.newInstance(
new Object[] {window, model});
}
av.show();
}
public void execute() throws AppError {
try {
handleCreateView();
} catch(Exception e) {
throw new AppError(e.toString());
}
}
}
Example: Polygon Viewer Customization of Version 3.0 of AFW
257
Some menu items are unimplemented
258
Two views of a five-sided polygon model have been created
Clicking on either view with the left mouse button causes the number of sides to increment by
one. This change is made in the underlying polygon model. All open views immediately redraw
themselves using the updated number of sides:
If the user attempts to quit the application or load a new model.
259
Design
Implementation
class Poly extends AppModel {
private int sides = 5;
public int getSides() { return sides; }
public void incSides() {
sides = Math.max((sides + 1) % 20, 3);
setUnsavedChanges(true);
setChanged();
notifyObservers();
clearChanged();
}
public void decSides() {
sides = sides - 1;
if (sides < 3) sides = 20;
setUnsavedChanges(true);
setChanged();
notifyObservers();
clearChanged();
}
// test harness:
public static void main(String[] args) {
AppWindow aw = new AppWindow(new Poly());
aw.setVisible(true);
}
}
class IncSidesCommand extends Command {
public IncSidesCommand(AppWindow w) {
super(w);
}
public void execute() {
Poly p = (Poly) model;
p.incSides();
}
260
public void undo() {
Poly p = (Poly) model;
p.decSides();
}
}
public class PolyGraphView extends AppView {
PolyPanel myPanel;
public void update(Observable o, Object m) {
myPanel.repaint();
}
class PolyPanel extends JPanel { ... }
class MouseHandler extends MouseAdapter {
public void mouseClicked(MouseEvent e) {
window.execute(new IncSidesCommand(window));
}
}
public PolyGraphView(AppWindow aw, Poly p) {
super(aw, p);
Container contentPane = getContentPane();
myPanel = new PolyPanel();
contentPane.add(myPanel);
addMouseListener(new MouseHandler());
}
}
class PolyPanel extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
Poly pol = (Poly) PolyGraphView.this.model;
int sides = pol.getSides();
int red = 255;
int blue = 175;
int green = 0;
g.setColor(new Color(red, green, blue));
// calculate client area dimensions:
Dimension d = getSize();
Insets in = getInsets();
int clientWidth = d.width - in.right - in.left;
int clientHeight = d.height - in.bottom - in.top;
int xUpperLeft = in.right;
int yUpperLeft = in.top;
int xLowerRight = xUpperLeft + clientWidth;
int yLowerRight = yUpperLeft + clientHeight;
int xCenter = xUpperLeft + clientWidth/2;
int yCenter = yUpperLeft + clientHeight/2;
// draw polygon:
Polygon p = new Polygon();
double radius = Math.min(clientHeight, clientWidth)/2;
double angle = 2 * Math.PI/sides;
for(int i = 0; i < sides; i++)
p.addPoint(
(int)(xCenter + radius * Math.cos(i * angle)),
(int)(yCenter + radius * Math.sin(i * angle)));
g.drawPolygon(p);
// draw "sides = ..." string
g.setColor(Color.black);
String s = "# sides = " + sides;
261
FontMetrics fm = g.getFontMetrics();
int w = fm.stringWidth(s);
int h = fm.getHeight();
g.drawString(s, xCenter - w/2, yCenter - h/2);
}
} // paintComponent()
Mementos
Memento [Go4]
Other Names
Token
Problem
The command processor pattern assumes commands know how to undo themselves. Using this pattern in
a framework can mean a lot of work for customizers who must provide implementations of undo() in each
of their concrete command classes. Alternatively, the framework's command processor or command base
class could simply save the non re-computable state of the model before each command is executed. If the
command is undone, then the former state of the model can easily be restored. Unfortunately, the internal
state of a model is usually private. Allowing the framework to access this data would violate
encapsulation.
Solution
A memento is an object created by a model that encapsulates some or all of its internal state at a given
moment in time. The memento can be stored inside of a command or command processor without
allowing access to its encapsulated data. The object that stores the memento is called a caretaker. The
undo() function simply passes the memento back to the model, where the encapsulated data is extracted
and used to restore the model's state to what it was at the time the memento was created.
Static Structure
When a command is executed, it first asks its model to make a memento encapsulating its state
information. Although the command has no idea what's inside the memento, it keeps the
memento until the command is undone:
262
Resource Managers
What would happen, for example, if a program created a fake desktop, modified a database
while another program was querying it, or created a run away thread that couldn't be
interrupted?
Resource Manager [ROG]
Other Names
Object manager, lifecycle manager
Problem
In some situations we may need to hide from clients the details how certain resources are allocated,
deallocated, manipulated, serialized, and deserialized. In general, we may need to control client access to
these objects.
Solution
A resource manager is responsible for allocating, deallocating, manipulating, tracking, serializing, and
deserializing certain types of resource objects. A resource manager adds a layer of indirection between
clients and resources. This layer may be used to detect illegal or risky client requests. In addition, the
resource manager may provide operations that can be applied to all instances of the resource such as
"save all", statistical information such as "get count", or meta-level information such as "get properties".
Static Structure
263
class Manager { // singleton
public int open() { ... } // resource factory method
public bool close(int i) { ... }
public bool serviceA(int i) { ... }
public bool serviceB(int i) { ... }
// etc.
private Map open = new Hashtable(); // all instances of open
// resources
private bool authorized(...); // various parameters
}
bool serviceA(int i) {
if (!authorized(...)) return false; // fail
Resource r = (Resource)open.get(Integer(i));
r.serviceA();
return true; // success
}
Authorization can have a variety of meanings: Does the requested resource exist? Does the
client have access rights to it? Is it currently available? Is the proposed operation legal?
An operating system provides resource managers for most classes of system resources:
264
Integrating Patterns in the Design of a Hierarchical File System
(reference: PATTERN HATCHING Design Patterns Applied J. Vlissides, Addison-Wesley,
1998)
Four main benefits of patterns:
1. They capture expertise and make it accessible to non-experts.
2. Their names collectively form a vocabulary that helps developers communicate better.
3. They help people understand a system more quickly when it is documented with the
patterns it uses.
4. They facilitate restructuring a system whether or not it was designed with patterns in mind.
Goals for the system:

Provide code for obtaining name of the files/directories

Treat files and directories in a uniform fashion.

Provide for extensions without major revisions (easy to extend the system)
265
Application of the Composite Pattern
Classes that immediately stand out: File Directory
We should provide a common interface for files/directories
Note that directories aggregate files (this property dictates the use of an aggregation variable in
the Directory class)
266
class Node {
public:
// declare common interface here
protected:
Node();
Node(const Node&);
};
class File : public Node {
public:
File();
// redeclare common interface here
};
class Directory : public Node {
public:
Directory();
// redeclare common interface here
private:
list<Node*> _nodes;
};
What is in the common interface?
names and sizes
What do we do with operations that don't apply to both?
e.g. retrieve a list of files:
virtual Node* getChild(int n);
// get the nth child of the current Node; this child can be either a File or
// Directory.
// What if we try to get the child of a child? n->getChild(i) -> getChild(j)
// Since getChild returns a Node it must be defined in the common base
// class which means in order for the cascaded getChild to work we need to
// downcast the result of n->getChild(i) to a Directory.
// But, we should avoid downcasting whenever possible since this will add
// complexity to the code
267
The file system requirements indicate that the Composite Pattern is a good fit.
Intent of the Composite Pattern:
Compose objects into tree structures to represent part-whole hierarchies and give clients
a uniform way of dealing with these objects whether they are internal nodes or leaves.
Applicability Section - use Composite when
 you want to represent part-whole hierarchies of objects.
 you want clients to be able to ignore the difference between compositions of objects and
individual objects. Clients will treat all objects in the composite structure uniformly.
Component
operation()
getChild(int)
Leaf
Composite
operation()
operation()
getChild(int)
children
Notes:
 Classes in italics are abstract classes.
 The diamond indicates that Composite aggregates its child instances (it owns them deleting a Composite will cause the deletion of its children)
 The circle at the arrowhead indicates the Composite aggregates one or more Components.
268
Directory
Add new kids (files/directories):
virtual void adopt(Node* child)
Client creates Node; directory adopts kid ==> owns kid ==> if dir deleted, all kids are
deleted
When a kid is given up it relinquishes ownership:
virtual void orphan(Node* child)
Include a mkdir command
e.g. mkdir newSubDir
mkdir subDirA/subDirB/newSubDir
subDirA and subDirB must already exist
269
mkdir version 1 - problems:
void Client::mkdir( Directory* current, const string& path) {
string subPath = subpath(path);
if (subPath.empty()) {
current->adopt(new Directory(path));
} else {
string name = head(path);
Node* child = find(name, current);
if (child) {
mkdir(child, subpath); // compilation error: see note 2
} else {
cerr << name << " nonexistent." << endl;
}
}
}
Node* Client::find( const string& name, Directory* current) {
Node* child = 0;
for (int i=0; child = current->getChild(i); ++i) {
if (name == child->getName()) {
return child;
}
}
return 0;
}
Notes:
1. head and subpath are string manipulation routines that extract the first name in a path and
the remaining names, respectively.
2. We have a problem since child should be a Directory
3. find must return a Node* since it can yield a file or a directory
270
mkdir version 2 - simplification via uniform treatment:
We can simplify the coding by treating files and directories in a uniform fashion - include
adopt and orphan functions in the Node class, as well as in the Directory class:
virtual void Node::adopt(Node* child) {
cerr << getName() << " is not a direcotry." << endl;
}
virtual void Node::orphan(Node* child) {
cerr << child->getName() << " not found." << endl;
}
virtual void Directory::adopt(Node* child) { ... }
virtual void Directory::orphan(Node* child) { ... }
Now, we can modify the heading of mkdir and find to:
void Client::mkdir( Node* current, const string& path) {
Node* Client::find( const string& name, Node* current) {
271
mkdir version 3 - use of downcasting:
If we didn't include adopt and orphan in the Node class we would have to resort to
downcasting:
void Client::mkdir( Directory* current, const string& path) {
string subPath = subpath(path);
if (subPath.empty()) {
current->adopt(new Directory(path));
} else {
string name = head(path);
Node* node = find(name, current);
if (node) {
Directory* child =
dynamic_cast<Directory*>(node);
if (child) {
mkdir(child,subPath);
} else {
cerr << getName()<< " is not a directory." <<endl;
}
} else {
cerr << name << " nonexistent." << endl;
}
}
}
Note that this non-uniform treatment of adopt and orphan forces us to use downcasting, which
produces more complex code with a new control path!
272
Adding Symbolic Links: Proxy Pattern





Similar to Unix Symbolic Links
Provides references to other nodes
Deleting links do not delete the referenced nodes
Contains there own access rights which might be different from the referenced node
Behave just like nodes (i.e. it's transparent whether operations are done on links or other
types of nodes - e.g. editing, etc.)
Which pattern is appropriate for adding Symbolic Links?
Refer to the Design Patterns text page 30 - Structural Patterns:
Structural Design Patterns
Adapter
Bridge
Composite
Decorator
Facade
Flyweight
Proxy
Aspect(s) That Can Vary
interface to an object
implementation of an object
structure and composition of an object
responsibilities of an object without
subclassing
interface to a subsystem
storage costs of objects
how an object is accessed; its location
The Proxy pattern seems like a close fit.
Refer to the Intent of the Proxy pattern (page 207):
Provide a surrogate or placeholder for another object to control access to it.
Refer to the Applicability of Proxy (page 208)
A protection proxy controls access to the original object. Protection proxies are useful when
objects should have different access rights.
273
Refer to its structure (page 209)
Subject
Client
Request()
...
...
RealSubject
realSubject
Request()
...
Proxy
Request()
...
realSubject->Request()
Notes:
 Node is the Subject
 RealSubject is either File or Directory - links can refer to either
 Refer to the Participants of Proxy:
maintains a reference that lets the proxy access the real subject. Proxy may refer to a
Subject if the RealSubject and Subject interfaces are the same.
In our case, the Subject and RealSubject are both Nodes
274
File System Class Structure with Composite and Proxy
Node
Proxy pattern
subject
getName()
getProtection()
streamIn(istream
)
streamOut(ostrea
m)
getChild(int)
adopt(Node)
orphan(Node)
Link
File
streamIn(istream)
streamIn(istream)
streamOut(ostrea
m)
getSubject()
Directory
streamIn(istream)
streamOut(ostrea
streamOut(ostrea
m)
m)
getChild(int)
adopt(Node)
orphan(Node)
Composite pattern
275
children
Visitor Pattern
The Node class is growing ==> as we add new file system features we will keep expanding the
Node class and will be forced to continually debug the class.
Nodes should implement a simple, coherent Node interface.
What if we want to add new operations such as cat, ls, etc.
Let's move these operations outside of the Node class so it remains fixed and we don't have to
continually remove new bugs.
void Client::cat (Node* node) {
Link* l;
if (dynamic_cast<File*>(node)) {
node->streamOut(cout); // stream out contents
} else if (dynamic_cast<Directory*>(node)) {
cerr << "Can't cat a directory." << endl;
} else if (l = dynamic_cast<Link*>(node)) {
cat(l->getSubject()); // cat the link's subject
}
}
Note that again, we are using dynamic_casts, which add complexity.
This strategy of fixing the structure classes and providing for extensions on operations calls for
the Visitor pattern.
Refer to the Intent of the Visitor pattern (page 331):
Represent an operation to be performed on the elements of an object structure. Visitor lets you
define a new operation without changing the classes of the elements on which it operates.
An example of the use of Visitor is drawn from the compiler project discussed in CSC 413. An
Abstract Syntax Tree (AST) is used as an internal representation of user programs processed by
the compiler. There are several operations that must be performed on the AST such as typechecking, printing for debug purposes, code generation, code optimization. There may be more
operations to be performed on the AST in practice. It would be very messy to continually
update the AST each time we desire to perform new operations on it.
The use of the Visitor pattern dictates the separation of the AST processors from the AST
itself. This scheme simplifies the design of the AST, as well as clarifying the specifications of
each of the operations in separate, distinct pieces of code.
276
The Visitor pattern dictates that we add an accept function to the Node class to accept visitors
and then add appropriate visitor functions within each class that specifies the operation to
perform.
Visitor Code With No Common Operation Behavior
class Visitor {
public:
virtual ~Visitor() {}
virtual void visit(File*) = 0; // no operation in common; pure virtual
// visitors must indicate what it means to visit a File
virtual void visit(Directory*) = 0;
virtual void visit(Link*) = 0;
protected:
Visitor();
Visitor(const Visitor&);
}
We will subclass Visitor for each type of Visitor operation.
Two examples follow:
class CatVisitor : public Visitor{ // implement the cat operation
public:
CatVisitor();
~CatVisitor() {}
void visit(File* f);
void visit(Directory* f);
void visit(Link* f);
}
CatVisitor:: visit(File* f) {
f->streamOut(cout);
}
CatVisitor:: visit(Directory* f) {
cerr << "Can't cat a directory." << endl;
}
CatVisitor:: visit(Link* f) {
f->getSubject()->accept(*this);
}
277
class SuffixPrinterVisitor : public Visitor { // print appropriate suffixes for a Node
public:
SuffixPrinterVisitor ();
~SuffixPrinterVisitor() {}
void visit(File* f) { };
void visit(Directory* f) { cout << "/"; };
void visit(Link* f) { cout << "@; };
}
Now, we can define a client ls operation:
void Client::ls (Node* n) {
SuffixPrinterVisitor suffixPrinter;
Node* child;
for (int i = 0; child = n->getChild(i); ++i) {
cout << child->getName();
child->accept(suffixPrinter);
cout << endl;
}
}
278
Visitor Code With Common Operation Behavior
class Visitor {
public:
virtual ~Visitor() {}
virtual void visit(File*) {
// common default behavior for File visiting
}
virtual void visit(Directory*) {
// common default behavior for Directory visiting
}
virtual void visit(Link*) {
// common default behavior for Link visiting
}
protected:
Visitor();
Visitor(const Visitor&);
}
279
Modifications to Code Using Visitor
Recall the previous version of cat with Downcasting complexity:
void Client::cat (Node* node) {
Link* l;
if (dynamic_cast<File*>(node)) {
node->streamOut(cout); // stream out contents
} else if (dynamic_cast<Directory*>(node)) {
cerr << "Can't cat a directory." << endl;
} else if (l = dynamic_cast<Link*>(node)) {
cat(l->getSubject()); // cat the link's subject
}
}
Revised version of cat:
void Client::cat (Node* node) {
node->accept(CatVisitor);
}
Modification to the Node class - add the following function:
virtual void Node::accept(Visitor&) = 0;
Modification to the File class - add the following function:
void File::accept(Visitor& v) { v.visit(this); }
Modification to the Directory class - add the following function:
void Directory::accept(Visitor& v) { v.visit(this); }
Modification to the Link class - add the following function:
void Link::accept(Visitor& v) { v.visit(this); }
These modifications are quite simple and avoid downcasting complexities.
Visitor Benefit
We can modify the file system by adding code rather than modifying existing code and
possibly breaking that old code.
280
Deleting Files and Directories within a Single User Protection Framework (e.g.
single user on a PC, not a multi-user system under e.g. UNIX) - the Template
Pattern
Recall the diagram presented earlier:
Node
subject
getName()
getProtection()
streamIn(istream)
streamOut(ostream)
getChild(int)
adopt(Node)
orphan(Node)
Directory
Link
File
streamIn(istream)
streamIn(istream)
streamOut(ostrea
m)
getSubject()
streamOut(ostream
)
streamIn(istream)
streamOut(ostream)
getChild(int)
adopt(Node)
orphan(Node)
Consider the following functions in the Node class:
const Protection& getProtection();
void setProtection(const Protection&); // we're adding this to the current phase
void setName(const string&);
281
children
The following table defines the protection mechanisms:
Protection
writable
unwritable
readable
unreadable
Operation notes on Nodes
clients can't delete nodes like other
objects; can't change
attributes/structure; no access to
setName, streamIn, adopt, orphan
no streamOut; no access to kids via
getChild
More notes on the unwritable protection:
 protect the destructor
can only delete within the Node class hierarchy, not by a client
this suggests the use of a private destructor which would, in turn,
disallow subclassing


disallows local Node objects (those on the stack) since the destructor
cannot be called
destructor can only be called within the Node hierarchy (this provides good control of
destruction)
if we use a friend to destroy Nodes then we would break encapsulation
since the rest of the Node's operations would be exposed, too
Use static member destruction so the destruction is still in the Node hierarchy
We could have:
static Node::destroy(Node*);
// this function will check that the Node is writable and then destroy it if so
Note that Node destruction involves two fundamental operations which must be performed in a
set order:
1. Checking whether the protection mechanism allows the destruction (is it writable or
???) and
2. Actually performing the destruction operation
Subclasses of Node can override the checking operation, as well as the destruction operation.
This suggests the use of the Template Method Pattern.
282
Refer to the Intent of the Template Method Pattern (page 325):
Define the skeleton of an algorithm in an operation, deferring some steps to subclasses.
Template Method lets subclasses redefine certain steps of an algorithm without changing the
algorithm’s structure.
Refer to the Applicability of the Template Method Pattern (page 326):
The Template Method pattern should be used to implement the invariant parts of an algorithm
once and leave it up to subclasses to implement the behavior that can vary.
void Node::destroy (Node* node) { // note the fixed template of control
if (node->isWritable()) {
delete node;
} else {
node->doWarning(undeleteableWarning);
}
}
class Node {
public:
static void destroy(Node*);
// ...
protected:
virtual ~Node(); // control Node destruction
virtual bool isWritable() = 0;
virtual void doWarning(int) = 0;
// the primitive functions isWritable and doWarning must be overridden
// ...
};
Notes:
 The invariant algorithm is given in Node::destroy
 Subclasses will define their own version of the primitives isWritable and doWarning they're protected in the Node class
283
Similar to destroy, streamOut should be a Template Method. Note that previously, it was a
pure virtual in the Node class. Now, we'll define the template in the Node class.
void Node::streamOut (ostream& out) {
if (isReadable()) {
doStreamOut(out);
} else {
doWarning(unreadableWarning);
}
}
Clearly, the Template Method primitives in this case are doStreamOut and doWarning.
284
Summary of Design Patterns Used in the File System Design
Composite: Component
Proxy: Subject
TemplateMethod:AbstractClass
Visitor: Element
Link
Leaf
Proxy
TemplateMethod:
ConcreteClass
Node
File
Director
y
Leaf
Proxy:
RealSubject
TemplateMethod
:
Composite
Proxy:
RealSubject
TemplateMethod
:
ConcreteClass
ConcreteClass
NodeVisit
or
CatVisitor
Visitor
ConcreteVisitor
285
Active and Distributed Objects (Appendix 8)
Multi-Threading
Concurrent programming-- doing several tasks in parallel, then putting the pieces together at
the end to produce a final answer.
Single processor system – time to switch programs/tasks/processes
Threads - light-weight processes; isn't a full fledged program with its own data and control
information.
Implementing and Scheduling Threads
In a simplified operating system, a thread is always in one of at least four states: Ready
(waiting for the CPU in the ready queue), Running (using the CPU), Blocked (waiting for
input), or Terminated. We can represent these states, the transitions between them, and the
actions that cause transition to occur using a state diagram:
Scheduler – determines which thread gets to use the cpu.
Running thread gives up control of a processor:
1. When its task is complete
2. When it blocks for i/o
3. When an o/s preempts a thread that attempts to starve its siblings by consuming large
slices of processor time without releasing it to the others.
4. When cooperative threads voluntarily release processor control, either by autotransitioning back to their Ready state, or by auto-transitioning to their Blocked state for
a specific amount of time (sleeping).
286
Inter-Thread Communication
Message passing - parent thread acts like a post office for child threads.
Promotes decoupling among the child threads; but, all messages must pass through the
broker. Must be limit on the amount of information that can be sent in a single message.
Shared memory – use global memory/data structures for holding communicated information;
Snchronizing issues arise. Programmers cannot predict order of execution; but, o/s
provides synchronization mechanisms: objects that work like latches and locks. If we
equip mailbox M with a locked latch, L, then if Thread B doesn't have a "key", it must
wait next to the mailbox for the latch to be unlocked. Of course this will only happen
after thread A, which does have a key, puts its message inside M.
Active Objects
Passive object does nothing unless a client calls one of its member functions.
Active object: "owns" a thread of control. It can use its thread to execute a control loop that
perpetually searches for work to be done:
1.
2.
3.
4.
5.
Inspect environment
Compare environment state to goal state
If same, quit
Update environment
Repeat
The Master-Slave Design Pattern
Master-Slave [POSA]
Problem
An identical computation must be performed many times, but with different inputs and context. The
results of these computations may need to be accumulated. If possible, we would like to take advantage of
multiple processors.
Solution
A master thread creates multiple slave threads. Each slave performs a variation of the computation,
reports the result to the master, then terminates. The master accumulates the results.
287
Each slave performs some task, and then reports its result back to the master. The master
accumulates results and produces a final result.
Java Threads
Java's Thread class makes multithreading of the underlying host operating system available to
programmers.
class Robot extends Thread {
public void run() { // overridden from Thread class
while(true) {
// run around doing stuff forever
}
}
// etc.
}
public class Factory {
public static void main(String[] args) {
for(int i = 0; i < 100; i++) {
Robot r2d2 = new Robot(...);
r2d2.start(); // launch thread; start calls run()
}
// etc.
}
}
Alternative
class Robot extends Device implements Runnable {
private Thread runner = null;
public void run() {...}
public void start() {
if (runner == null) {
runner = new Thread(this);
runner.start(); // calls run() above
}
}
// etc.
}
288
Scheduling
Thread States
Preemptive vs. Nonpreemptive Scheduling
Java multithreading is implemented using the multithreading system provided by the
host platform.
Non-preemptive system: all runnable threads wait in a ready queue for the currently running
thread to release the CPU
Preemptive system: the CPU is allocated to a thread for a fixed time slice (e.g. one second). If
a thread doesn't release the CPU before its time slice expires, it will be interrupted, sent back to
the ready queue, and the CPU will be allocated to the "next" thread in the ready queue.
The Java ready queue is a priority queue - threads are inserted according to their priority (110) Thread.getPriority() and Thread.setPriority()
Cooperative Multitasking using yield() or sleep()
Cooperative threads: use sleep/yield to share CPU use. This will mitigate the o/s specific
scheduling schemes
Example: Bouncing Balls
In the bouncing ball applet each ball is animated by its own slave thread. New balls are created
by mouse clicks. Keyboard inputs stop, suspend, and resume the balls:
289
import
import
import
import
java.awt.*;
java.awt.event.*;
java.util.*;
// for Vector
java.applet.*;
public class Bounce extends Applet {
// client area metrics
protected int xUpperLeft, yUpperLeft, xLowerRight, yLowerRight;
private Vector balls;
private int ballDiam;
private Random numberGen;
private boolean paused;
private void killAll() {
showStatus("Killing all balls");
for(int i = 0; i < balls.size(); i++) {
Ball b = (Ball)balls.elementAt(i);
b.stop();
}
balls.removeAllElements();
repaint();
}
private void resumeAll() {
if (paused) {
showStatus("Resuming all balls");
290
paused = false;
for(int i = 0; i < balls.size(); i++) {
Ball b = (Ball)balls.elementAt(i);
if (!b.isAlive())
showStatus("This ball is dead!");
b.resume(); // b moves to ready queue
}
}
}
private void suspendAll() {
if (!paused) {
showStatus("Suspending all balls");
paused = true;
for(int i = 0; i < balls.size(); i++) {
Ball b = (Ball)balls.elementAt(i);
b.suspend();
}
}
}
public void start() { updateMetrics(); resumeAll(); }
public void stop() { suspendAll(); }
public void destroy() { killAll(); }
public void init() {
addMouseListener(new MouseEventHandler());
addKeyListener(new KeyEventHandler());
Object parent = getParent();
balls = new Vector();
ballDiam = 10;
numberGen = new Random();
paused = false;
}
protected void updateMetrics() {
Dimension d = getSize(); //size of drawing area of applet
Insets in = getInsets();
int clientWidth = d.width - in.right - in.left;
int clientHeight = d.height - in.bottom - in.top;
xUpperLeft = in.right;
yUpperLeft = in.top;
xLowerRight = xUpperLeft + clientWidth;
yLowerRight = yUpperLeft + clientHeight;
}
class KeyEventHandler extends KeyAdapter {
public void keyTyped(KeyEvent e) {
// int key = e.getKeyCode(); // always 0!
char key = e.getKeyChar();
if (key == 'k') killAll();
else if (key == 'r') resumeAll();
else if (key == 's') suspendAll();
else showStatus("key pressed = " + key);
}
}
class MouseEventHandler extends MouseAdapter {
public void mouseClicked(MouseEvent e) {
showStatus("New Ball");
Point p = e.getPoint();
291
Ball b = new Ball(p.x, p.y);
balls.addElement(b);
b.start();
}
public void mouseReleased(MouseEvent e) {
showStatus("");
}
}
public void paint(Graphics g) { // use graphics context to paint
for(int i = 0; i < balls.size(); i++) {
Ball b = (Ball)balls.elementAt(i);
g.setColor(b.color);
g.fillOval(b.xc, b.yc, ballDiam, ballDiam);
}
}
// slave thread
class Ball extends Thread {
public int xc, yc;
private int dx, dy, oldxc, oldyc, red, green, blue;
public Color color;
public Ball(int x, int y) {
xc = x;
yc = y;
dx = numGen.nextInt() % 7 - 3;
dy = numGen.nextInt() % 7 - 3;
int max = Thread.MAX_PRIORITY + Thread.MIN_PRIORITY + 1;
int pri = (numGen.nextInt() % max) - Thread.MIN_PRIORITY;
red = numberGen.nextInt() % 256;
blue = numberGen.nextInt() % 256;
green = numberGen.nextInt() % 256;
color = new Color(red, green, blue);
}
private void move() {
oldxc = xc;
oldyc = yc;
xc += dx;
yc += dy;
if (xc <= xUpperLeft || xLowerRight <= xc)
dx = -dx;
if (yc <= yUpperLeft || yLowerRight <= yc)
dy = -dy;
}
public void run() {
while(true) {
move();
repaint(oldxc, oldyc, 2 * ballDiam, 2 * ballDiam);
try { Thread.sleep(5); }
catch(InterruptedException e) {}
}
}
}
} // Bounce
HTML
292
<HTML>
<TITLE> Bouncing Balls </TITLE>
<BODY>
Mouse click creates a ball. Press s to suspend, r to resume, and
k to kill.<BR>
<APPLET CODE="Bounce.class" WIDTH=300 HEIGHT=500>
Sorry, your browser can't show applets.
</APPLET>
</BODY>
</HTML>
Producer-Consumer Problems
Communication through shared memory: easy to set up in the Master-Slave architecture
because all slaves share their master's heap and static memory segments. In this case thread A
simply makes a modification to some global data structure. For example, the data structure
might represent a mailbox where messages can be stored. Later, thread B simply examines the
global data structure to see what thread A has done. For example, thread B might remove the
message from the mailbox and read it.
Problem. Suppose thread B checks the mailbox before thread A has had a chance to put
the message inside? This may result in an error!
Use synchronization mechanisms provided by the operating system. A synchronization
mechanism allows one thread to lock the global data structure until it is finished. Other threads
must wait for the data structure to be unlocked before they can access it.
Producer-Consumer problems: master thread creates a global buffer and two slave threads.
One slave is called the producer. The producer perpetually creates imaginary objects called
widgets and places them in the global buffer. The other slave is called the consumer. The
consumer repeatedly removes widgets from the buffer and consumes them:
If the slaves aren't clever, the consumer slave may start to consume the last widget in the buffer
and suspends itself, waiting for the producer to produce more widgets. At the same time, the
producer slave adds a second widget to the buffer, failing to realize that the first widget is in
the process of being consumed. If the producer only notifies the consumer when it adds a
widget to the empty buffer, no notification is sent. The producer proceeds to fill the buffer with
widgets. When the buffer is full, the producer suspends itself, waiting for the consumer to
consume some widgets to make more room in the buffer. Of course at this point both the
producer and consumer are suspended!
Example: A Joint Checking Account Simulation
Console application. consumer and producer worker threads will be equipped with pointers to a
shared checking account (the buffer). The producer repeatedly deposits money in the joint
account, while the consumer repeatedly withdraws money.
293
Implementation
Account (version 1)
class Account {
// shared buffer
private double balance = 0;
public Account(double amt) { balance = amt; }
public void withdraw(double amt) {
System.out.println("... withdrawing $" + amt);
double temp = balance;
// simulate consumption time:
try { Thread.sleep(10); }
catch(InterruptedException e) {}
if (amt <= balance) {
temp -= amt;
balance = temp;
System.out.println("... balance $" + balance);
} else {
System.out.println("... sorry, insufficient funds");
}
}
public void deposit(double amt) {
double temp = balance;
System.out.println("depositing $" + amt);
// simulate production time:
try { Thread.sleep(12); }
catch(InterruptedException e) {}
temp += amt;
balance = temp;
System.out.println("balance $" + balance);
}
}
Producer
class Producer extends Thread {
private Account account;
public Producer(Account acct) { account = acct; }
public void run() {
for(int i = 0; i < 5; i++) {
account.deposit(10);
// deposits $10
294
}
}
}
Consumer
class Consumer extends Thread {
private Account account;
public Consumer(Account acct) { account = acct; }
public void run() {
for(int i = 0; i < 5; i++) {
account.withdraw(5); // withdraws $5
}
}
}
Master
class PCMaster { // the master only ends after its slaves terminate
public static void main(String[] args) {
Account acct = new Account(100);
Producer depositor = new Producer(acct);
Consumer withdrawer = new Consumer(acct);
withdrawer.start();
depositor.start();
}
}
Program Output
C:\Pearce\JPOP\PC>java PCMaster
... withdrawing $5.0
depositing $10.0
... balance $95.0
 how did this happen??
... withdrawing $5.0
balance $110.0
depositing $10.0
... balance $90.0
... withdrawing $5.0
balance $120.0
depositing $10.0
... balance $85.0
... withdrawing $5.0
... balance $80.0
... withdrawing $5.0
balance $130.0
depositing $10.0
... balance $75.0
balance $140.0
depositing $10.0
balance $150.0
Synchronization
There was $100 in the account initially, so the closing balance should have been $100 + $50 $25 = $125, not $150. What happened?
295
The consumer starts to withdraw $5. In the middle of the transaction, the producer interrupts
and begins a $10 deposit. Now the consumer interrupts the producer to complete its
transaction, leaving a balance of $95 in the shared account. But it's too late. The producer has
already copied the $100 balance into a local variable and incremented it to $110.
Monitors
Any object that can control the number of threads that can call its methods is called a monitor.
We can make a Java object a monitor by writing "synchronized" in front of any methods that
access important member variables.
Account (version 2)
class Account {
private double balance = 0;
public Account(double amt) { balance = amt; }
synchronized public void withdraw(double amt) {
System.out.println("... withdrawing $" + amt);
double temp = balance;
// simulate consumption time:
try { Thread.sleep(10); }
catch(InterruptedException e) {}
if (amt <= balance) {
temp -= amt;
balance = temp;
System.out.println("... balance $" + balance);
} else {
System.out.println("... sorry, insufficient funds");
}
}
synchronized public void deposit(double amt) {
double temp = balance;
System.out.println("depositing $" + amt);
// simulate production time:
try { Thread.sleep(12); }
catch(InterruptedException e) {}
temp += amt;
balance = temp;
System.out.println("balance $" + balance);
296
}
}
Program Output
Notice that the producer and consumer no longer interrupt each other:
C:\Pearce\JPOP\PC>java PCMaster
... withdrawing $5.0
... balance $95.0
depositing $10.0
balance $105.0
... withdrawing $5.0
... balance $100.0
depositing $10.0
balance $110.0
... withdrawing $5.0
... balance $105.0
depositing $10.0
balance $115.0
... withdrawing $5.0
... balance $110.0
depositing $10.0
balance $120.0
... withdrawing $5.0
... balance $115.0
depositing $10.0
balance $125.0
Improving Consumption
Let’s try another experiment. Suppose the master created a joint account with an initial balance
of $0. Here's the output produced:
C:\Pearce\JPOP\PC>java PCMaster
... withdrawing $5.0
... sorry, insufficient funds
 we don’t want to turn away customers!
depositing $10.0
balance $10.0
... withdrawing $5.0
... balance $5.0
depositing $10.0
balance $15.0
... withdrawing $5.0
... balance $10.0
depositing $10.0
balance $20.0
... withdrawing $5.0
... balance $15.0
depositing $10.0
balance $25.0
... withdrawing $5.0
... balance $20.0
depositing $10.0
balance $30.0
Account (version 3)
297
When a thread calls wait() inside a monitor, the thread is blocked placed in the associated
queue. It can only be unblocked if another thread calls notifyAll() inside the same monitor.
This allows us to achieve more sophisticated forms of synchronization.
class Account {
private double balance = 0;
public Account(double amt) { balance = amt; }
synchronized public void withdraw(double amt) {
System.out.println("... withdrawing $" + amt);
while(balance < amt)
try {
wait();
} catch(InterruptedException e) {}
double temp = balance;
// simulate consumption time
try { Thread.sleep(10); }
catch(InterruptedException e) {}
if (amt <= balance) {
temp -= amt;
balance = temp;
System.out.println("... balance $" + balance);
} else {
System.out.println("... sorry, insufficient funds");
}
}
synchronized public void deposit(double amt) {
double temp = balance;
System.out.println("depositing $" + amt);
// simulate production time
try { Thread.sleep(12); }
catch(InterruptedException e) {}
temp += amt;
balance = temp;
System.out.println("balance $" + balance);
notifyAll();
}
}
Program Output
Now the consumer is never turned away hungry:
C:\Pearce\JPOP\PC>java PCMaster
depositing $10.0
balance $10.0
... withdrawing $5.0
... balance $5.0
depositing $10.0
balance $15.0
... withdrawing $5.0
... balance $10.0
depositing $10.0
balance $20.0
... withdrawing $5.0
... balance $15.0
depositing $10.0
balance $25.0
... withdrawing $5.0
... balance $20.0
298
depositing $10.0
balance $30.0
... withdrawing $5.0
... balance $25.0
Direct Communication
Objects A and B on different machines: send messages across a network using a socket.
Socket is a transceiver: combines a transmitter (an object that can be used to send or transmit
messages) with a receiver (an object that can receive messages). Telephones, CB radios, and
mailboxes are examples of transceivers.
class Socket {
public Socket(String host, int port) { ... }
public InputStream getInputStream() { ... }
public OutputStream getOutputStream() { ... }
// etc.
}
299
Example: A Date Client
Most standard Internet servers- ftp servers, telnet servers, web servers, etc.- perpetually listen
for clients at well known port numbers below 100. For example, most computers are equipped
with a date server that listens for clients at port 13. When a connection is made, the date
server sends the local date and time to the client.
import java.util.*;
import java.io.*;
import java.net.*;
public class DateClient {
protected Socket sock;
protected BufferedReader sockin;
protected PrintWriter sockout;
public DateClient(String host) { ... }
String getDate() throws IOException {
return sockin.readLine();
}
public static void main(String[] args) {
try {
DateClient client = new DateClient("localhost");
System.out.println(client.getDate());
} catch (IOException ioe) {
}
}
}
public DateClient(String host) {
try {
Socket sock = new Socket(host, 13);
sockin = new BufferedReader(
new InputStreamReader(
sock.getInputStream()));
sockout = new PrintWriter(
sock.getOutputStream(), true);
} catch(UnknownHostException uhe) {
System.err.println("unknown host " + uhe);
System.exit(1);
} catch(IOException ioe) {
System.err.println("failed to create streams " + ioe);
System.exit(1);
}
}
300
A Server Framework
Design
Implementation
public abstract class Server {
// Master
protected ServerSocket mySocket;
protected int myPort;
protected InetAddress myAddr; // not used much
public Server(int port) { ... }
// abstract handler factory:
abstract public RequestHandler makeHandler(Socket s);
// listens for a request, then creates & starts a handler
public void listen() { ... }
}
public Server(int port) {
try {
myPort = port;
mySocket = new ServerSocket(myPort);
myAddr = mySocket.getInetAddress();
} catch(IOException ioe) {
System.err.println("Failed to create socket; " + ioe);
System.exit(1);
} // catch
}
public void listen() {
try {
while(true) {
System.out.println("Server listening at " + myAddr);
Socket request = mySocket.accept(); // blocks
RequestHandler handler = makeHandler(request);
handler.start();
} // while
} catch(IOException ioe) {
System.err.println("Failed to accept socket, " + ioe);
System.exit(1);
301
} // catch
}
public abstract class RequestHandler extends Thread { // Slave
protected Socket request;
protected BufferedReader in;
protected PrintWriter out;
public RequestHandler(Socket s) { ... }
public void run() { ... }
public abstract boolean processRequest();
}
public RequestHandler(Socket s) {
request = s;
try {
in = new BufferedReader(
new InputStreamReader(
request.getInputStream()));
out = new PrintWriter(
request.getOutputStream(), true);
} catch(IOException ioe) {
System.err.println("failed to create streams; " + ioe);
System.exit(1);
}
}
public void run() {
try {
boolean more = true;
// processing the request is dependent on the instantiation
// of the framework
while(more) more = processRequest();
request.close();
} catch (IOException ioe) {
System.err.println("" + ioe);
}
}
Example: A Command Server Framework
class CommandHandler extends RequestHandler {
public CommandHandler(Socket s) { super(s); }
public boolean processRequest() { ... } // calls execute()
String execute(String cmmd) throws IOException {
if (cmmd.equals("quit"))
throw new IOException("client quitting");
return "echo: " + cmmd; // for now
}
}
public boolean processRequest() {
boolean morePlease = true;
try {
System.out.println("processing a request");
String cmmd = in.readLine();
System.out.println("command = " + cmmd);
String result = execute(cmmd);
System.out.println("result = " + result);
out.println(result);
} catch(IOException ioe) {
302
System.err.println("" + ioe);
morePlease = false;
}
return morePlease;
}
public class CommandServer extends Server {
public static int COMMAND_PORT = 4242;
public CommandServer() { super(COMMAND_PORT); }
public RequestHandler makeHandler(Socket s) {
return new CommandHandler(s);
}
public static void main(String[] args) {
Server server = new CommandServer();
server.listen();
}
}
public class CommandClient {
protected Socket sock;
protected BufferedReader sockin;
protected PrintWriter sockout;
protected BufferedReader stdin;
protected PrintWriter stdout;
protected PrintWriter stderr;
public CommandClient() { ... }
public void controlLoop() { ... }
public static void main(String[] args) {
CommandClient client = new CommandClient("localhost");
client.controlLoop();
}
}
public CommandClient(String host) {
try {
Socket sock = new Socket(host, 4242);
sockin = new BufferedReader(
new InputStreamReader(
sock.getInputStream()));
sockout = new PrintWriter(
sock.getOutputStream(), true);
stdout = new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(System.out)), true);
stderr = new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(System.out)), true);
stdin = new BufferedReader(
new InputStreamReader(System.in));
} catch(IOException ioe) {
System.err.println("failed to create streams; " + ioe);
System.exit(1);
}
}
public void controlLoop() {
boolean more = true;
String cmmd, result;
while(more) {
try {
303
stdout.print("-> ");
stdout.flush(); // force the write
cmmd = stdin.readLine();
if (cmmd.equals("quit")) {
more = false;
} else { // an application-specific command?
sockout.println(cmmd);
result = sockin.readLine();
stdout.println(result);
}
} catch (Exception exp) {
stderr.println("Serious error, " + exp);
more = false;
}
} // while
stdout.println("bye");
}
Indirect Communication
Two objects separated by a process boundary must communicate with each other through
sockets. Using sockets requires some awareness of network protocols and addressing formats.
Also, transmitting anything other than a string through a socket is difficult. However, this form
of communication is used many places, e.g., ftp, telnet protocols.
In the context of C++ objects, "face-to-face conversation" means invoking member
functions. No special transceiver device is required, parameters and return values can be
304
arbitrary objects, and objects can be located using ordinary pointers rather than protocoldependent network addresses.
Remote method invocation (RMI) combines the best of both worlds by allowing distributed
objects to communicate by ordinary member function invocations. This illusion is created by
introducing proxies- local representatives of remote objects- into the address space of each
object. Instead of direct communication, distributed objects communicate indirectly through
proxies, which hide the details of direct communication.
Proxies
Proxy [POSA], [Go4]
Other Names
Proxies are also called surrogates, handles, and wrappers. They are closely related in structure, but not
purpose, to adapters and decorators.
Problem
1. A server may provide the basic services needed by clients, but not administrative services such as
security, synchronization, collecting usage statistics, and caching recent results.
2. Inter-process communication mechanisms can introduce platform dependencies into a program. They
can also be difficult to program and they are not particularly object-oriented.
Solution
Instead of communicating directly with a server, a client communicates with a proxy object that
implements the server's interface, hence is indistinguishable from the original server. The proxy can
perform administrative functions before delegating the client's request to the real server or another proxy
that performs additional administrative functions.
Structure
305
The class diagram suggests that process or machine boundaries may exist between proxies.
Proxies that run in the same process or on the same machine as the client are called client-side
proxies, while proxies that run in the same process or on the same machine as the server are
called server-side proxies.
Scenario
As in the decorator pattern, proxies can be chained together.
306
Examples of Client-Side Proxies
A firewall proxy is essentially a filter that runs on the bridge that connects a company network
to the Internet. It filters out client requests and server results that may be inconsistent with
company policies
A cache proxy is a client-side proxy that searches a local cache containing recently received
results. If the search is successful, the result is returned to the client without the need of
establishing a connection to a remote server. Otherwise, the client request is delegated to the
server or another proxy. For example, most web browsers transparently submit requests for
web pages to a cache proxy, which attempts to fetch the page from a local cache of recently
downloaded web pages.
Virtual proxies provide a partial result to a client while waiting for the real result to arrive
from the server. For example, a web browser might display the text of a web page before the
images have arrived, or a word processor might display empty rectangles where embedded
objects occur.
Examples of Server-Side Proxies
Protection proxies can be used to control access to servers. For example, a protection proxy
might be inserted between the CIA's web server and the Internet. It demands user
identifications and passwords before it forwards client requests to the web server.
A synchronization proxy uses techniques similar to the locks discussed earlier to control the
number of clients that simultaneously access a server. For example, a file server may use a
307
synchronization proxy to insure that two clients don't attempt to write to the same file at the
same time.
High-volume servers run on multiple machines called server farms. A load balancing proxy is
a server-side proxy that keeps track of the load on each server in a farm and delegates client
requests to the least busy server.
Counting proxies are server-side proxies that maintain usage statistics such as hit counters.
Remote Proxies and Remote Method Invocation
Communicating with remote objects through sockets is awkward. It is entirely different from
communicating with local objects, where we can simply invoke a member function and wait
for a return value. The socket adds an unwanted layer of indirection, it restricts us to sending
and receiving strings, it exposes the underlying communication protocol, and it requires us to
know the IP address or DNS name of the remote object.
A remote proxy encapsulates the details of communicating with a remote object. This creates
the illusion that no process boundary separates clients and servers. Clients communicate with
servers by invoking the methods of a client-side remote proxy. Servers communicate with
clients by returning values to server-side remote proxies. This is called Remote Method
Invocation or RMI.
<< Refer to other notes on RMI for further discussion>>
Conclusion
We began this chapter with a discussion of threads, locks, and the Master-Slave design pattern.
This was the architecture we subsequently used for our server framework, which we
subsequently specialized into the command server framework.
Our clients and servers communicated through awkward, transceiver-like devices called
sockets. We were able to hide sockets inside remote proxies on both the server side (skeletons)
and the client side (stubs). Remote proxies created the illusion that clients and servers were
communicating using ordinary method call and return mechanisms.
Remote method invocation completes the merger of object-oriented programming with clientserver programming, the two great programming paradigms of the 1990s. By adding a
sophisticated persistence mechanism such as an object-oriented database, the location of an
object (local, remote, or in storage) becomes irrelevant.
308
Documentation and Coding Standards
Consider the following levels of documentation - each level serves a welldefined purpose:

The Code - It should be readable so as not to need internal comments choose appropriate identifiers (don't abbreviate unless you are using a
conventional abbreviation). The code should be self-documenting and easy
to read (be careful of your spacing, etc.)

Internal Comments - Minimize these comments since they distract the
reader from understanding the code. These might be used to clarify
conventional coding tricks or special cases or notes regarding
constraints. DO NOT restate what is obvious in the code. Include
important decisions and hints about future modifications and current
problems.

External Comments - These elaborate on special algorithms and other
issues that aren't appropriate to document at the lower levels mentioned
above

User Manual - This indicates the functional specifications (constraints)
of the system.

Developer's Manual - This provides information necessary for people that
need a more detailed view of the system including the system
architecture.

Advertising - Entice people to use the system
For our purposes, we will focus on the first two levels of documentation
described above - the code and internal comments. Since this documentation
will be used by various people involved in the critical aspects of system
development it will serve as the most valuable communication mechanism. As
such, it should adhere to principles that have been developed over a long
period of time for effective communication. It is not advisable for each
309
programmer to develop his/her own (unproven) styles. The most
straightforward mechanism for describing the style of interest is to simply
view examples of that style used by experts. Therefore, the Java String
class as found in the VisualCafe class hierarchy is used for demonstration.
The following sections of the source code are included to demonstrate
appropriate coding standards.
310
Sample Coding Standards
/*
* @(#)String.java
1.85 98/07/01
*
* Copyright 1995-1998 by Sun Microsystems, Inc.,
* ...
* All rights reserved.
*
* This software is the confidential and proprietary information..
*/
Provide a file prelude - note the use of a copyright notice, use
of *'s (on each line and their indentation), date of modification
package java.lang;
import java.util.Hashtable;
The package name occurs as the first non-commented line; it is
followed by the import(s)
/**
* The <code>String</code> class represents character strings. All
* string literals in Java programs, such as <code>"abc"</code>, are
* implemented as instances of this class.
* <p>
* Strings are constant; their values cannot be changed after they
* are created. String buffers support mutable strings.
* Because String objects are immutable they can be shared. For example:
* <p><blockquote><pre>
*
String str = "abc";
* </pre></blockquote><p>
* is equivalent to:
* <p><blockquote><pre>
*
char data[] = {'a', 'b', 'c'};
*
String str = new String(data);
* </pre></blockquote><p>
* Here are some more examples of how strings can be used:
* <p><blockquote><pre>
*
System.out.println("abc");
*
String cde = "cde";
* </pre></blockquote>
* The Java language provides special support for the string
* concatentation operator ( + ), and for conversion of
* other objects to strings. String concatenation is implemented
* through the <code>StringBuffer</code> class and its
* <code>append</code> method.
* inherited by all classes in Java. For additional information on
* string concatenation and conversion, see Gosling, Joy, and Steele,
* <i>The Java Language Specification</i>.
*
* @author Lee Boynton
* @version 1.85, 07/01/98
* @see
java.lang.Object#toString()
* @since
JDK1.0
*/
A detailed description of the class is provided using JavaDoc,
311
including special cases, constraints, author(s), system version,
references, jdk dependencies; the line lengths are at most 72
chars to facilitating printouts and screen viewing
public final
class String implements java.io.Serializable {
Class names begin with capital letters
/** The value is used for character storage. */
private char value[];
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -6849794470754667710L;
Variables are private. Comments are provided to describe their
usage. Names are chosen to reflect their use - simple and
descriptive; important constraints are mentioned; note that
descriptive identifiers are used - if words are run together then
the second/subsequent words are capitalized;
variable names begin with lower case
/**
* Allocates a new <code>String</code> constructed from a subarray
* @deprecated This method does not properly convert bytes into
*
characters.
* As of JDK 1.1, the preferred way to do this is via the
* <code>String</code> constructors that take a character-encoding
* name or
* that use the platform's default encoding.
*
* @param
ascii
the bytes to be converted to characters.
* @exception StringIndexOutOfBoundsException if the
* <code>offset</code>
*
or <code>count</code> argument is invalid.
* @see
java.lang.String#String(byte[], java.lang.String)
*/
Methods are commented; comments include usage, constraints,
parameters, exceptions, deprecation information, return values,
references; note the alignment of the JavaDoc parts (param, etc.)
and the fields (ascii, etc.)
and associated comments - a block style is used; sentences are
used to describe the functionality
public String(byte ascii[], int hibyte, int offset, int count) {
if (offset < 0) {
throw new StringIndexOutOfBoundsException(offset);
}
Note the careful check of pre-conditions used to assure
correctness
// Note: offset or count might be near -1>>>1.
if (offset > ascii.length - count) {
throw new StringIndexOutOfBoundsException(offset + count);
}
Comments referring to special constraints are provided; all
compound statements should use braces, even if it's optional (even
though the if
statement only has one statement in its body braces are used; this
helps to avoid future errors where you might want to add code to the
body and forget to include the required braces); note that each
312
subpart of a construct is indented; note the opening brace is on the
same line as the construct and the closing brace is aligned with the
construct.
if (hibyte == 0) {
for (int i = count ; i-- > 0 ;) {
value[i] = (char) (ascii[i + offset] & 0xff);
}
} else { ...
}
}
Note the placement of the else on the same line as the closing
brace of the then-clause; the opening brace of the else clause is
on the same line
}
// Private constructor which shares value array for speed.
private String(int offset, int count, char value[]) {
this.value = value;
}
/**
* Tests if two string regions are equal.
* <p>
* If <code>toffset</code> or <code>ooffset</code> is negative, or
*
* @param
ignoreCase
if <code>true</code>, ignore case when
* comparing
*
characters.
* @param
toffset
the starting offset of the subregion in
* this
*
string.
* @return <code>true</code> if the specified subregion of this
* string
*
matches the specified subregion of the string argument;
*
<code>false</code> otherwise. Whether the matching is
*
exact
*
or case insensitive depends on the
*
<code>ignoreCase</code>
*
argument.
*/
Method names begin with lower case
public boolean regionMatches(boolean ignoreCase,
int toffset,
String other, int ooffset, int len) {
char ta[] = value;
// Note: toffset, ooffset, or len might be near -1>>>1.
if ((ooffset < 0) || (toffset < 0) || (toffset > count - len) ||
(ooffset
> other.count - len)) {
return false;
}
else-clauses are not used except when necessary - they add to the
complexity of the program
while (len-- > 0) {
if (c1 == c2)
continue;
if (ignoreCase) {
// If characters don't match but case may be ignored,
// try converting both characters to uppercase.
313
// If the results match, then the comparison scan should
// continue.
char u2 = Character.toUpperCase(c2);
// Unfortunately, conversion to uppercase does not work
// properly
// for the Georgian alphabet, which has strange rules about
// case
// conversion. So we need to make one last check before
// exiting.
if (Character.toLowerCase(u1) == Character.toLowerCase(u2))
continue;
}
return false;
Note the alignment of braces in the while; comments are indented
at the same level as the construct it refers to
}
return true;
}
/**
* Removes white space from both ends of this string.
* <p>
* All characters that have codes less than or equal to
* <code>'\u0020'</code> (the space character) are considered to
* be
* white space.
*
* @return this string, with white space removed from the front and
* end.
*/
public String trim() {
int off = offset;
/* avoid getfield opcode */
char[] val = value;
/* avoid getfield opcode */
return ((st > 0) || (len < count)) ? substring(st, len) : this;
Always use parentheses to avoid mistakes regarding
priority/associatively of operators; use the ?: operator in place
of an if statement to provide more concise code
}
}
314
Summary of Notes Mentioned in the String Class

Provide a file prelude including file name, date of
creation/modification, copyright notice, author(s); commenting *'s should
be indented and aligned; the package name occurs as the first noncommented line; it is followed by the import(s)

A detailed description of the class is provided using JavaDoc, including
special cases, constraints, author(s), system version, references, jdk
dependencies; the line lengths are at most 72 chars to facilitating
printouts and screen viewing

Class names begin with capital letters

Variables are private. Comments are provided to describe their usage.
Names are chosen to reflect their use - simple and descriptive; important
constraints are mentioned; note that descriptive identifiers are used if words are run together then the second/subsequent words are
capitalized; variable names begin with lower case

Methods are commented; comments include usage, constraints, parameters,
exceptions, deprecation information, return values, references; the
JavaDoc fields/subfields are aligned (param, etc.); a block style is
used; associated comments are aligned; sentences are used to describe the
functionality

Pre-condition checks are used in methods to assure correctness

Comments referring to special constraints are provided; all compound
statements should use braces, even if it's optional (even though the if
statement only has one statement in its body braces are used; this helps
to avoid future errors here you might want to add code to the body and
forget to include the required braces); note that each subpart of a
construct is indented; note the opening brace is on the same line as the
construct and the closing brace is aligned with the construct.

Note in an if-statement the placement of the else is on the same line as
the closing brace of the then-clause; the opening brace of the else
clause is on the same line

Method names begin with lower case

else-clauses are not used except when necessary - they add to the
complexity of the program

The alignment of braces in the while is the same as in the if-statement the opening brace is on the same line as the conditional and the closing
brace aligns with the while; comments are indented at the same level as
the construct it refers to

Always use parentheses to avoid mistakes regarding priority/associatively
of operators; use the ?: operator in place of an if statement to provide
315
more concise code

If you find that you must vary from these standards you should:
1. Convince others that your variation is worthwhile and
2. Be consistent to avoid confusion
Miscellaneous Issues
Indentation for constructs not described above:
SWITCH.
switch (expression) {
case n:
 case indented
statement;
 construct indented
break;
case x:
statement;
// Continue to default case
default:
 always add the default case
statement;
break;
}
TRY/CATCH/FINALLY.:
try {
statement;
}
catch (ExceptionClass e) {
statement;
}
finally {
statement;
}
General Comments:
Names:
Packages: Prefix each package with the domain name cedit - e.g. package Cedit.query;
Interfaces: Prefix each interface name with I - e.g. interface Istore
 Methods: Methods for debug-only implementation should begin debug.- e.g. debugDumpToScreen ()
 Getters and setters should begin with "get" / "set" and return the appropriate object type. - e.g. void
setVal(int theVal); int getVal();

Boolean getters should use "is" or "can" as a prefix, such as "isUndoable()" rather than "getUndoable()"
"MAGIC" NUMBERS. Literal ordinal constants embedded within source should almost never be used. Whenever
possible, use constants instead of literal ordinal constants:
int totalDays = 10 * DAYSINWEEK;
//boo, hiss!
static final int kDaysInWeek = 7;
//hooray! hooray!
316
COMPONENT FACTORY NAMES . A component factory is a public class that implements only static methods.
These static methods are "Factory functions" or "component constructors". Factory class names should include
the word "Factory". Factory method names should start with the word "Make." For example,
public class WidgetFactory
{
static Button MakeButton(int aButtonType);
static ListBox MakeListBox();
};
General Programming
1.
Special Comments. Use XXX in a comment to flag something that is bogus but works. Use FIXME to flag
something that is bogus and broken.
LAYOUT OF SOURCE FILES (*.java)
The layout for a class will be broken up into the following main sections: Copyright Notice, File Description;
Package Name, Imports, Constants, Methods, protected and private members. Each section will be prefaced by an
appropriate header block defining the section.
1.
2.
3.
4.
5.
6.
COPYRIGHT NOTICE . This will be the standard "legalese" copyright notice.
/* Copyright Notice =====================================
* This file contains proprietary information of CEDIT.
* Copying or reproduction without prior written approval is prohibited.
* Copyright (c) 1997 ===================================*/
FILE DESCRIPTION . This is a brief description of what the file is, does and represents. It should describe
the overview of how to use the file (or classes therein). JavaDoc compatible commenting style is required.
/* Description
* Appropriate Description.
*/
PACKAGE NAME . Package names should occur on the first non-commented line of the source file and
should following the naming conventions defined in this document.
IMPORTS . Immediately following the package name should be the imported class names.
CONSTANTS . See the naming conventions defined in this document.
METHOD DECLARATIONS . Each method is preceded by a description in JavaDoc format. Public methods
MUST have a standard javadoc comment header. These must be professionally commented. Remember,
you never know what code will eventually be made public outside of the company.
Standard access methods may be grouped without a description (access methods assign or return an object
attribute). Optionally, methods may be grouped within logical groupings. Here are some suggestions:








Factory
Private
Protected
Interfaces
Accessor
Temporal (fickle)
I/O
Debugging
/*================================================
* CONSTRUCTOR/DESTRUCTOR METHODS
* ================================================== */
public CkSomeClass();
public CkSomeClass(CkSomeClass aSourceToCopy);
317
/*================================================== * FACTORY
METHODS (usually static)
*================================================== */
/** brief description */
public CkSomeClass createSomeClass(void){ }
7.
/*================================================ *
ACCESSOR METHODS
*================================================== */
public int getState(void);
public void setState(int aNewValue);
/*================================================= * STANDARD
METHODS
* ================================================== */
//...whatever these might be...
/*===============================================
* DEBUGGING METHODS
* ================================================= */
void DoUnitTest(void);
CLASS DEFINITIONS. This section is the actual implementation of the class. Each method (and private
function) will be prefaced by the standard documentation header. Read the Documentation for Methods and
Functions defined in this document.
318
SCRUM: An Empirically-Based Process for Software Project
Management
References:
1. Agile Software Development with Scrum, Ken Schwaber & Mike Beedle, Prentice-Hall,
2001
2. IEEE Computer, Special Issue on Agile Software Development, June 2003
Invented by Jeff Sutherland. Formalized and commercialized by Sutherland and Ken Schwaber,
1994. It’s an Agile process (http://www.agilemanifesto.org/principles.html):
Principles behind the Agile Manifesto
We follow these principles:
 Our highest priority is to satisfy the customer through early and continuous delivery of
valuable software.
 Welcome changing requirements, even late in development. Agile processes harness
change for the customer's competitive advantage.
 Deliver working software frequently, from a couple of weeks to a couple of months,
with a preference to the shorter timescale.
 Business people and developers must work together daily throughout the project.
 Build projects around motivated individuals.
Give them the environment and support they need, and trust them to get the job done.
 The most efficient and effective method of conveying information to and within a
development team is face-to-face conversation.
 Working software is the primary measure of progress.
 Agile processes promote sustainable development.
The sponsors, developers, and users should be able to maintain a constant pace
indefinitely.
 Continuous attention to technical excellence and good design enhances agility.
 Simplicity--the art of maximizing the amount of work not done--is essential.
 The best architectures, requirements, and designs emerge from self-organizing teams.
 At regular intervals, the team reflects on how to become more effective, then tunes and
adjusts its behavior accordingly
319
Overview
Identify players/stakeholders and related conditions affecting product development:
 Customer

Management

Available technology

Resources available, including financial commitment

Market conditions

Product owner

Scrum master

Scrum team(s)
320
Steps in the Scrum Process
1. Develop vision of product
2. The customer and development team define requirements that will deliver the
highest business value from the vision; define Product Backlog
3. Form Scrum team(s) (5-8 people)
4. Hold Sprint Planning Meeting
Develop a prioritized Sprint Backlog of tasks/issues from Product
Backlog
5. Start the Sprint (30 days)
Hold Daily Scrum Meetings
Scrum Team self-organizes, as necessary to accomplish goal
Adjust/maintain Sprint Backlog, Burndown Chart (visual depiction of
Sprint progress)
6. Conduct Sprint Review
7. Repeat steps 4. through 6., as long as necessary
The Scrum process is ideally suited for projects with short term to market, rapidly changing
requirements – e.g. Web projects or product development for new markets. Product
requirements evolve; Scrum is not prescriptive. It can react quickly to requirements changes.
The process is empirical; it can accommodate for changes to requirements, as well as changes
gleaned by players/stakeholders over time based on their dynamic experiences. The short
feedback loop (relatively short “inspect and adapt” Sprint cycle) is essential to this empirical
process.
321
Scrum is an iterative and incremental process: each Sprint produces a new increment of the
product, Scrum iterates over Sprints
Business Conditions &
Requirements
Standards, Conventions,
Guidelines
Product Backlog: a prioritized
list of all work to be completed
prior to releasing a product
Sprint Planning Meeting
Sprint Backlog
Scrum Master/
Scrum Team
Daily
Scrum
Sprint 30 days
Sprint Review Executable Product
Increment
SCRUM PROCESS
322
Scrum Summary
Similar to Rugby – both are:
 Adaptive
 Quick
 Self-organizing
 Have few rests
Scrum promotes transparency
 Each Scrum Team member reports his or her work to the team on a daily basis
 Management can attend Daily Scrum meetings to observe status reports
Scrums
Since each participant is fully informed of work of others it’s possible to help other developers
(e.g., developer A can ask developer B to make a slight mod that will save much time for A)
323
Scrum Components and Processes– Definitions and Functionality
Product Backlog
Business Conditions &
Requirements
SCRUM PROCESS: Product
Backlog
Standards, Conventions,
Guidelines
Product Backlog: a prioritized
list of all work to be completed
prior to releasing a product
Sprint Planning Meeting
Sprint Backlog
Scrum Master/
Scrum Team
Daily
Scrum
Sprint 30 days
Sprint Review Executable Product
Increment
324
Product Backlog: A list of all desired work on the project

Story-based work (“let user search and replace”)

Task-based work (“improve exception handling”)

Tasks are grouped into increments that should take no more than 30 days

Lists all outstanding work to be done
Contains everything needed or good ideas in product
List of:

Features

Functions

Technologies

Enhancements

Bug fixes for future releases
325
Example:

Allow users to access and view account balances for last six months

Improve scalability of product

Simplify installation process when multiple databases are used

Determine how workflow can be added to product
Initially only include requirements for 30 day Sprint (e.g. concepts, wish list)
It is dynamic – requirements evolve.
Product Marketing
determines features/functions
Sales includes customer
features
Product
Backlog
Engineering includes
relevant technology
Customer Support (fix
major bugs)
326
Deliverables
Prioritize Product Backlog

Address top priority items first

Better-defined items have higher priority, more value
Include issues – e.g., fast response time
Product Owner turns issues into work for Scrum Team
Grows with time as clarity of product emerges; requirements change
First Scrum => sort out architecture; the deliverable is the architecture
327
Product Owner
Only person to manage/control the Product Backlog (makes visible to all)
This person might be a product manager; officially responsible for the product – meets with
customer and management to make the decisions on requirements and functionality.
She/he is the only person that can tell Scrum Teams what to do via the Product Backlog – e.g.
the product’s requirements and functionality.
Her/his decisions are reflected in the Product Backlog.
Estimation of the Product Backlog Effort

Product Owner talks to developers, tech writers, quality control staff, etc.

Time includes: architecture, design, build, and test

Time is not very accurate until the team gains experience

Estimate is the best guess (not binding on team)

Scrum Team selects amount of Product Backlog to handle in a Sprint based on these
estimates

Starts estimates with top priority items (best understood)

As development occurs, estimates are modified
328
Sprint Planning Meeting
Business Conditions &
Requirements
SCRUM PROCESS: Sprint Planning
Meeting, Sprint Backlog
Standards, Conventions,
Guidelines
Product Backlog: a prioritized
list of all work to be completed
prior to releasing a product
Sprint Planning Meeting
Sprint Backlog
Scrum Master/
Scrum Team
Daily
Scrum
Sprint 30 days
Sprint Review Executable Product
Increment
329
Scrum Teams meet with Scrum Master and others to plan each Sprint. There are 2 consecutive
meetings:
1. Team meets with Product Owner, management and users to determine functionality to
build during the next Sprint
2. Team meets to figure how to build functionality into a product increment during Sprint;
the input provided is from Product Backlog, latest increment of the product,
capabilities/performance of the team
Product
Backlog
Team
Capabilities
Business
Conditions
Review,
Consider &
Organize
Next Sprint
goal
Technology
Stability
Executable
Product
Increment
(Figure 3.1 from Scrum text)
330
Identify Sprint Backlog, Goal for next Sprint
Product Owner presents top priority Product Backlog; leads discussion about appropriate
changes to the Product Backlog, given demo of deliverables at end of prior Sprint
The Sprint Backlog list is built. This is a list of all the tasks/activities needed to develop the
Product Backlog selected for this Sprint.
Top Priority Product Backlog:
Implement middleware alternative providing secure access to legacy database – should
develop details such as Implement Object Oriented middleware between applications and
legacy databases using Tuxedo from BEA and CORBA wrappers
Sprint Goal: to provide a standardized middleware mechanism for the identified customer
service transactions to access backend databases (this is less precise, provides “wiggle room”
regarding functionality)
Sprint Review: management, customers and Product Owner review how implemented
functionality meets projected goal. If there are problems then modify requirements or team
composition, etc.
331
Define Sprint Backlog to meet Sprint Goal
1. Establish goal
2. Team determines what work is needed based on Product Backlog
All team members must be present; may invite others for advice (e.g., domain experts, Product
Owner)

Team self-organizes here.

Management does not affect meeting

Team compiles list of tasks with details for development
Tasks are detailed (4-16 hours to complete each task)
These tasks will form the Sprint Backlog
Team assigns tasks to members
Define initial investigation, design, and architecture
e.g.
1. Map transaction elements to backend database tables
2. Write business objects in Java to handle transaction and interfaces
3. ...
Sprint Backlog is modified dynamically as tasks and time estimates are clarified
If the team discovers it selected too much for the goal, the Scrum Master meets with Product
Owner and team to remove items and still meet goal; or, remove functionality
332
Changes are made to Sprint Backlog during Sprint
333
Scrum Master/Scrum Team
Business Conditions &
Requirements
SCRUM PROCESS: Scrum
Master; Scrum Team
Standards, Conventions,
Guidelines
Product Backlog: a prioritized
list of all work to be completed
prior to releasing a product
Sprint Planning Meeting
Sprint Backlog
Scrum Master/
Scrum Team
Daily
Scrum
Sprint 30 days
Sprint Review Executable Product
Increment
334
Scrum Master
Scrum Master is a new management role; oversees Scrum operation: typically a Project
Manager or Team Leader
Responsibilities:

Represents management

Represents team members to each other

Checks progress vs. Sprint goals and predictions during prior Daily Scrum
e.g., if someone spends too much time on a task the Scrum Master will try
to find help

Checks team progress and recognizes problems; provides help

Works with customer and management to identify/institute Product Owner (“owns” and
is responsible for product delivery; decides order in which components are built).

Works with management to form Scrum Teams

Works with Product Owner and Scrum Teams to create Sprint Backlog for a Sprint

Conducts all Daily Scrums - identifies and removes impediments
Scrum Master is normally a team leader, project leader or project manager. Initially, if there are
significant impediments the Scrum Master should be a Senior Manager or outside Scrum
consultant
Should make immediate decisions in Daily Scrum meetings, even in the context of incomplete
information – must keep the team moving. Decisions can always be modified later.
335
Scrum Team
Scrum Master meets with team to review Product Backlog and establish Sprint Goal
Team commits to the work
Team is self-organizing to draw on strengths of members; if a member has problems then focus
shouldn’t be on problems but should be on strengths
Team sorts out problems, not Scrum Master
Size:
5 – 8; complexity is maintained by limiting the size of the team
If there are slightly more than 8 people then break into smaller teams.
Scrum Master picks a team to select from Product Backlog. Then other teams select, etc.
(teams select from Product Backlog based on members’ strengths)
If there are 2 or more teams then conduct Daily Scrums for each team and then daily Scrum of
Scrums (Scrum Masters from each team meet after the Daily Scrum for their own Daily Scrum)
336
Team Composition:

Based on people needed to meet the Scrum Goal

Include analysts, designers, QA, coders

Very experienced engineer should mentor junior engineers

If there is no QA person available then each developer should do their own testing.

Team members commit based on time available; e.g., consultants or part-time people
have less time

Team members DO NOT have titles; no job descriptions; everyone contributes

Scrum Master or Project Manager can change team at the end of a Sprint (e.g. Domain
experts might be added, under performers might be moved out)
337
Team Responsibility/Authority:

Must meet Sprint Goal

The amount of Product Backlog to address is up to the team

Members must make impediments known and request their removal

They must conform to organizational responsibilities (e.g., coding standards,
architectures)

Team can cancel a Sprint if they cannot meet the Sprint Goals. THIS IS SERIOUS!
Working Environment:

Adequate resources must be provided (e.g., workstations, monitors)

Adequate technology must be provided – Scrum Master checks on this and remedies, if
necessary, to ensure the Sprint Goal is met

Facilitate communication between team members (e.g., put their cubicles nearby,
supply conference rooms with whiteboards for brainstorming, etc.)

Team picks (reasonable) work hours
338
Business Conditions &
Requirements
Standards, Conventions,
Guidelines
Product Backlog: a prioritized
list of all work to be completed
prior to releasing a product
Sprint Planning Meeting
Sprint Backlog
Scrum Master/
Scrum Team
Daily
Scrum
Sprint 30 days
Sprint Review Executable Product
Increment
SCRUM PROCESS: Daily Scrum
339
Daily Scrum Meetings

Held each day

Lasts 15 minutes

Status reporting, not discussions
Presentations by each Scrum Team member:
1. Accomplishments since last meeting (24 hours ago)
2. What member will do before next meeting
3. Obstacles encountered
340
Meeting operation, results and benefits:

Familiarizes team members with team-based, rapid, intense, cooperative development

Improves communication, project knowledge

Scrum Master conducts Daily Scrum – ensures people speak briefly.

Managers can attend AS OBSERVERS, NOT PARTICIPANTS; the meetings provide a
mechanism for managers to understand progress, learn of results and impediments; but
can’t interfere in any way; can learn of under-performing workers

Use rooms with whiteboards e.g. SCI 254

Use same room, same time each day

Room should be easily accessible from team’s workplace

If team members cannot physically be present set up a conference call with
speakerphone

Fixed time, fixed location
341
Starting Meeting

Scrum Master oversees,

Scrum Master takes care for everything to be set up (e.g., chairs, tables) to ensure
everything can be done in 15 minutes.

Team sits around a round table, observers (managers, etc.) may sit further out from
table, not within the Scrum Team seats

Everyone arrives on time! Meeting starts on time. Late arrivals are penalized (e.g. must
pay $1 for a charity fund)
Format

Only 1 person talks at a time, no side conversations

Scrum Master picks people, in order, seated around the table to speak on the 3
presentation items (see beginning of this section).

Stay focused on commitments

Distractions are impediments

Members speak briefly and to the point; don’t discuss, e.g., design issues
342
Identifying Impediments

Scrum Master records discussion items at the Daily Scrums and removes impediments
– writes notes on a whiteboard – e.g., pc problems, slow network, management asking
members to attend other meetings, member does not understand technology

Members can provide advise to Scrum Master for improvements

Address stated impediments at the next Daily Scrum

If there are too many impediments, perhaps the company isn’t providing necessary
support – cancel Sprint?
Making Decisions

Team has full authority to make decisions to support Sprint Goal – e.g., bring in
consultants, new technologies, books, etc. (within budgetary constraints); Scrum Master
shouldn’t make too many decisions

Make decisions quickly so as not to impede work – bad decisions can be corrected at
end of Sprint

If Scrum Master must make a decision then do so at the Daily Scrum else within 1 hour
afterwards
Establishing Follow-up Meetings

If additional discussions (brainstorming, design issues) are necessary then the follow-up
meeting should occur right after the Daily Scrum
343
Sprint

Lasts 30 days

Team self-organizes and improves delivery over time

Team, alone, determines its destiny

Nobody outside can change scope/nature of work

Management’s risk is 30 days; Daily Scrum provides continuous feedback

Team can hold meetings or ? It controls its working hours, etc. (within reason)

Maintain Sprint Burndown Chart to better understand progress (diagram completed
over time depicting remaining work in a Sprint)
The Sprint Burndown Chart is one of the artifacts produced by instrumenting the Scrum
process to better understand the variables such as remaining work, state of the product
development, cost, functionality, etc.
Processes, such as testing, inspections, and requirements analysis, may be used within a Scrum
project. The difference between the Scrum process and other standard software engineering
processes (e.g., Waterfall development method) however, is that the use of these processes is
determined empirically by need, not prescriptively … if the team feels that the aforementioned
processes are needed, they are used; otherwise, they are not used.
344
If the organization is transitioning from a plan-based development method, such as the
Waterfall development method, to an Agile-based method such as Scrum then it might help to
consider the following Sprint types:

Prototyping

Requirements capture

Analysis and design

Implementation

Stabilization
345
346
Sprint Review
Business Conditions &
Requirements
Standards, Conventions,
Guidelines
Product Backlog: a prioritized
list of all work to be completed
prior to releasing a product
Sprint Planning Meeting
Sprint Backlog
Scrum Master/
Scrum Team
Daily
Scrum
Sprint 30 days
Sprint Review Executable Product
Increment
SCRUM PROCESS: Sprint
Review
347

4 hour informational meeting; working meeting – questions, observations, suggestions,
etc.

Team presents to management, customers, users and Product Owner the product
increment built during the Sprint

Team mentions successes and failures

Management observes progress with given resources

Customers provide feedback on deliverables

Product Owner checks on built functionality

Other engineers learn more about team
Preparation for the Sprint Review

Scrum Master conducts meeting; meets with team to establish agenda and presentation
and who presents what

Team organizes presentation – discuss architecture, design, functionality, etc.

Compare results with Sprint Goal, Product Backlog and reasons for non-delivery

Team member reviews simple architecture diagram; highlights previously completed
technology/functions and then adds new functionality to diagram with demos

No extensive preparations by team; no PowerPoint slides, etc. Team should spend less
than 2 hours to prepare
Scrum Teams meet with Scrum Master and others to plan each Sprint. There are 2 consecutive
meetings:
348
3. Team meets with Product Owner, management and users to determine functionality to
build during the next Sprint
4. Team meets to figure how to build functionality into a product increment during Sprint;
the input provided is from Product Backlog, latest increment of the product,
capabilities/performance of the team
Product
Backlog
Team
Capabilities
Business
Conditions
Review,
Consider &
Organize
Next Sprint
goal
Technology
Stability
Executable
Product
Increment
(Figure 3.1 from Scrum text)
Identify Sprint Backlog, Goal for next Sprint
Product Owner presents top priority Product Backlog; leads discussion about appropriate
changes to the Product Backlog, given demo of deliverables at end of prior Sprint
349
The Sprint Backlog list is built. This is a list of all the tasks/activities needed to develop the
Product Backlog selected for this Sprint.
Top Priority Product Backlog:
Implement middleware alternative providing secure access to legacy database – should
develop details such as Implement Object Oriented middleware between applications and
legacy databases using Tuxedo from BEA and CORBA wrappers
Sprint Goal: to provide a standardized middleware mechanism for the identified customer
service transactions to access backend databases (this is less precise, provides “wiggle room”
regarding functionality)
Sprint Review: management, customers and Product Owner review how implemented
functionality meets projected goal. If there are problems then modify requirements or team
composition, etc.
Define Sprint Backlog to meet Sprint Goal
3. Establish goal
4. Team determines what work is needed based on Product Backlog
All team members must be present; may invite others for advice (e.g., domain experts, Product
Owner)

Team self-organizes here.

Management does not affect meeting

Team compiles list of tasks with details for development
Tasks are detailed (4-16 hours to complete each task)
These tasks will form the Sprint Backlog
350
Team assigns tasks to members
Define initial investigation, design, and architecture
e.g.
4. Map transaction elements to backend database tables
5. Write business objects in Java to handle transaction and interfaces
6. ...
Sprint Backlog is modified dynamically as tasks and time estimates are clarified
If the team discovers it selected too much for the goal, the Scrum Master meets with Product
Owner and team to remove items and still meet goal; or, remove functionality
351
Scalability

Scrum has been used in groups of more than 600 people.

Form many Scrum Teams and form a hierarchy of Scrum Teams (Scrum of Scrums,
etc.)

Every part of organization is made up of teams (Scrums), including management teams

Developer scrums meet daily

Scrum of Scrums (team leaders from each Scrum in a product line) meet weekly

Management Scrum meets monthly
Individual teams have their daily Scrum meeting, where core members provide updates and
members from other teams can sit in to hear the progress and obstacles as they arise.
Depending on the degree of interdependency among the teams, an additional Scrum meeting by
designated members from each team can provide a fast and structured forum to monitor
progress on cross-team requirements and issues.
During the design and system architecture phase of Scrum, the designers and architects divide
the project into packets. Packets are assigned to teams based on priority and scheduling
constraints. Based on the degree of coupling between packets, groups of up to six teams are
organized into a management control cluster. This continues upwards until a top-level cluster
has been created.
352
Management Issues

Involved on a daily basis (Daily Scrum)

Scrum master identifies impediments – some may be quite surprising to management
(management might have spent time setting up a reasonable environment but might
have missed the mark)

Scrum Team operations are transparent to management via the Daily Scrums

Risk is addressed – management can input concerns on a 30-day period (Sprint Review)
If a company was recently involved in a merger then there might be added issues for
consideration such as organizational politics and culture. These issues are also relevant when
the Scrum teams are not co-located, such as when there is joint development with global
partners.
353
Scrum Notes for 668/868
We will hold Daily Scrums, etc. during class to ensure all are present (these are really weekly
Scrums)
Dr. Levine will serve as Scrum Master.
Sprints will be at most 3 weeks
Scrum Teams must develop estimates (be careful) and Burndown Charts
Product Owner, Customer selected from outside the Scrum Team – classmates participating in
other projects.
Use the following samples to produce your own artifacts
354
Sprint Burndown Chart
Today’s Date: November 3, 2003
Sprint Review Date: November 11, 2003
Task
Install Oracle
Set up CVS
Set up Apache on
home PC
Set up Tomcat on
home PC
Expected Duration
(days)
3
%Complete Comments
2
4
50
20
3
80
100
Unicorn
Problems
Works great
PC problems
mod_jk
problems
Person
Responsible
Jane
Jack
Sharon
Jim
Use Excel to produce the chart above and the chart below. The chart below was produced from
the following data in Excel:
task 1
task 2
task 3
Date
Days Remaining
1/1/2003
1/5/2003
1/20/2003
12
7
4
You can produce the Excel chart by following the steps:
0. Select Data to chart
1. Select the Chart Wizard (tool bar)
choose XY (Scatter) - scatter with data points connected by lines
2. Step 2 of wizard: Next
3. Step 3 of wizard:
Titles tab
Chart: Sprint Burndown Chart (Sprint Review 1/15/2003)
X axis: Date
Y axis: Estimated Days Remaining
Data labels tab: "show value"
4. Finish
Data points should be taken/plotted just prior to each weekly Scrum meeting.
355
Sprint Burndown Chart (Sprint Review 1/20/2003)
14
Estimated Days Remaining
12
12
10
8
7
6
4
4
2
0
12/30/2002
1/4/2003
1/9/2003
1/14/2003
Date
Sprint Progress Report
Jane Doe - November 10, 2003
Progress Since Last Scrum:
 Set up CVS
 Installed Oracle
 ...
Tasks for the Next Week:
 Install CourseWork
 Port Database
 ...
Impediments
 Exams in other courses
 Unicorn problems
 ...
356
1/19/2003
1/24/2003
Group 1: Jim Marx, Anita Wall, Todd Lincoln, Sophia Rand, Tim Nunn
Product Backlog - may include any workproducts of value to the project
Product Owner prioritizes
Item
number
Requirement
1 Log credit payments to AR
2 process sale-simple cash scenario
3 slow credit payment approval
4 sales commission calculation
5 lay-away plan payments
6 PDA sale capture
7 process sale-credit pmt scenario
Category
feature
use case
issue
defect
enhance
technology
use case
357
Estimated Development
Status
Priority Time (hours)
underway
5
2
underway
5
60
not started
4
10
complete
4
2
not started
3
20
not started
1
100
underway
5
30
Weekly Deliverables (due each Friday)
Team Lead Posts the following (check ilearn for sample forms):
 Product Backlog (Product Backlog.xls)
 Tasklist spreadsheet (TaskList868.xls)
 Sprint Backlog/Burndown Chart (Sprint Backlog.xls - updated versions should be
posted each week)
Be sure to create 3 discussion topics, one for each of the above titles for threaded discussions
for each series of reports. For example, there should be a discussion topic Product Backlog.
Each item in this thread will be an update of the previous backlog so it’s easy to see the
succession of backlogs
Each team member submits the following:
 email me the weekly status report – see ilearn; be sure to include the following in the
email subject line: last name, first name, group number, status report (e.g., Doe, Jane
group 2 status report)
 When work starts on the project create a discussion topic in your group’s discussion
area of ilearn titled Sprint Progress Report – Your Name You will submit a report each
week by posting this report in the discussion thread (see below for the form of the
report
Sprint Progress Report
Progress Since Last Scrum:
 Set up CVS
 Installed Oracle
 ...
Tasks for the Next Week:
 Install CourseWork
 Port Database
 ...
Impediments
 Exams in other courses
 Unicorn problems
 ...
358
Squeak Smalltalk Exercise
Define a binary tree class and construct a tree internally via Smalltalk statements, e.g.
node1 _ BinTree new: ‘A’ “build a new tree with node labeled A”.
node2 _ BinTree new: ‘C’.
node3 _ BinTree new: ‘B’.
node1 addLeftKid: node2.
node2 addRightKid node3.
At this point, the tree looks like:
A
C
B
Perform traversals on the tree; print the node labels as they are encountered
Inorder traversal (left root right)
CBA
Preorder traversal (root, left, right) A C B
Postorder traversal (left, right, root) B C A
Note: view a tree as a collection (ordered) of nodes; use first, next to yield nodes in indicated
order; you should have a BinTree class and traversal classes with constructors - e.g. you would
like to do something like:
suppose theTree is the variable referencing the tree just constructed;
traversal _ InorderTraversal new setTree: theTree.
“the statement above performs initialization; it DOES NOT create a new
collection”
nextLabel _ traversal first.
(nextLabel notNil) whileTrue: [ nextLabel print. nextLabel _ traversal next ]
...
DO NOT create a collection, besides the original BinTree; simply keep track where you are
traversing in the tree (for each traversal type) so you can determine which node to yield for the
next request of first/next. DO NOT include a link from each node to its parent.
Hint: For each traversal maintain a stack of nodes which form the path from the last node
yielded to the root of the tree.
Turn in:
1. Test cases
2. Class diagrams
3. Documentation on your traversal algorithms. This documentation should include
pictures depicting the operation of your algorithm. Since you will select different
359
algorithms you will need to ensure I understand your algorithm by providing adequate
documentation. DO NOT describe in words a concept that would be easier to
understand via pictures/diagrams.
Show a series of pictures for each traversal, demonstrating with an interesting case how
the algorithm works. Be sure to provide the invariant associated with the Stack you use
(i.e. how is the stack being used and what must always hold true regarding the stack)
4. Smalltalk code – use Squeak Smalltalk. File out the source code and then print it for
submission.
Teamwork

All group members are expected to contribute to the best of their abilities

Each group member MUST participate in teamwork and group work, attend meetings
and be courteous and responsible

All group members get the same grade UNLESS the instructor is told or discover that
somebody is not contributing fully
360
Project Information
Write a project proposal for the OOP course. The complexity and scope of the project should
be greater than the assignments that you have been doing in the course. Be careful not to take
on too much work.
You must program your project on a computer in a departmental computer laboratory (to be
designated), or be prepared to bring any equipment you use to the lab for the demonstration
upon completion.
You may program the project in Java, C++ or an approved OOP language..
Type your proposal in the form:
1. Brief overview of system
2. System functionality (describe, e.g., a sample session using your system...be as specific
as possible)
3. Expected size of code (number of lines)
4. Expected time to complete
5. Project activities/timeline for completion
Upon completion you will demonstrate your project on a computer in the lab or classroom.
Your demonstration should not last longer than 5 minutes.
The proposals should be accepted by me before <in a few weeks>
The projects should be completed before <announced later>
USE PICTURES...show simple examples, class hierarchy, object interactions using
UML...also, if you are working with GUIs you must show relevant screen-shots..
Use categories within your class definitions to aid in readability:
e.g.
YourClass : SuperClass
<accessing functions>
Public:
getSize(...) Get size of generated picture
...
Private:
361
setScope(...) Set scope of visual items
...
<traversing functions>
...
Project Milestones (Deliverables include mockups, specs, architecture
diagrams as determined by the indicated milestone)
Milestone
1. High Level
Specs/Analysis (use
cases, conceptual
model, threads.)
2. Design
(specification of
patterns, class
diagrams, packages)
3. Specification of GUI
(screen snapshots of
actual GUI)
4. Test of project
5. Project completion
TOTAL
Weight
5
Due Date
---
10
---
5
---
5
75
100
--Last Class Session
Submission for each Milestone should include the current Milestone, project proposal, and all
prior Milestones.
Final Project Deliverables
Upon project completion (the last class session) you will submit the following - be sure to use
the comments provided above as guidelines on documentation:
1. Contributions made by group members
2. Indicate the platform and software used
3. Original project proposal
4. User Guide
5. Design overview
6. External documentation on algorithms, etc., as appropriate
7. Use Cases (these should be updated per my prior comments)
8. Conceptual model (these should be updated per my prior comments); be sure the model
represents all of the concepts included in the project specification – check the nouns
you included in the project specification
9. Relevant sequence diagrams – one sequence diagram per use case
10. Class Diagrams (these should be updated per my prior comments)
362
11. Package diagram/descriptions
12. Javadoc comments
You will not submit the code. However, it should be accessible to me via java.net should I need
to examine it.
Please ask me if you have questions concerning your documentation...I'm interested in
reading/analyzing your documentation RATHER THAN your code.
Comments on your submission
User Guide - as you describe the operation of your system be sure to include screen snapshots
as an aid (professional system tutorials include these to help aid in understanding). As usual,
you may write on the snapshots to help explanations.
YOU MUST integrate these snapshots in the user guide so I can fully understand your
system.
Design overview - provide a detailed design overview; use pictures to help with your
description. You will be downgraded quite a bit if you do not provide necessary pictures.
Include comments on problems in meeting the goals of your proposal. INCLUDE
PICTURES and simple examples to help me understand the thrust of your project (include
pictures to help describe your interesting algorithms). I should be able to understand all
aspects of your project without delving into your code.
Provide written descriptions of threads used by your system; describe the roles played by
each thread
Package diagrams to depict the architecture: vertical layers and horizontal partitions
Class Diagram - provide layered description with labels, as needed. Use a UML tool to produce
these diagrams. Include public (NOT PRIVATE) methods with their types/return values and
important data fields with type information.
Provide a summary showing class associations, an expanded version including all variables
and methods and a class hierarchy; be sure to provide a few diagrams and not one large,
detailed diagram which would not be useful to the reader
Your external documentation should allow me to fully understand:
a) the OOP design principles you used in your project
b) the organization of your project
c) any interesting algorithms
d) descriptions of all of your classes, including class descriptions
(both class diagrams and written descriptions) and design decisions
e) comments on the extensibility/flexibility of your designs, as well as constraints your design
imposes
YOU WILL RECEIVE LITTLE CREDIT FOR PROGRAMS THAT ARE TOO DIFFICULT
TO UNDERSTAND DUE TO POOR DOCUMENTATION
363
Your grade will be based on your external (and internal) documentation, the success of the
application of OOP principles and the scope and complexity of your project.
If you submit all of your work in a binder then be sure to use margins when printing so any
documentation is not hidden.
Flesh out the architecture by describing use-cases for the project (focus on some of the methods
and show the sequence of events that occur once the method is invoked).







identify all of the objects/classes that will be involved in implementing the method
show a time-line of events that occur and the messages passed between the objects
identify the threads to be used; be careful to keep the number of threads under control
since they add to the complexity of the application.
provide a picture of objects and communication paths/mechanisms.
provide a pictorial class diagram depicting all of the major classes and their associations
be sure to use interfaces appropriately.
use packages appropriately
Consider using a layered design. Identify events/listeners, streams, exception hierarchy, data handling,
etc.
364