SE450: Horstmann Chapter 6

Contents [0/65]

Object-Oriented Design & Patterns [1/65]
Chapter Topics [2/65]
Modeling Specialization [3/65]
Modeling Specialization [4/65]
Modeling Specialization [5/65]
Manager Methods and Fields [6/65]
The Super/Sub Terminology [7/65]
The Super/Sub Terminology [8/65]
Inheritance Hierarchies [9/65]
Inheritance Hierarchies [10/65]
The Substitution Principle [11/65]
Invoking Superclass Methods [12/65]
Invoking Superclass Methods [13/65]
Invoking Superclass Constructors [14/65]
Preconditions [15/65]
Postconditions, Visibility, Exceptions [16/65]
Graphic Programming with Inheritance [17/65]
Mouse Listeners [18/65]
Mouse Adapters [19/65]
Car Mover Program [20/65]
Car Mover Program [21/65]
Scene Editor [22/65]
Scene Editor [23/65]
The SceneShape Interface Type [24/65]
The SceneShape Interface Type [25/65]
The SceneShape Interface Type [26/65]
CarShape and HouseShape Classes [27/65]
Abstract Classes [28/65]
Abstract Classes [29/65]
Abstract Classes [30/65]
Abstract Classes and Interface Types [31/65]
Scene Editor [32/65]
Uniform Highlighting Technique [33/65]
Uniform Highlighting Technique [34/65]
Template Method [35/65]
TEMPLATE METHOD Pattern [36/65]
TEMPLATE METHOD Pattern [37/65]
TEMPLATE METHOD Pattern [38/65]
TEMPLATE METHOD Pattern [39/65]
Compound Shapes [40/65]
Compound Shapes [41/65]
Access to Superclass Features [42/65]
Protected Access [43/65]
Hierarchy of Swing Components [44/65]
Hierarchy of Swing Components [45/65]
Hierarchy of Swing Components [46/65]
Look and Feel [47/65]
Hierarchy of Swing Components [48/65]
Hierarchy of Geometrical Shapes [49/65]
Hierarchy of Geometrical Shapes [50/65]
Rectangular Shapes [51/65]
Float/Double Classes [52/65]
Float/Double Classes [53/65]
Float/Double Classes [54/65]
Float/Double Classes [55/65]
Float/Double Classes [56/65]
TEMPLATE METHOD Pattern [57/65]
Hierarchy of Exception Classes [58/65]
Hierarchy of Exception Classes [59/65]
Catching Exceptions [60/65]
Defining Exception Classes [61/65]
When Not to Use Inheritance [62/65]
When Not to Use Inheritance [63/65]
When Not to Use Inheritance [64/65]
When Not to Use Inheritance [65/65]

Object-Oriented Design & Patterns [1/65]

Cay S. Horstmann

Chapter 6

Inheritance and Abstract Classes

horstmann-oodp2

Chapter Topics [2/65]

Modeling Specialization [3/65]

Modeling Specialization [4/65]

Modeling Specialization [5/65]

.

Manager Methods and Fields [6/65]

The Super/Sub Terminology [7/65]

The Super/Sub Terminology [8/65]


.

Inheritance Hierarchies [9/65]

Inheritance Hierarchies [10/65]

.

The Substitution Principle [11/65]

Invoking Superclass Methods [12/65]

Invoking Superclass Methods [13/65]

Invoking Superclass Constructors [14/65]

Preconditions [15/65]

Postconditions, Visibility, Exceptions [16/65]

Graphic Programming with Inheritance [17/65]

Mouse Listeners [18/65]

Mouse Adapters [19/65]

Car Mover Program [20/65]

file:horstmann/ch06_car/CarComponent.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package horstmann.ch06_car;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;

import javax.swing.JComponent;

/**
   A component that shows a scene composed of items.
 */
