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.