Constraint Design
I. Definitions
A. What they are: In this course we are going to be concerned with
spreadsheet-style constraints. In a spreadsheet, a formula can be
attached to a cell that computes the cell's value. The formula can
use the values of other cells as inputs. When any of these cells
change value, the formula is automatically re-evaluated and the cell's
value is updated. In a graphical interface, the cells are properties.
B. How they are specified: Formulas are written as ordinary functions
in whatever language is used as the implementation language
for the graphical interface. The functions must use special
accessor functions to read the values of other properties so that
the constraint solver can determine which properties the formula
depends on. For example, here is a formula function that computes the
right side of a rectangle:
int computeRight (Object owner) {
return owner.getLeft() + owner.getWidth();
}
The getLeft and getWidth methods will notify the constraint solver
that their respective properties are being accessed and the constraint
solver will record that the computeRight function depends on these
two properties. We'll discuss the constraint solver's implementation
in greater detail in a later set of notes.
C. How they are used
1. Specify graphical layout, such as keeping the endpoints of arrows
attached to objects or aligning objects with other objects.
2. Maintain consistency between a model and a view
e.g., the height of a rectangle depends on the amount of
sales of a product
3. Communicate information among widgets
e.g., the enabled property of a menu item depends on whether
an object is selected in a drawing editor
II. Why Use Constraints?
A. Declarative: The programmer specifies the relationship but not how
to maintain the relationship
B. Modularity--Suppose you have a label centered inside a rectangle. When
the rectangle moves, it must send a move message to the label.
1. Now suppose there's a second rectangle, b, that wants to appear 20
pixels to the right of the rectangle. You have to augment
rect's move method to issue a move message to b.
2. Now suppose you delete b. Rect will crash.
3. So you have to maintain a list of objects that rect sends a move
message to. But now b must remember to remove itself
from rect's list. This gets complicated and violates
modularity.
4. If you use constraints, then all of b's constraints disappear when
b is deleted and there is no problem. The constraint solver
does all the work of maintaining the lists.
III. Drawbacks of Constraints
A. Hard to debug--can become like sphagetti when there are a lot of
constraints
B. Many programmers think imperatively rather than declaratively
IV. Constraints are a Mediator Pattern
A. Constraints should already have struck you as resembling an
observer pattern. Properties are the observees and the constraint
solver is the observer
B. Constraints are actually an improvement on the observer pattern.
The observer pattern has two weaknesses:
1. The observer pattern cannot control the order in which changes
propagate through the system. We will show later that this kind
of uncontrolled propagation can cause computations to be
unnecessarily repeated
2. The observer pattern requires the programmer to implement
bookkeeping, such as adding and removing objects from observer
lists.
C. Constraints remedy these two shortcomings
1. The constraint solver evaluates constraints in topographical
order so that each computation is performed at most once.
2. The constraint solver is responsible for removing and adding
constraints to the observer lists of properties.
D. Constraint solvers also provide a finer granularity than the observer
pattern. In the typical observer pattern, every property can
fire a property changed event and observers have to use conditional
code to determine whether they are interested in that particular
property. With constraints, only the formulas that depend on a
changed property are triggered, thus eliminating conditional code.
E. Constraint solvers are an example of the mediator pattern. A
mediator defines an object that encapsulates how a set of objects
interact. In this case, the mediator is the constraint solver and
the set of objects are the formulas. A mediator promotes a loose
coupling among objects by keeping objects from referring to each
other explicitly, and it lets you vary their interaction
independently. In this case, formulas do not explicitly tell other
formulas when they have changed. Instead they tell the constraint
solver, the mediator, and the constraint solver then determines
in which order they will be evaluated. Since formulas do not know
about other formulas in the system, the user can add and delete
them without worrying about their effect on other formulas in
the system.