Application development
4
- the drawtool applications
- guidelines for design
- from specification to implementation
Additional keywords and phrases:
hush framework, interactive editors, law of Demeter,
formal specification in Z, abstract systems
slide: Application development
public interface widget { widget
public String path();
public void eval(String cmd);
public void pack(String s);
public void bind(handler h,String s);
public void bind(String p, handler h,String s);
public void configure(String cmd);
public void geometry(int x, int y);
public void xscroll(widget w);
public void yscroll(widget w);
public widget self(); // to define compound widgets
public void redirect(widget inner);
};
import hush.dv.api.event;
import hush.dv.widgets.canvas;
class draw extends canvas {
boolean dragging;
public draw(String path) {
super(path);
dragging = false;
bind(this);
}
public void press(event ev) {
dragging = true;
}
public void release(event ev) {
dragging = false;
}
public void motion(event ev) {
if (dragging)
circle(ev.x(),ev.y(),2,"-fill black");
}
};
path ::= '.' '.'string path'.'string
import hush.dv.api.*;
import hush.dv.widgets.frame;
public class toolbox extends frame { toolbox
tablet tablet;
public toolbox(widget w, tablet t) {
super(w,"toolbox");
tablet = t;
new toolbutton(this,"draw");
new toolbutton(this,"move");
new toolbutton(this,"box");
new toolbutton(this,"circle");
new toolbutton(this,"arrow");
}
public int operator() {
tablet.mode(_event.arg(1)); // reset tablet mode
return OK;
}
};
import hush.dv.api.*;
import hush.dv.widgets.button;
public class toolbutton extends button { toolbutton
public toolbutton(widget w, String name) {
super(w,name);
text(name);
bind(w,name);
pack("-side top -fill both -expand 1");
}
};
import hush.dv.api.widget;
public class menubar extends hush.dv.widgets.menubar { menubar
public menubar(widget w, tablet t, toolbox b) {
super(w,"bar");
configure("-relief sunken");
new FileMenu(this,t);
new EditMenu(this,b);
new HelpButton(this);
}
};
import hush.dv.api.*;
import hush.dv.widgets.*;
public class tablet extends canvas { tablet
int _mode;
canvas canvas;
handler[] handlers;
final int DRAW = 0;
final int MOVE = 1;
final int CIRCLE = 2;
final int BOX = 3;
final int ARROW = 5;
public tablet(widget w, String name, String options) {
super(w,name,"*");
handlers = new handler[12];
init(options);
redirect(canvas); // to delegate to canvas
bind(this); // to intercept user actions
handlers[DRAW] = new DrawHandler(canvas);
handlers[MOVE] = new MoveHandler(canvas);
handlers[BOX] = new BoxHandler(canvas);
handlers[CIRCLE] = new CircleHandler(canvas);
handlers[ARROW] = new ArrowHandler(canvas);
_mode = 0; // drawmode.draw;
}
public int operator() {
handlers [mode].dispatch(_event);
return OK;
}
public int mode(String s) {
int m = -1;
if ("draw".equals(s)) m = DRAW;
if ("move".equals(s)) m = MOVE;
if ("box".equals(s)) m = BOX;
if ("circle".equals(s)) m = CIRCLE;
if ("arrow".equals(s)) m = ARROW;
if (m >= 0) _mode = m;
return _mode;
}
void init(String options) {
widget root = new frame(path(),"-class tablet");
canvas = new canvas(root,"canvas",options);
canvas.configure("-relief sunken -background white");
canvas.geometry(200,100);
scrollbar scrollx = new Scrollbar(root,"scrollx");
scrollx.orient("horizontal");
scrollx.pack("-side bottom -fill x -expand 0");
scrollbar scrolly = new Scrollbar(root,"scrolly");
scrolly.orient("vertical");
scrolly.pack("-side right -fill y -expand 0");
canvas.pack("-side top -fill both -expand 1");
canvas.xscroll(scrollx); scrollx.xview(canvas);
canvas.yscroll(scrolly); scrolly.yview(canvas);
}
};
import hush.dv.api.*;
import hush.dv.widgets.frame;
import hush.dv.widgets.canvas;
public class drawtool extends canvas { drawtool
widget root;
tablet tablet;
public drawtool() { System.out.println("meta handler created"); }
public drawtool(String p, String options) {
super(p,"*"); // create empty tablet
init(options);
}
public int operator() {
System.out.println("Calling drawtool:" + _event.args(0) );
String[] argv = _event.argv();
if ("self".equals(argv[1])) tk.result(self().path());
else if ("drawtool".equals(argv[0]))
create(argv[1],_event.args(2));
else if ("path".equals(argv[1])) tk.result(path());
else if ("pack".equals(argv[1])) pack(_event.args(2));
else self().eval( _event.args(1) ); // send through
return OK;
}
void create(String name, String options) {
drawtool m = new drawtool(name,options);
}
void init(String options) {
root = new frame(path(),"-class Meta");
frame frame = new frame(root,"frame");
tablet = new tablet(frame,"tablet",options);
toolbox toolbox = new toolbox(frame,tablet);
menubar menubar = new menubar(root,tablet,toolbox);
toolbox.pack("-side left -fill y -expand 0");
tablet.pack("-side left -fill both -expand 1");
menubar.pack();
frame.pack("-expand 1 -fill both");
redirect( tablet ); // the widget of interest
}
};
Canvas c = new DrawTool("draw","");
tk.bind("drawtool",c);
c.circle(20,20,20,"-fill red");
c.rectangle(30,30,70,70,"-fill blue");
c.pack();
Development process -- cognitive factors
- model realize refine
Design criteria -- natural, flexible, reusable
- abstraction -- types
- modularity -- strong cohesion (class)
- structure -- subtyping
- information hiding -- narrow interfaces
- complexity -- weak coupling
slide: Criteria for design
Class design -- guidelines
- only methods public -- information hiding
- do not expose implementation details
- public members available to all classes -- strong cohesion
- as few dependencies as possible -- weak coupling
- explicit information passing
- root class should be abstract model -- abstraction
slide: Individual class design
Invariant properties -- algebraic laws
class employee { employee
public:
employee( int n = 0 ) : sal(n) { }
employee* salary(int n) { sal = n; return this; }
virtual long salary() { return sal; }
protected:
int sal;
};
Invariant
k == (e->salary(k))->salary()
slide: Invariant properties as algebraic laws
Problem -- hidden bonus
class manager : public employee { manager
public:
long salary() { return sal + 1000; }
};
Invariant
k =?= (m->salary(k))->salary()
slide: Violating the invariant
Solution -- explicit bonus
class manager : public employee { manager'
public:
manager* bonus(int n) { sal += n; return this; }
};
Invariant -- restored
k + n == ((m->salary(k))->bonus(n))->salary()
slide: Restoring the invariant
Good Object-Oriented Design
- organize and reduce dependencies between classes
Client
-- A method m is a client of C if m calls a method of C
Supplier
-- If m is a client of C then C is a supplier of m
Acquaintance
-- C is an acquaintance of m if C is a supplier of m but not (the type of) an argument of m or (of) an instance variable
of the object of m
- C is a preferred acquaintance of m if an object of C is created in m or C is the type of a global variable
- C is a preferred supplier of m if C is a supplier and C is (the type of) an instance variable, an argument or a preferred acquaintance
slide: Clients, suppliers and acquaintances
Law of Demeter
ignorance is bliss
Do not refer to a class C in a method m unless C is (the type of)
1. an instance variable
2. an argument of m
3. an object created in m
4. a global variable
- Minimize the number of acquaintances!
Class transformations
- lifting -- make structure of the class invisible
- pushing -- push down responsibility
slide: The Law of Demeter
State and operations
Z
-
-
Change and invariance
-
-
Verification
slide: Model-based specification
Counter in Z">
State
Counter
\begin{schema}{Counter}
n : \nat
\where
n \geq 0
\end{schema}
Operations
\begin{schema}{Incr}
\Delta Counter
\where
\mbox{ }
\end{schema}
\begin{schema}{Decr}
\Delta Counter
\where
n > 0 \\
\mbox{ }
\end{schema}
slide: The specification of a
Counter">
Counter
Z
Bounded counter
slide: An alternative specification of the
State
Library (1)
\begin{schema}{Library}
books : \power Book \\
borrowed : Book \pfun Person
\where
\dom borrowed \subseteq books
\end{schema}
slide: The specification of a library
Operations
Library (2)
\begin{schema}{Borrow}
\Delta Library; b? : Book; p? : Person
\where
b? \not\in \dom borrowed \\
b? \in books \\
borrowed' \mbox{ } borrowed \cup { b? \mapsto p? }
\end{schema}
\begin{schema}{Return}
\Delta Library; b? : Book; p? : Person
\where
b? \in \dom borrowed \\
borrowed' \mbox{ } borrowed \hide { b? \mapsto p? }
\end{schema}
\begin{schema}{Has}
\Xi Library; p? : Person; bks : \power Book
\where
bks! \mbox{ } borrowed ^{-1} \limg { p? } \rimg
\end{schema}
slide: The library operations
Abstract systems -- design methodology
- abstract system = abstract data types + protocol
Events -- high level glue
- realization of the interaction protocol
slide: Abstract systems and events
Abstract system -- exemplary interface
library
p = new person();
b = new book();
p = b->borrower;
s = p->books;
tf = b->inlibrary();
b->borrow(p);
p->allocate(b);
p->deallocate(b);
b->_return(p);
For person* p; book* b; set<book>* s; bool tf;
slide: The library system
class book { book
public:
person* borrower;
book() {}
void borrow( person* p ) { borrower = p; }
void _return( person* p ) { borrower = 0; }
bool inlibrary() { return !borrower; }
};
class person { person
public:
person() { books = new set(); }
void allocate( book* b ) { books->insert(b); }
void deallocate( book* b ) { books->remove(b); }
set* books;
};
book* Stroustrup = new book(); example
book* ChandyMisra = new book();
book* Smalltalk80 = new book();
person* Hans = new person();
person* Cees = new person();
Stroustrup->borrow(Hans);
Hans->allocate(Stroustrup);
ChandyMisra->borrow(Cees);
Cees->allocate(ChandyMisra);
Smalltalk80->borrow(Cees);
Cees->allocate(Smalltalk80);
class Event {
public:
virtual void operator()() = 0;
};
class Borrow : public Event { Borrow
public:
Borrow( person* _p, book* _b ) { _b = b; _p = p; }
void operator()() {
require( _b && _p ); // _b and _p exist
_b->borrow(p);
_p->allocate(b);
}
private:
person* _p; book* _b;
};
class Return : public Event { Return
public:
Return( person* _p, book* _b ) { _b = b; _p = p; }
void operator()() {
require( _b && _p );
_b->_return(p);
_p->deallocate(b);
}
private:
person* _p; book* _b;
};
- Give an example of your choice to describe OO application development.
- Discuss possible guidelines for individual class design.
- Discuss how inheritance may affect class invariants.
- What would be your rendering of the Law of Demeter?
Can you phrase its underlying intuition? Explain.
- Define the notions of client, supplier and acquaintance.
What restrictions must be satisfied to speak of
a preferred acquaintance and a preferred supplier?
- Characterize the differences between semantic
modeling and object-oriented modeling.
- How would you characterize the notion of
abstract systems?
- Explain how events may be employed to
maintain system integrity. Give an example!
The original paper on hush is [HUSH].
A veritable catalogue of object-oriented
applications can be found in [Harmon93].
A classical paper on class design is [JF88].
For the Law of Demeter, consult [LH89].
The notion of abstract systems
was introduced in [Henderson93],
which also gives a good account of a
formal approach to object-oriented design.
For an introduction to formal methods and Z,
consult [Diller94].
For object-oriented extensions of Z, see [Stepney].