Lecture outline
1 Designing Methods for Unions of Classes
5.3.5

Lecture: Methods for unions

Design methods for unions of classes of data.
Practice using wish lists. Wish lists.

     IShape.java   

Lecture outline

1 Designing Methods for Unions of Classes

Here are our two classes that represent shapes - we defined them to implement the common interface IShape.

The class diagram below shows our ambitious program - of designing five methods for these classes.

                    +---------------------------------+

                    | IShape                          |

                    +---------------------------------+

                    +---------------------------------+

                    | double area()                   |

                    | boolean distTo0()               |

                    | IShape grow(int inc)            |

                    | boolean biggerThan(IShape that) |

                    | boolean contains(CartPt pt)     |

                    +---------------------------------+

                                   |

                                  / \

                                  ---

                                   |

                 -------------------------------

                 |                             |

  +---------------------------------+          |

  | Circle                          |          |

  +---------------------------------+          |

+-| CartPt center                   |          |

| | int radius                      |          |

| | String color                    |          |

| +---------------------------------+          |

| | double area()                   |          |

| | boolean distTo0()               |          |

| | IShape grow(int inc)            |          |

| | boolean biggerThan(IShape that) |          |

| | boolean contains(CartPt pt)     |          |

| +---------------------------------+          |

|                                              |

|                       +--------------------------------+

|                       | Square                         |

|                       +--------------------------------+

|                     +-| CartPt nw                      |

|                     | | int size                       |

|                     | | String color                   |

|                     | +--------------------------------+

|                     | | double area()                  |

|                     | | boolean distTo0()              |

|                     | | IShape grow(int inc)           |

|                     | | boolean biggerThan(IShape that)|

|                     | | boolean contains(CartPt pt)    |

|                     | +--------------------------------+

|                     |

+----+ +--------------+

     | |

     v v

+--------+

| CartPt |

+--------+

| int x  |

| int y  |

+--------+