@SuppressWarnings("serial")
public class CarComponent extends JComponent
{
  public CarComponent()
  {
    car = new CarShape(20, 20, 50);
    addMouseListener(new
        MouseAdapter()
    {
      public void mousePressed(MouseEvent event)
      {
        mousePoint = event.getPoint();
        if (!car.contains(mousePoint))
          mousePoint = null;
      }
    });

    addMouseMotionListener(new
        MouseMotionAdapter()
    {
      public void mouseDragged(MouseEvent event)
      {
        if (mousePoint == null) return;
        Point lastMousePoint = mousePoint;
        mousePoint = event.getPoint();

        double dx = mousePoint.getX() - lastMousePoint.getX();
        double dy = mousePoint.getY() - lastMousePoint.getY();
        car.translate((int) dx, (int) dy);
        repaint();
      }
    });
  }

  public void paintComponent(Graphics g)
  {
    Graphics2D g2 = (Graphics2D) g;
    car.draw(g2);
  }

  private CarShape car;
  private Point mousePoint;
}

file:horstmann/ch06_car/CarMover.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package horstmann.ch06_car;
import javax.swing.JFrame;

/**
   A program that allows users to move a car with the mouse.
 */
public class CarMover
{
  public static void main(String[] args)
  {
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    frame.add(new CarComponent());
    frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
    frame.setVisible(true);
  }

  private static final int FRAME_WIDTH = 400;
  private static final int FRAME_HEIGHT = 400;
}


file:horstmann/ch06_car/CarShape.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
package horstmann.ch06_car;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;

/**
   A car shape.
 */
public class CarShape
{
  /**
      Constructs a car shape.
      @param x the left of the bounding rectangle
      @param y the top of the bounding rectangle
      @param width the width of the bounding rectangle
   */
  public CarShape(int x, int y, int width)
  {
    this.x = x;
    this.y = y;
    this.width = width;
  }

  public void draw(Graphics2D g2)
  {
    Rectangle2D.Double body
    = new Rectangle2D.Double(x, y + width / 6,
        width - 1, width / 6);
    Ellipse2D.Double frontTire
    = new Ellipse2D.Double(x + width / 6, y + width / 3,
        width / 6, width / 6);
    Ellipse2D.Double rearTire
    = new Ellipse2D.Double(x + width * 2 / 3,
        y + width / 3,
        width / 6, width / 6);

    // The bottom of the front windshield
    Point2D.Double r1
    = new Point2D.Double(x + width / 6, y + width / 6);
    // The front of the roof
    Point2D.Double r2
    = new Point2D.Double(x + width / 3, y);
    // The rear of the roof
    Point2D.Double r3
    = new Point2D.Double(x + width * 2 / 3, y);
    // The bottom of the rear windshield
    Point2D.Double r4
    = new Point2D.Double(x + width * 5 / 6, y + width / 6);
    Line2D.Double frontWindshield
    = new Line2D.Double(r1, r2);
    Line2D.Double roofTop
    = new Line2D.Double(r2, r3);
    Line2D.Double rearWindshield
    = new Line2D.Double(r3, r4);

    g2.draw(body);
    g2.draw(frontTire);
    g2.draw(rearTire);
    g2.draw(frontWindshield);
    g2.draw(roofTop);
    g2.draw(rearWindshield);
  }

  public boolean contains(Point2D p)
  {
    return x <= p.getX() && p.getX() <= x + width
        && y <= p.getY() && p.getY() <= y + width / 2;
  }

  public void translate(int dx, int dy)
  {
    x += dx;
    y += dy;
  }

  private int x;
  private int y;
  private int width;
}

Car Mover Program [21/65]

.

Scene Editor [22/65]

Scene Editor [23/65]



The SceneShape Interface Type [24/65]

The SceneShape Interface Type [25/65]

.

The SceneShape Interface Type [26/65]

public interface SceneShape
{
   void setSelected(boolean b);
   boolean isSelected();
   void draw(Graphics2D g2);
   void drawSelection(Graphics2D g2);
   void translate(int dx, int dy);
   boolean contains(Point2D aPoint);
}


CarShape and HouseShape Classes [27/65]

public class CarShape implements SceneShape 
{
...
public void setSelected(boolean b) { selected = b; }
public boolean isSelected() { return selected; }
private boolean selected;
}

public class HouseShape implements SceneShape
{
...
public void setSelected(boolean b) { selected = b; }
public boolean isSelected() { return selected; }
private boolean selected;
}

Abstract Classes [28/65]

