// CS U213 Spring 2007 // Lecture 6: January 22, 2007 Last time: +----------------------------+ | Circle | +----------------------------+ | CartPt center | | double radius | +----------------------------+ | double area() | | boolean contains(CartPt p) | +----------------------------+ This is an 'extended' class diagram; we list the method headers Now we add a Square: +---------+ | Shape | +---------+ | / \ +---+ | +-----------+-----------+ | | | | +------------------+---------+ +------+---------------------+ | Square | | Circle | +----------------------------+ +----------------------------+ | CartPt nw | | CartPt center | | double size | | double radius | +----------------------------+ +----------------------------+ | double area() | | double area() | | boolean contains(CartPt p) | | boolean contains(CartPt p) | +----------------------------+ +----------------------------+ +------------------------+ | : | | :100 B | | : | | 200 +-------+ | |~~~~~~~~| | | | | E |50 C | | A | | | | +-------+ | | | | D | +------------------------+ // The class Square... // contains: CartPt p -> boolean // - Is a given CartPt inside this square? // Examples Square s = new Square(new CartPt(200,100), 50); CartPt A = new CartPt(100, 130), // -> false B = new CartPt(220, 50), // -> false C = new CartPt(270, 140), // -> false D = new CartPt(230, 170), // -> false E = new CartPt(225, 120), // -> true // Method Template (or 'Inventory') boolean contains(CartPt p){ this.nw -- CartPt this.size -- double this.area() -- double // Because the inventory will be the same for most methods, we // can place it after the contructor definition, adding new // methods as we implement them // These are only used incase of emergency... they are members // of CartPt and a little to specific to use directly // - e.g. if we wanted to change coordinate systems, we would // have to change a bunch of specific 'CartPt' references p.x -- double p.y -- double // Unfortunately we must access p.x and p.y for this method // implementation sketch: // return nw.x < p.x && // p.x < (nw.x + size) && // nw.y < p.y && // p.y < (nw.y + size); // Note that we might get rid of these references (x&y) if // there happened to be a method for CartPt... // // inInterval: is this point between the given points? // boolean inInterval(CartPt p1, CartPt p2) // this would take a little more designing of CartPt } Back to Scheme... ;; shape is one of: ;; -- circle ;; -- square ;; circle is ;; (make-circle posn number) ;; square is ;; (make-square posn number) ;; contains: shape posn -> boolean ;; is the given point within the given shape? (define (contains ashape p) (cond [(circle? ashape) (< (dist (circle-center ashape) p) (circle-radius ashape))] [(square? ashape) ( ... )])) The implementation in the cond corresponds to the Java methods inside the class 'variants' Because Circle/Square each implement 'contains', we can add that to our interface definition (could also add area()) interface Shape{ // is the given point within this shape? boolean contains(CartPt p); } // This is our contract; we can now write tests for Shape Shape s = new Square( ... ); Shape c = new Circle( ... ); In Scheme we decide what happens inside the 'cond,' how does this work with classes? Java knows just what to do - Above, s is of 'type' Square and also of 'type' Shape, but we say that s is an instance-of Square (not of Shape) - Java uses this information to lookup the right method for a given shape, so s.contains(...) works as expected Add a new method to Shape... Shape move(double dx, double dy); First in Scheme ;; move: shape number number -> shape ;; move the given shape by the deltas given (x&y) ;; examples: ;; (move (make-circle (make-posn 100 100) 50) 1 1) --> ;; (make-circle (make-posn 101 101) 50) ;; (move (make-square (make-posn 200 100) 50) 20 30) --> ;; (make-square (make-posn 220 130) 50) (define (move ashape dx dy) (cond [(circle? ashape) ( ... )] [(square? ashape) ( ... )])) ;; Moving the point is a little messy ;; (make-posn (+ (posn-x (circle-center ashape) dx)) ;; (+ (posn-y (circle-center ashape) dy))) ;; So we should make a helper function ;; move-posn: posn number number -> posn ;; move a posn by dx and dy... ;; (define (move-posn p dx dy) ;; (make-posn (+ (posn-x p dx)) ;; (+ (posn-y p dy)))) For Java we need to: - Add the method to our interface (contract/purpose) - Examples!! - Add it to our 'global' templates (Circle and Square) - Implement it for our variants: Circle and Square Main Points/Summary * Scheme 'cond' corresponds to our Java variants - Method header (contract) goes into the common interface - Each clause becomes the implementation of our method - Java does the work of the 'cond' for us!