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.