public class SelectableShape implements Item 
{
public void setSelected(boolean b) { selected = b; }
public boolean isSelected() { return selected; }
private boolean selected;
}

Abstract Classes [29/65]

.

Abstract Classes [30/65]

Abstract Classes and Interface Types [31/65]

Scene Editor [32/65]

file:horstmann/ch06_scene1/SceneComponent.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
package horstmann.ch06_scene1;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.util.ArrayList;

import javax.swing.JComponent;

/**
   A component that shows a scene composed of shapes.
 */
@SuppressWarnings("serial")
public class SceneComponent extends JComponent
{
  public SceneComponent()
  {
    shapes = new ArrayList<SceneShape>();

    addMouseListener(new
        MouseAdapter()
    {
      public void mousePressed(MouseEvent event)
      {
        mousePoint = event.getPoint();
        for (SceneShape s : shapes)
        {
          if (s.contains(mousePoint))
            s.setSelected(!s.isSelected());
        }
        repaint();
      }
    });

    addMouseMotionListener(new
        MouseMotionAdapter()
    {
      public void mouseDragged(MouseEvent event)
      {
        Point lastMousePoint = mousePoint;
        mousePoint = event.getPoint();
        for (SceneShape s : shapes)
        {
          if (s.isSelected())
          {
            double dx = mousePoint.getX() - lastMousePoint.getX();
            double dy = mousePoint.getY() - lastMousePoint.getY();
            s.translate((int) dx, (int) dy);
          }
        }
        repaint();
      }
    });
  }

  /**
      Adds a shape to the scene.
      @param s the shape to add
   */
  public void add(SceneShape s)
  {
    shapes.add(s);
    repaint();
  }

  /**
      Removes all selected shapes from the scene.
   */
  public void removeSelected()
  {
    for (int i = shapes.size() - 1; i >= 0; i--)
    {
      SceneShape s = shapes.get(i);
      if (s.isSelected()) shapes.remove(i);
    }
    repaint();
  }

  public void paintComponent(Graphics g)
  {
    super.paintComponent(g);
    Graphics2D g2 = (Graphics2D) g;
    for (SceneShape s : shapes)
    {
      s.draw(g2);
      if (s.isSelected())
        s.drawSelection(g2);
    }
  }

  private ArrayList<SceneShape> shapes;
  private Point mousePoint;
}

file:horstmann/ch06_scene1/SceneEditor.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package horstmann.ch06_scene1;
import java.awt.BorderLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

/**
   A program that allows users to edit a scene composed
   of items.
 */
public class SceneEditor
{
  public static void main(String[] args)
  {
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    final SceneComponent scene = new SceneComponent();

    JButton houseButton = new JButton("House");
    houseButton.addActionListener(event -> scene.add(new HouseShape(20, 20, 50)));

    JButton carButton = new JButton("Car");
    carButton.addActionListener(event -> scene.add(new CarShape(20, 20, 50)));

    JButton removeButton = new JButton("Remove");
    removeButton.addActionListener(event -> scene.removeSelected());

    JPanel buttons = new JPanel();
    buttons.add(houseButton);
    buttons.add(carButton);
    buttons.add(removeButton);

    frame.add(scene, BorderLayout.CENTER);
    frame.add(buttons, BorderLayout.NORTH);
    frame.setSize(300, 300);
    frame.setVisible(true);
  }
}


file:horstmann/ch06_scene1/HouseShape.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
package horstmann.ch06_scene1;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;

/**
   A house shape.
 */
public class HouseShape extends SelectableShape
{
  /**
      Constructs a house shape.
      @param x the left of the bounding rectangle
      @param y the top of the bounding rectangle
      @param width the width of the bounding rectangle
   */
  public HouseShape(int x, int y, int width)
  {
    this.x = x;
    this.y = y;
    this.width = width;
  }