We want to design the following mtehods that would work for any shape - the two we have defined now, and any other shape class we may define in the future (for example a Square class.

To compute the area of a shape in DrRacket the function purpose, contract and header would have been:

;; to compute the area of the given shape

;; area : Shape -> Number

(define (area ashape) ...)

and the template would have been:

;; to compute the area of the given shape

;; area : Shape -> Number

(define (area ashape)

  (cond

       [(circle? ashape) ...]

       [(square? ashape) ...]))

In Java the methods that deal with each type of object are defined within the class definition for that class of objects. So the area method that computes the area of a circle is defined in the Circle class and the area method that computes the area of a square is defined in the Square class.

A union of several classes in Java is represented by an interface type. We would like to assure that every class that implements our IShape interface does indeed define the method #area. The interface definition, that until now looked likejust an empty shell of a code, indeed can define method headers for some methods. That defines a contract between the interface and the classes that implement the interface. Every class that implements the interface is required to implement all methods that the interface specifies. In turn, if we use any object of the type specified by the interface, we are guaranteed that a method defined in the interface can be invoked by this object.

So, our code would look like this:

// to represent a geometric shape

interface IShape {

  // to compute the area of this shape

  public double area();

}

 

// to represent a circle

class Circle implements IShape {

  CartPt center;

  int radius;

  String color;

 

  Circle(CartPt center, int radius, String color) {

    this.center = center;

    this.radius = radius;

    this.color = color;

  }

 

  /* TEMPLATE

    FIELDS:

    ... this.center ...              -- CartPt

    ... this.radius ...              -- int

    ... this.color ...               -- String

    METHODS

    ... this.area() ...                  -- double

  */

 

  // to compute the area of this shape

  public double area() {

    return Math.PI * this.radius * this.radius;

  }

}

 

// to represent a square

class Square implements IShape {

  CartPt nw;

  int size;

  String color;

 

  Square(CartPt nw, int size, String color) {

    this.nw = nw;

    this.size = size;

    this.color = color;

  }

 

  /* TEMPLATE

    FIELDS:

    ... this.nw ...              -- CartPt

    ... this.size ...            -- int

    ... this.color ...           -- String

    METHODS:

    ... this.area() ...                  -- double

  */

 

  // to compute the area of this shape

  public double area() {

    return this.size * this.size;

  }

}

 

class ExamplesShapes {

  ExamplesShapes() {}

 

  IShape c1 = new Circle(new CartPt(50, 50), 10, "red");

  IShape s1 = new Square(new CartPt(50, 50), 30, "red");

 

  // test the method area in the classes that implement IShape

  boolean testIShapeArea(Tester t) {

    return

    t.checkInexact(this.c1.area(), 314.15, 0.01) &&

    t.checkInexact(this.s1.area(), 900.0, 0.01);

  }

}

A challenge:

See if you would be able to define the following methods for the classes Circle and Square:

// to represent a geometric shape

interface IShape {

  // to compute the area of this shape

  public double area();

 

  // to compute the distance form this shape to the origin

  public double distTo0();

 

  // to increase the size of this shape by the given increment

  public IShape grow(int inc);

 

  // is the area of this shape is bigger than the area of the given shape?

  public boolean biggerThan(IShape that);

 

  // does this shape (including the boundary) contain the given point?

  public boolean contains(CartPt pt);

}

Well, here is the solution. Study if carefully, especially the places where we decided to use helper methods in the class CartPt.

Also look carefully at the way we extend the basic template when the method argument is an instance of the class in which the method is defined — in that case we have access to all fields of the argument object and the methods these fields can invoke.

// to represent a circle

class Circle implements IShape {

  CartPt center;

  int radius;

  String color;

 

  Circle(CartPt center, int radius, String color) {

    this.center = center;

    this.radius = radius;

    this.color = color;

  }

 

  /* TEMPLATE

    FIELDS

    ... this.center ...              -- CartPt

    ... this.radius ...              -- int

    ... this.color ...               -- String

    METHODS

    ... this.area() ...                  -- double

    ... this.distTo0() ...               -- double

    ... this.grow(int inc) ...           -- IShape

    ... this.biggerThan(IShape that) ... -- boolean

    ... this.contains(CartPt pt) ...     -- boolean

    METHODS FOR FIELDS:

    ... this.center.distTo0() ...        -- double

    ... this.center.distTo(CartPt) ...   -- double

  */

 

  // to compute the area of this shape

  public double area(){

    return Math.PI * this.radius * this.radius;

  }

 

  // to compute the distance form this shape to the origin

  public double distTo0(){

    return this.center.distTo0() - this.radius;

  }

 

  // to increase the size of this shape by the given increment

  public IShape grow(int inc){

    return new Circle(this.center, this.radius + inc, this.color);

  }

 

  // is the area of this shape is bigger than the area of the given shape?

  public boolean biggerThan(IShape that){

  /*---------------------------------------------------

  // TEMPLATE for this method:

    ... this.center ...              -- CartPt

    ... this.radius ...              -- int

    ... this.color ...               -- String

 

    ... this.area() ...                  -- double

    ... this.distTo0() ...               -- double

    ... this.grow(int inc) ...           -- IShape

 

    ... that.center ...              -- CartPt

    ... that.radius ...              -- int

    ... that.color ...               -- String

 

    ... that.area() ...                  -- double

    ... that.distTo0() ...               -- double

    ... that.grow(int inc) ...           -- IShape

  ---------------------------------------------------*/

    return this.area() >= that.area();

  }

 

  // does this shape (including the boundary) contain the given point?

  public boolean contains(CartPt pt){

  /*---------------------------------------------------

  // TEMPLATE for this method:

  public returnType methodName()

    ... this.center ...              -- CartPt

    ... this.radius ...              -- int

    ... this.color ...               -- String

 

    ... this.area() ...                  -- double

    ... this.distTo0() ...               -- double

    ... this.grow(int inc) ...           -- IShape

 

    ... this.center.distTo0() ...           -- double

    ... this.center.distTo(CartPt x) ...    -- double

 

    ... pt.distTo0() ...                    -- double

    ... pt.distTo(CartPt x) ...             -- double

  ---------------------------------------------------*/

    return this.center.distTo(pt) <= this.radius;

  }

}

// to represent a square

class Square implements IShape {

  CartPt nw;

  int size;

  String color;

 

  Square(CartPt nw, int size, String color) {

    this.nw = nw;

    this.size = size;

    this.color = color;

  }

 

    /* TEMPLATE

    FIELDS

    ... this.nw ...              -- CartPt

    ... this.size ...            -- int

    ... this.color ...           -- String

    METHODS

    ... this.area() ...                  -- double

    ... this.distTo0() ...               -- double

    ... this.grow(int inc) ...           -- IShape

    ... this.biggerThan(IShape that) ... -- boolean

    ... this.contains(CartPt pt) ...     -- boolean

    METHODS FOR FIELDS:

    ... this.nw.distTo0() ...        -- double

    ... this.nw.distTo(CartPt) ...   -- double

  */

 

  // to compute the area of this shape

  public double area(){

    return this.size * this.size;

  }

 

  // to compute the distance form this shape to the origin

  public double distTo0(){

    return this.nw.distTo0();

  }

 

  // to increase the size of this shape by the given increment

  public IShape grow(int inc){

    return new Square(this.nw, this.size + inc, this.color);

  }

 

  // is the area of this shape is bigger than the area of the given shape?

  public boolean biggerThan(IShape that){

  /*---------------------------------------------------

  // TEMPLATE for this method:

    ... this.nw ...                  -- CartPt

    ... this.size ...                -- int

    ... this.color ...               -- String

 

    ... this.area() ...                  -- double

    ... this.distTo0() ...               -- double

    ... this.grow(int inc) ...           -- IShape

 

    ... that.nw ...                  -- CartPt

    ... that.size ...                -- int

    ... that.color ...               -- String

 

    ... that.area() ...                  -- double

    ... that.distTo0() ...               -- double

    ... that.grow(int inc) ...           -- IShape

  ---------------------------------------------------*/

    return this.area() >= that.area();

  }

 

  // does this shape (including the boundary) contain the given point?

  public boolean contains(CartPt pt){

  /*---------------------------------------------------

  // TEMPLATE for this method:

  public returnType methodName()

    ... this.nw ...                  -- CartPt

    ... this.size ...                -- int

    ... this.color ...               -- String

 

    ... this.area() ...                  -- double

    ... this.distTo0() ...               -- double

    ... this.grow(int inc) ...           -- IShape

 

    ... this.nw.distTo0() ...            -- double

    ... this.nw.distTo(CartPt x) ...     -- double

 

    ... pt.distTo0() ...                    -- double

    ... pt.distTo(CartPt x) ...             -- double

  ---------------------------------------------------*/

    return (this.nw.x <= pt.x) && (pt.x <= this.nw.x + this.size) &&

           (this.nw.y <= pt.y) && (pt.y <= this.nw.y + this.size);

  }

}

/*

+--------+

| CartPt |

+--------+

| int x  |

| int y  |

+--------+

*/

 

// to represent a Cartesian point

class CartPt {

  int x;

  int y;

 

  CartPt(int x, int y) {

    this.x = x;

    this.y = y;

  }

 

  /* TEMPLATE

     FIELDS:

    ... this.x ...            -- int

    ... this.y ...            -- int

    METHODS:

    ... this.distTo0() ...         -- double

    ... this.distTo0(CartPt) ...   -- double

  */

 

  // to compute the distance form this point to the origin

  public double distTo0(){

    return Math.sqrt(this.x * this.x + this.y * this.y);

  }

 

  // to compute the distance form this point to the given point

  public double distTo(CartPt pt){

    return Math.sqrt((this.x - pt.x) * (this.x - pt.x) +

                     (this.y - pt.y) * (this.y - pt.y));

  }

}

class ExamplesShapes {

  ExamplesShapes() {}

 

  CartPt pt1 = new CartPt(0, 0);

  CartPt pt2 = new CartPt(3, 4);

  CartPt pt3 = new CartPt(7, 1);

 

  IShape c1 = new Circle(new CartPt(50, 50), 10, "red");

  IShape c2 = new Circle(new CartPt(50, 50), 30, "red");

  IShape c3 = new Circle(new CartPt(30, 100), 30, "blue");

 

  IShape s1 = new Square(new CartPt(50, 50), 30, "red");

  IShape s2 = new Square(new CartPt(50, 50), 50, "red");

  IShape s3 = new Square(new CartPt(20, 40), 10, "green");

 

  // test the method distTo0 in the class CartPt

  boolean testDistTo0(Tester t) {

    return

    t.checkInexact(this.pt1.distTo0(), 0.0, 0.001) &&

    t.checkInexact(this.pt2.distTo0(), 5.0, 0.001);

  }

 

  // test the method distTo in the class CartPt

  boolean testDistTo(Tester t) {

    return

    t.checkInexact(this.pt1.distTo(this.pt2), 5.0, 0.001) &&

    t.checkInexact(this.pt2.distTo(this.pt3), 5.0, 0.001);

  }

 

  // test the method area in the class Circle

  boolean testCircleArea(Tester t) {

    return

    t.checkInexact(this.c1.area(), 314.15, 0.01);

  }

 

  // test the method area in the class Square

  boolean testSquareArea(Tester t) {

    return

    t.checkInexact(this.s1.area(), 900.0, 0.01);

  }

 

  // test the method distTo0 in the class Circle

  boolean testCircleDistTo0(Tester t) {

    return

    t.checkInexact(this.c1.distTo0(), 60.71, 0.01) &&

    t.checkInexact(this.c3.distTo0(), 74.40, 0.01);

  }

 

  // test the method distTo0 in the class Square

  boolean testSquareDistTo0(Tester t) {

    return

    t.checkInexact(this.s1.distTo0(), 70.71, 0.01) &&

    t.checkInexact(this.s3.distTo0(), 44.72, 0.01);

  }

 

  // test the method grow in the class Circle

  boolean testCircleGrow(Tester t) {

    return

    t.checkExpect(this.c1.grow(20), this.c2);

  }

 

  // test the method grow in the class Square

  boolean testSquareGrow(Tester t) {

    return

    t.checkExpect(this.s1.grow(20), this.s2);

  }

 

  // test the method biggerThan in the class Circle

  boolean testCircleBiggerThan(Tester t) {

    return

    t.checkExpect(this.c1.biggerThan(this.c2), false) &&

    t.checkExpect(this.c2.biggerThan(this.c1), true) &&

    t.checkExpect(this.c1.biggerThan(this.s1), false) &&

    t.checkExpect(this.c1.biggerThan(this.s3), true);

  }

 

  // test the method biggerThan in the class Square

  boolean testSquareBiggerThan(Tester t) {

    return

    t.checkExpect(this.s1.biggerThan(this.s2), false) &&

    t.checkExpect(this.s2.biggerThan(this.s1), true) &&

    t.checkExpect(this.s1.biggerThan(this.c1), true) &&

    t.checkExpect(this.s3.biggerThan(this.c1), false);

  }

 

   // test the method contains in the class Circle

  boolean testCircleContains(Tester t) {

    return

    t.checkExpect(this.c1.contains(new CartPt(100, 100)), false) &&

    t.checkExpect(this.c2.contains(new CartPt(40, 60)), true);

  }

 

  // test the method contains in the class Square

  boolean testSquareContains(Tester t) {

    return

    t.checkExpect(this.s1.contains(new CartPt(100, 100)), false) &&

    t.checkExpect(this.s2.contains(new CartPt(55, 60)), true);

  }

}