  public void draw(Graphics2D g2)
  {
    Rectangle2D.Double base
    = new Rectangle2D.Double(x, y + width, width, width);

    // The left bottom of the roof
    Point2D.Double r1
    = new Point2D.Double(x, y + width);
    // The top of the roof
    Point2D.Double r2
    = new Point2D.Double(x + width / 2, y);
    // The right bottom of the roof
    Point2D.Double r3
    = new Point2D.Double(x + width, y + width);

    Line2D.Double roofLeft
    = new Line2D.Double(r1, r2);
    Line2D.Double roofRight
    = new Line2D.Double(r2, r3);

    g2.draw(base);
    g2.draw(roofLeft);
    g2.draw(roofRight);
  }

  public void drawSelection(Graphics2D g2)
  {
    Rectangle2D.Double base
    = new Rectangle2D.Double(x, y + width, width, width);
    g2.fill(base);
  }

  public boolean contains(Point2D p)
  {
    return x <= p.getX() && p.getX() <= x + width
        && y <= p.getY() && p.getY() <= y + 2 * width;
  }

  public void translate(int dx, int dy)
  {
    x += dx;
    y += dy;
  }

  private int x;
  private int y;
  private int width;
}

Uniform Highlighting Technique [33/65]

Uniform Highlighting Technique [34/65]



Template Method [35/65]

file:horstmann/ch06_scene2/SelectableShape.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package horstmann.ch06_scene2;
import java.awt.Graphics2D;

/**
   A shape that manages its selection state.
 */
public abstract class SelectableShape implements SceneShape
{
  public void setSelected(boolean b)
  {
    selected = b;
  }

  public boolean isSelected()
  {
    return selected;
  }

  public void drawSelection(Graphics2D g2)
  {
    translate(1, 1);
    draw(g2);
    translate(1, 1);
    draw(g2);
    translate(-2, -2);
  }

  private boolean selected;
}

file:horstmann/ch06_scene2/HouseShape.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package horstmann.ch06_scene2;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;

/**
   A house shape.
 */
public class HouseShape extends SelectableShape
{
  /**
      Constructs a house shape.
      @param x the left of the bounding rectangle
      @param y the top of the bounding rectangle
      @param width the width of the bounding rectangle
   */
  public HouseShape(int x, int y, int width)
  {
    this.x = x;
    this.y = y;
    this.width = width;
  }

  public void draw(Graphics2D g2)
  {
    Rectangle2D.Double base
    = new Rectangle2D.Double(x, y + width, width, width);

    // The left bottom of the roof
    Point2D.Double r1
    = new Point2D.Double(x, y + width);
    // The top of the roof
    Point2D.Double r2
    = new Point2D.Double(x + width / 2, y);
    // The right bottom of the roof
    Point2D.Double r3
    = new Point2D.Double(x + width, y + width);

    Line2D.Double roofLeft
    = new Line2D.Double(r1, r2);
    Line2D.Double roofRight
    = new Line2D.Double(r2, r3);

    g2.draw(base);
    g2.draw(roofLeft);
    g2.draw(roofRight);
  }

  public boolean contains(Point2D p)
  {
    return x <= p.getX() && p.getX() <= x + width
        && y <= p.getY() && p.getY() <= y + 2 * width;
  }

  public void translate(int dx, int dy)
  {
    x += dx;
    y += dy;
  }

  private int x;
  private int y;
  private int width;
}

TEMPLATE METHOD Pattern [36/65]

Context

  1. An algorithm is applicable for multiple types.
  2. The algorithm can be broken down into primitive operations. The primitive operations can be different for each type
  3. The order of the primitive operations doesn't depend on the type

TEMPLATE METHOD Pattern [37/65]

Solution

  1. Define a superclass that has a method for the algorithm and abstract methods for the primitive operations.
  2. Implement the algorithm to call the primitive operations in the appropriate order.
  3. Do not define the primitive operations in the superclass, or define them to have appropriate default behavior.
  4. Each subclass defines the primitive operations but not the algorithm.

TEMPLATE METHOD Pattern [38/65]

.

TEMPLATE METHOD Pattern [39/65]

Name in Design Pattern
Actual Name (Selectable shapes)
AbstractClass
SelectableShape
ConcreteClass
CarShape, HouseShape
templateMethod()
drawSelection
primitiveOp1(), primitiveOp2()
translate, draw

Compound Shapes [40/65]

file:horstmann/ch06_scene3/CompoundShape.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package horstmann.ch06_scene3;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;

/**
   A scene shape that is composed of multiple geometric shapes.
 */
public abstract class CompoundShape extends SelectableShape
{
  public CompoundShape()
  {
    path = new GeneralPath();
  }

  protected void add(Shape s)
  {
    path.append(s, false);
  }

  public boolean contains(Point2D aPoint)
  {
    return path.contains(aPoint);
  }

  public void translate(int dx, int dy)
  {
    path.transform(
        AffineTransform.getTranslateInstance(dx, dy));
  }

  public void draw(Graphics2D g2)
  {
    g2.draw(path);
  }

  private GeneralPath path;
}







file:horstmann/ch06_scene3/HouseShape.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package horstmann.ch06_scene3;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;

/**
   A house shape.
 */
public class HouseShape extends CompoundShape
{
  /**
      Constructs a house shape.
      @param x the left of the bounding rectangle
      @param y the top of the bounding rectangle
      @param width the width of the bounding rectangle
   */
  public HouseShape(int x, int y, int width)
  {
    Rectangle2D.Double base
    = new Rectangle2D.Double(x, y + width, width, width);

    // The left bottom of the roof
    Point2D.Double r1
    = new Point2D.Double(x, y + width);
    // The top of the roof
    Point2D.Double r2
    = new Point2D.Double(x + width / 2, y);
    // The right bottom of the roof
    Point2D.Double r3
    = new Point2D.Double(x + width, y + width);

    Line2D.Double roofLeft
    = new Line2D.Double(r1, r2);
    Line2D.Double roofRight
    = new Line2D.Double(r2, r3);

    add(base);
    add(roofLeft);
    add(roofRight);
  }
}

Compound Shapes [41/65]

.

Access to Superclass Features [42/65]

Protected Access [43/65]

Hierarchy of Swing Components [44/65]

Hierarchy of Swing Components [45/65]

.

Hierarchy of Swing Components [46/65]

Look and Feel [47/65]



Hierarchy of Swing Components [48/65]

Hierarchy of Geometrical Shapes [49/65]

Hierarchy of Geometrical Shapes [50/65]

.


Rectangular Shapes [51/65]

Float/Double Classes [52/65]

Float/Double Classes [53/65]

.

Float/Double Classes [54/65]

public class Rectangle2D 
{
public static class Float extends Rectangle2D
{
public double getX() { return x; }
public double getY() { return y; }
public double getWidth() { return width; }
public double getHeight() { return height;}
public void setRect(float x, float y, float w, float h)
{
this.x = x; this.y = y;
this.width = w; this.height = h;
}
public void setRect(double x, double y,
double w, double h)
{
this.x = (float)x; this.y = (float)y;
this.width = (float)w; this.height = (float)h;
}
...
public float x;
public float y;
public float width;
public float height;
}
. . .

Float/Double Classes [55/65]

   . . .
public static class Double extends Rectangle2D
public double getX() { return x; }
public double getY() { return y; }
public double getWidth() { return width; }
public double getHeight() { return height;}
public void setRect(double x, double y,
double w, double h)
{
this.x = x; this.y = y;
this.width = w; this.height = h;
}
...
public double x;
public double y;
public double width;
public double height;
}
...
}

Float/Double Classes [56/65]

TEMPLATE METHOD Pattern [57/65]

Name in Design Pattern
Actual Name (Rectangles)
AbstractClass
Rectangle
ConcreteClass
Rectangle2D.Double
templateMethod()
contains
primitiveOpn()
getX, getY, getWidth, getHeight

Hierarchy of Exception Classes [58/65]

Hierarchy of Exception Classes [59/65]

.

Catching Exceptions [60/65]

Defining Exception Classes [61/65]

When Not to Use Inheritance [62/65]

public class Point 
{
public Point(int anX, int aY) { ... }
public void translate(int dx, int dy) { ... }
private int x;
private int y;
}

public class Circle extends Point // DON'T
{
public Circle(Point center, int radius) { ... }
public void draw(Graphics g) { ... }
private int radius;
}

When Not to Use Inheritance [63/65]

When Not to Use Inheritance [64/65]

When Not to Use Inheritance [65/65]


Revised: 2007/09/11 16:28