Presentation is loading. Please wait.

Presentation is loading. Please wait.

Five-Minute Review What are enumerated types? How can we represent enumerations in Java? What is character arithmetic? What is a string, conceptually?

Similar presentations


Presentation on theme: "Five-Minute Review What are enumerated types? How can we represent enumerations in Java? What is character arithmetic? What is a string, conceptually?"— Presentation transcript:

1 Five-Minute Review What are enumerated types? How can we represent enumerations in Java? What is character arithmetic? What is a string, conceptually? How do we check strings for equality? What are the rules governing the order of expression evaluation? Types that are identified by counting off the elements a) defining different constants, b) using enum type Arithmetic on the Unicode encodings of characters Ordered collection of characters With equals method. With string literals, can also use ==, because JVM uses pool of string literals a) precedence, b) association, c) left-to-right

2 Programming – Lecture 10 Detecting Bugs
Object-Oriented Graphics (Chapter 9) acm.graphics package GCanvas Encapsulated Coordinates GMath, GObjects Interfaces GLabel, GRect, GOval, GLine, GArc GImage, GPolygon, GCompound Graphical Object Decomposition Randomness in games

3 Detecting Bugs println-debugging Using debugger (in IDE, e.g. Eclipse)
Test class, unit testing Assertions Experience has shown that writing assertions while programming is one of the quickest and most effective ways to detect and correct bugs. As an added benefit, assertions serve to document the inner workings of your program, enhancing maintainability.

4 Debugging with Eclipse
Set breakpoints simply by clicking left of line number Execute with "bug-icon". Then: Step Into (F5) Step Over (F6) Step Return (F7) Resume (F8)

5 assert condition : error-string;
Must also enable in Eclipse: Preferences → Java → Installed JREs → Edit → Default VM arguments: -ea Disabled assertions have no performance penalty! Should be used for checking invariants, preconditions, postconditions Should be used instead of comments Thus, performance concerns are not an argument against assertions!

6 Invariants Invariant: something expected to hold at certain point. Before assertions, invariants typically stated as a comment: int n = countSomething(); if (n % 2 == 0) { ... } else { // It must be n % 2 == 1 } Problem: no check that assumption holds Consider e.g. negative n Invariant in Java: n == n / d * d + n % d for all int's n, d

7 Invariants – With Assertions
int n = countSomething(); if (n % 2 == 0) { ... } else { assert n % 2 == 1 : "n % 2 = " + n % 2 + ", not 1!"; }

8 Preconditions Preconditions must hold before some computation, e.g., on method arguments For non-public methods, use assertions to check input arguments: private void setInterval(int i) { assert i > 0 && i <= 1000/MAX_RATE : i + " is not legal interval"; ... }

9 Preconditions For public methods, don't use assertion, but throw exception upon invalid input arguments: public void setRate(int rate) { if (rate <= 0 || rate > MAX_RATE) throw new IllegalArgumentException( "Illegal rate: " + rate); setInterval(1000/rate); }

10 Postconditions Postconditions must hold after some computation, e.g., on method return value public int countSomething() { ... assert count >= 0 : "got negative count!"; return count; }

11 {B ∧ P} S {Q} , {¬B ∧ P } T {Q} {P} if B then S else T endif {Q}
Aside 1: Hoare Logic Hoare logic (a.k.a. Floyd-Hoare logic) to formally analyze computer programs Key component is the Hoare triple, of form { Precondition } Command { Postcondition } Set of reasoning rules, such as {B ∧ P} S {Q}   ,   {¬B ∧ P } T {Q} {P} if B then S else T endif {Q} Can prove partial correctness: if program terminates, then it produces correct result C.A.R. Hoare, An axiomatic basis for computer programming, Communications of the ACM, 12 (10): 576–580, Oct. 1969

12 Aside 2: Design by Contract
Clients and implementors of piece of software (e.g., a method) agree on contract, specifying Preconditions – obligations on clients Postconditions – obligations on implementors (Class) invariants – obligations on both Bertrand Meyer, Applying Design by Contract, Computer (IEEE), 25 (10),Oct. 1992

13 Summary Assertions Assertions are a very effective mechanism for detecting bugs early, both during development and when deployed Assertions can be disabled, in which case they carry no run-time overhead Common uses of assertions are the checking of invariants, preconditions, postconditions Assertions should not replace input checks on public methods, which should be performed irrespective of whether assertions are enabled or not Example with Catalan Numbers (in WS 17/18: Set 8, Number 3, "The Ugly Side of Recursion" Assume we erroneously compute computeCatalanNumber(k n, currentStackDepth + 1) instead of computeCatalanNumber(n k, currentStackDepth + 1); Assume further we want to compute decreasing Catalan numbers with the following code: int c = 5; for (int i = 0; i <= 5; i++) { c--; System.out.println("catalan(" + c + ") = " + helper.catalan(c)); } Executing this yields Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -5 at programming.set8.catalan.RatherExcellentRecursiveCatalanNumbersCalculationHelper.computeCatalanNumber(RatherExcellentRecursiveCatalanNumbersCalculationHelper.java:76) at programming.set8.catalan.RatherExcellentRecursiveCatalanNumbersCalculationHelper.computeCatalanNumber(RatherExcellentRecursiveCatalanNumbersCalculationHelper.java:87) at programming.set8.catalan.RatherExcellentRecursiveCatalanNumbersCalculationHelper.catalan(RatherExcellentRecursiveCatalanNumbersCalculationHelper.java:55) at programming.set8.catalan.RatherExcellentRecursiveCatalanNumbersCalculationHelper.main(RatherExcellentRecursiveCatalanNumbersCalculationHelper.java:13) MEASURE 1: Since things can obviously go wrong here. put in assertion at beginning of computeCatalanNumber():     assert n >= 0 : "Want n >= 0, got n = " + n; Still get the same exception. MEASURE 2: Enable exception in Eclipse: Preferences → Java → Installed JREs → Edit → Default VM arguments: -ea Now we properly check argument of computeCatalanNumber(). We still have not eliminated the bug. To find the bug: MEASURE 3: Use debgger to identify the bad call to computeCatalanNumber(). In catalan(), set breakpoint at first call to computeCatalanNumber(). Start debugger (click on bug icon). When breakpoint is reached, step into call to computeCatalanNumber(). Step over statements, until we reach recursive call to computeCatalanNumber(). When stepping over second call to computeCatalanNumber(), we get exception. Thus this must be the culprit. Now delete first breakpoint, instead set breakpoint at second call to computeCatalanNumber(). Look at arguments: n = 4, k = 0, thus k – 1 – n = -5. MEASURE 4: Apply fix by correcting to Run code again – now get this output: catalan(4) = 14 Exception in thread "main" catalan(3) = 5 catalan(2) = 2 catalan(1) = 1 catalan(0) = 1 java.lang.IllegalArgumentException: n must be >= 0. at programming.set8.catalan.RatherExcellentRecursiveCatalanNumbersCalculationHelper.catalan(RatherExcellentRecursiveCatalanNumbersCalculationHelper.java:45) First, note that there is a race condition between output from program and from exception. MEASURE 5: To find out wrong value of n: again, in catalan(), set breakpoint at first call to computeCatalanNumber() to get value of n. In fact, might have been helpful to provide value of n as part of the java.lang.IllegalArgumentException message. Use resume to step through calls, observe values of n: 4, 3, 2, 1, -1. Realize that loop in test function steps into negative values. MEASURE 6: In main(), change loop from

14 Recall: Stacking Order
Before you can appreciate the classes and methods available in acm.graphics, you need to understand the assumptions, conventions, and metaphors on which the package is based. Collectively, these ideas that define the appropriate mental picture for a package are called a model. The model for a package allows you to answer various questions about how you should think about working in that domain. Before using a graphics package, for example, you need to be able to answer the following sorts of questions: • What are the real-world analogies and metaphors that underlie the package design? • How do you specify positions on the screen? • What units do you use to specify lengths? To support the notion of object-oriented design, the acm.graphics package uses the metaphor of a collage. A collage artist works by taking various objects and assembling them on a background canvas. Those objects might be geometrical shapes, words clipped from newspapers, lines formed from bits of string, or images taken from magazines. The acm.graphics package offers counterparts for all of these. The notion that the image is a collage has important implications for the way you describe the process of creating a design. If you are painting, you might talk about making a brush stroke in a particular position or filling an area with paint. In the collage model, the key operations are adding and removing objects, along with repositioning them on the background. Collages also have the property that some objects can be positioned on top of other objects, where they obscure whatever is behind them. Removing those objects reveals whatever used to be underneath. Wikipedia / marcel4995 / CC

15 acm.graphics Package GObject is abstract class Interfaces: GCanvas
GPoint GDimension GRectangle GMath GTurtle GCompound GObject GPen Classes lower two rows: shape classes GLabel GRect GOval GLine GArc GImage GPolygon Interfaces: GFillable GResizable GScalable GContainer GRoundRect G3DRect GObject is abstract class

16 Calls to these methods are forwarded from GraphicsProgram to GCanvas :
Adds the object to the canvas at the front of the stack Moves the object to (x, y) and then adds it to the canvas Removes the object from the canvas Removes all objects from the canvas Returns the frontmost object at (x, y), or null if none Returns the width in pixels of the entire canvas Returns the height in pixels of the entire canvas Sets the background color of the canvas to c. add(object) add(object, x, y) remove(object) removeAll() getElementAt(x, y) getWidth() getHeight() setBackground(c) Methods in GraphicsProgram only Pauses the program for the specified time in milliseconds Suspends the program until the user clicks the mouse pause(milliseconds) waitForClick()

17 Encapsulated Coordinates
GPoint(x, y) GDimension(width, height) GRectangle(x, y, width, height) getX, getY, getWidth, getHeight

18 GMath Class GMath.sinDegrees(theta)
GMath.cosDegrees(theta) GMath.tanDegrees(theta) GMath.distance(x0, y0, x1, y1) GMath.toRadians(degrees) GMath.toDegrees(radians) GMath.distance(x, y) GMath.round(x) GMath.angle(x, y) GMath.angle(x0, y0, x1, y1) Returns the sine of theta, measured in degrees Returns the cosine of theta Returns the tangent of theta Returns the angle in degrees formed by the line connecting the origin to the point (x, y) connecting the points (x0, y0) and (x1, y1) Returns the distance from (x0, y0) to (x1, y1) Converts an angle from degrees to radians Converts an angle from radians to degrees Returns the distance from the origin to (x, y) Returns the closest int to x The GMath class exports the following static methods:

19 Methods Common to GObjects
setLocation(x, y) move(dx, dy) movePolar(r, theta) getX() getY() getWidth() getHeight() contains(x, y) setColor(c) getColor() setVisible( flag) isVisible() sendToFront() sendToBack() sendForward() sendBackward() Resets the location of the object to the specified point Moves the object dx and dy pixels from its current position Moves the object r pixel units in direction theta Returns the x coordinate of the object Returns the y coordinate of the object Returns the horizontal width of the object in pixels Returns the vertical height of the object in pixels Returns true if the object contains the specified point Sets the color of the object to the Color c Returns the color currently assigned to the object Sets the visibility flag (false = invisible, true = visible) Returns true if the object is visible Sends the object to the front of the stacking order Sends the object to the back of the stacking order Sends the object forward one position in the stacking order Sends the object backward one position in the stacking order

20 Multiple Inheritance Multiple inheritance (Mehrfachvererbung): a class has multiple superclasses Allowed in some languages (e.g. C++) Conceptually tricky – consider diamond problem (next slide) No multiple inheritance in Java In Java, classes have exactly one parent class, except java.lang.Object, which has none

21 Diamond Problem class A implements method foo()
classes B and C inherit from A, both override foo() class D inherits from B and C Problem: which foo() should D use? A B C D

22 Interfaces Interface: conceptually, collection of method signatures
Some functionality of multiple inheritance: classes can implement several interfaces Generally, class must implement methods of interface; since Java 8, can implement default and static methods in interface def. public class GRect extends GObject   implements GFillable, GResizable, GScalable 

23 Interfaces in acm.graphics
setFilled( flag) isFilled() setFillColor(c) getFillColor() Sets the fill state for the object (false = outlined, true = filled) Returns the fill state for the object Sets the color used to fill the interior of the object to c Returns the fill color GFillable (GArc, GOval, GPolygon, GRect) Methods Defined by Interfaces setSize(width, height) setBounds(x, y, width, height) Sets the dimensions of the object as specified Sets the location and dimensions together GResizable (GImage, GOval, GRect) scale(sf ) scale(sx, sy) Scales both dimensions of the object by sf Scales the object by sx horizontally and sy vertically GScalable (GArc, GCompound, GLine, GImage, GOval, GPolygon, GRect)

24 Shape Classes GObject GLabel GRect GOval GLine GArc GImage GPolygon The shape classes are the GObject subclasses that appear in yellow at the bottom of the hierarchy diagram. Each of the shape classes corresponds precisely to a method in the Graphics class in the java.awt package. Once you have learned to use the shape classes, you will easily be able to transfer that knowledge to Java’s standard graphics tools. GRoundRect G3DRect Each corresponds to method in Graphics class in java.awt package

25 hello, world public class HelloProgram extends GraphicsProgram {
public void run() { GLabel label = new GLabel("hello, world", 100, 75); label.setFont("SansSerif-36"); label.setColor(Color.RED); add(label); } You’ve been using the GLabel class ever since Chapter 2 and already know how to change the font and color, as shown in the most recent version of the “Hello World” program: HelloProgram hello, world

26 The quick brown fox jumps over the lazy dog.
origin The quick brown fox jumps over the lazy dog. ascent baseline height descent

27 Note: Most characters have smaller height than ascent
public class CenterLabel extends GraphicsProgram { public void run() { setSize(400, 200); GLabel label = new GLabel("hello, world"); label.setFont("SansSerif-48"); label.setColor(Color.RED); double x = (getWidth() - label.getWidth()) / 2; double x1 = x + label.getWidth(); double y = (getHeight() + label.getAscent()) / 2; double y1 = y - label.getAscent(); double y2 = y + label.getDescent(); add(label, x, y); add(new GLine(x, y, x1, y)); add(new GLine(x, y1, x1, y1)); add(new GLine(x, y2, x1, y2)); } The following update to the “Hello World” program centers the label in the window: Note: Most characters have smaller height than ascent

28 GRect GRoundRect G3DRect
add(new GRoundRect(100, 60, 75, 50)); add(new G3DRect(300, 60, 75, 50)); RoundAnd3DRect

29 GOval(getWidth(), getHeight()); oval.setFilled(true);
public void run() { GOval oval = new GOval(getWidth(), getHeight()); oval.setFilled(true); oval.setColor(Color.GREEN); add(oval, 0, 0); } The GOval class represents an elliptical shape defined by the boundaries of its enclosing rectangle. As an example, the following run method creates the largest oval that fits within the canvas: LargestOval

30 Setting Points in GLine
public void run() { GLine line = new GLine(0, 0, 100, 100); add(line); line.setLocation(200, 50); line.setStartPoint(200, 150); line.setEndPoint(300, 50); } public void run() { GLine line = new GLine(0, 0, 100, 100); add(line); line.setLocation(200, 50); line.setStartPoint(200, 150); line.setEndPoint(300, 50); } The following run method illustrates the difference between the setLocation method (which moves both points together) and setStartPoint/setEndPoint (which move only one): LineGeometryExample

31 GArc(double width, double height,
double start, double sweep)

32 Filled Arcs public void run() { GArc arc = new GArc(0, 0,
getWidth(), getHeight(), 0, 90); arc.setFilled(true); add(arc); } The GArc class implements the GFillable interface, which means that you can call setFilled on a GArc object. A filled GArc is displayed as the pie-shaped wedge formed by the center and the endpoints of the arc, as illustrated below: FilledEllipticalArc

33 add(new GImage("EarthFromApollo17.jpg"));
public void run() { add(new GImage("EarthFromApollo17.jpg")); addCitation("Courtesy NASA/JPL-Caltech"); } public void run() { add(new GImage("EarthFromApollo17.jpg")); addCitation("Courtesy NASA/JPL-Caltech"); } private void addCitation(String text) { GLabel label = new GLabel(text); label.setFont(CITATION_FONT); double x = getWidth() - label.getWidth(); double y = getHeight() - CITATION_MARGIN + label.getAscent(); add(label, x, y); } EarthImage Courtesy NASA/JPL-Caltech

34 GPolygon Polygons, vertices, edges Reference points diamond
regular hexagon five-pointed star

35 Drawing a Diamond (addVertex)
public void run() { GPolygon diamond = createDiamond(100, 75); diamond.setFilled(true); diamond.setFillColor(Color.MAGENTA); add(diamond, getWidth() / 2, getHeight() / 2); } private GPolygon createDiamond(double width, double height) { GPolygon diamond = new GPolygon(); diamond.addVertex(-width / 2, 0); diamond.addVertex(0, -height / 2); diamond.addVertex(width / 2, 0); diamond.addVertex(0, height / 2); return diamond; diamond public void run() { GPolygon diamond = createDiamond(100, 75); diamond.setFilled(true); diamond.setFillColor(Color.MAGENTA); add(diamond, getWidth() / 2, getHeight() / 2); } private GPolygon createDiamond(double width, double height) { GPolygon diamond = new GPolygon(); diamond.addVertex(-width / 2, 0); diamond.addVertex(0, -height / 2); diamond.addVertex(width / 2, 0); diamond.addVertex(0, height / 2); return diamond; } diamond diamond The following program draws a diamond using addVertex: DrawDiamond skip simulation

36 Drawing a Diamond (addEdge)
public void run() { GPolygon diamond = createDiamond(100, 75); diamond.setFilled(true); diamond.setFillColor(Color.MAGENTA); add(diamond, getWidth() / 2, getHeight() / 2); } private GPolygon createDiamond(double width, double height) { GPolygon diamond = new GPolygon(); diamond.addVertex(-width / 2, 0); diamond.addEdge(width / 2, -height / 2); diamond.addEdge(width / 2, height / 2); diamond.addEdge(-width / 2, height / 2); diamond.addEdge(-width / 2, -height / 2); return diamond; diamond public void run() { GPolygon diamond = createDiamond(100, 75); diamond.setFilled(true); diamond.setFillColor(Color.MAGENTA); add(diamond, getWidth() / 2, getHeight() / 2); } private GPolygon createDiamond(double width, double height) { GPolygon diamond = new GPolygon(); diamond.addVertex(-width / 2, 0); diamond.addEdge(width / 2, -height / 2); diamond.addEdge(width / 2, height / 2); diamond.addEdge(-width / 2, height / 2); diamond.addEdge(-width / 2, -height / 2); return diamond; } diamond diamond This program draws the same diamond using addEdge: DrawDiamond skip simulation

37 addPolarEdge(r, theta)

38 Hexagon 50.0 -240 -300 -180 -120 60 -60 public void run() {
GPolygon hexagon = createHexagon(50); add(hexagon, getWidth() / 2, getHeight() / 2); } private GPolygon createHexagon(double side) { GPolygon hex = new GPolygon(); hex.addVertex(-side, 0); int angle = 60; for (int i = 0; i < 6; i++) { hex.addPolarEdge(side, angle); angle -= 60; return hex; hex public void run() { GPolygon hexagon = createHexagon(50); add(hexagon, getWidth() / 2, getHeight() / 2); } private GPolygon createHexagon(double side) { GPolygon hex = new GPolygon(); hex.addVertex(-side, 0); int angle = 60; for (int i = 0; i < 6; i++) { hex.addPolarEdge(side, angle); angle -= 60; } return hex; hex side angle 50.0 hexagon This program draws a regular hexagon using addPolarEdge: -240 -300 -180 -120 60 -60 DrawHexagon skip simulation

39 Defining GPolygon Subclasses
public class GHexagon extends GPolygon { public GHexagon(double side) { addVertex(-side, 0); int angle = 60; for (int i = 0; i < 6; i++) { addPolarEdge(side, angle); angle -= 60; }

40 polygon.addArc(width, height, start, sweep);
public class GArchedDoor extends GPolygon { public GArchedDoor(double width, double height) { double lengthOfVerticalEdge = height - width / 2; addVertex(-width / 2, 0); addEdge(width, 0); addEdge(0, -lengthOfVerticalEdge); addArc(width, width, 0, 180); addEdge(0, lengthOfVerticalEdge); } Using the addArc Method: The following class definition creates a new GPolygon subclass that appears as an arched doorway, as shown in the sample run: DrawArchedDoor

41 So far: method decomposition Now: object decomposition
DrawTrain How to achieve this? So far: method decomposition Now: object decomposition

42 GCompound Conceptually: GCanvas (can add objects to it) + GObject (can add it to a canvas) DrawFace

43 GFace import acm.graphics.*; /** Defines a compound GFace class */
public class GFace extends GCompound { /** Creates a new GFace object with the specified dimensions */ public GFace(double width, double height) { head = new GOval(width, height); leftEye = new GOval(EYE_WIDTH * width, EYE_HEIGHT * height); rightEye = new GOval(EYE_WIDTH * width, EYE_HEIGHT * height); nose = createNose(NOSE_WIDTH * width, NOSE_HEIGHT * height); mouth = new GRect(MOUTH_WIDTH * width, MOUTH_HEIGHT * height); add(head, 0, 0); add(leftEye, 0.25 * width - EYE_WIDTH * width / 2, 0.25 * height - EYE_HEIGHT * height / 2); add(rightEye, 0.75 * width - EYE_WIDTH * width / 2, add(nose, 0.50 * width, 0.50 * height); add(mouth, 0.50 * width - MOUTH_WIDTH * width / 2, 0.75 * height - MOUTH_HEIGHT * height / 2); } page 1 of 2 skip code

44 GFace /* Creates a triangle for the nose */
private GPolygon createNose(double width, double height) { GPolygon poly = new GPolygon(); poly.addVertex(0, -height / 2); poly.addVertex(width / 2, height / 2); poly.addVertex(-width / 2, height / 2); return poly; } /* Constants specifying feature size as a fraction of the head size */ private static final double EYE_WIDTH = 0.15; private static final double EYE_HEIGHT = 0.15; private static final double NOSE_WIDTH = 0.15; private static final double NOSE_HEIGHT = 0.10; private static final double MOUTH_WIDTH = 0.50; private static final double MOUTH_HEIGHT = 0.03; /* Private instance variables */ private GOval head; private GOval leftEye, rightEye; private GPolygon nose; private GRect mouth; import acm.graphics.*; /** Defines a compound GFace class */ public class GFace extends GCompound { /** Creates a new GFace object with the specified dimensions */ public GFace(double width, double height) { head = new GOval(width, height); leftEye = new GOval(EYE_WIDTH * width, EYE_HEIGHT * height); rightEye = new GOval(EYE_WIDTH * width, EYE_HEIGHT * height); nose = createNose(NOSE_WIDTH * width, NOSE_HEIGHT * height); mouth = new GRect(MOUTH_WIDTH * width, MOUTH_HEIGHT * height); add(head, 0, 0); add(leftEye, 0.25 * width - EYE_WIDTH * width / 2, 0.25 * height - EYE_HEIGHT * height / 2); add(rightEye, 0.75 * width - EYE_WIDTH * width / 2, add(nose, 0.50 * width, 0.50 * height); add(mouth, 0.50 * width - MOUTH_WIDTH * width / 2, 0.75 * height - MOUTH_HEIGHT * height / 2); } page 2 of 2 skip code

45 Specifying Behavior of a GCompound
public void run() { GStoplight stoplight = new GStoplight(); add(stoplight, getWidth() / 2, getHeight() / 2); stoplight.setState(Color.RED); } GStoplightExample

46 GStoplight /** * Defines a GObject subclass that displays a stoplight. The * state of the stoplight must be one of the Color values RED, * YELLOW, or GREEN. */ public class GStoplight extends GCompound { /** Creates a new Stoplight object, which is initially GREEN */ public GStoplight() { GRect frame = new GRect(FRAME_WIDTH, FRAME_HEIGHT); frame.setFilled(true); frame.setFillColor(Color.GRAY); add(frame, -FRAME_WIDTH / 2, -FRAME_HEIGHT / 2); double dy = FRAME_HEIGHT / 4 + LAMP_RADIUS / 2; redLamp = createFilledCircle(0, -dy, LAMP_RADIUS); add(redLamp); yellowLamp = createFilledCircle(0, 0, LAMP_RADIUS); add(yellowLamp); greenLamp = createFilledCircle(0, dy, LAMP_RADIUS); add(greenLamp); setState(Color.GREEN); } page 1 of 3 skip code

47 GStoplight /** Sets the state of the stoplight */
public void setState(Color color) { if (color.equals(Color.RED)) { redLamp.setFillColor(Color.RED); yellowLamp.setFillColor(Color.GRAY); greenLamp.setFillColor(Color.GRAY); } else if (color.equals(Color.YELLOW)) { redLamp.setFillColor(Color.GRAY); yellowLamp.setFillColor(Color.YELLOW); } else if (color.equals(Color.GREEN)) { greenLamp.setFillColor(Color.GREEN); } state = color; /** Returns the current state of the stoplight */ public Color getState() { return state; /** * Defines a GObject subclass that displays a stoplight. The * state of the stoplight must be one of the Color values RED, * YELLOW, or GREEN. */ public class GStoplight extends GCompound { /** Creates a new Stoplight object, which is initially GREEN */ public GStoplight() { GRect frame = new GRect(FRAME_WIDTH, FRAME_HEIGHT); frame.setFilled(true); frame.setFillColor(Color.GRAY); add(frame, -FRAME_WIDTH / 2, -FRAME_HEIGHT / 2); double dy = FRAME_HEIGHT / 4 + LAMP_RADIUS / 2; redLamp = createFilledCircle(0, -dy, LAMP_RADIUS); add(redLamp); yellowLamp = createFilledCircle(0, 0, LAMP_RADIUS); add(yellowLamp); greenLamp = createFilledCircle(0, dy, LAMP_RADIUS); add(greenLamp); setState(Color.GREEN); } page 2 of 3 skip code

48 GStoplight /* Creates a filled circle centered at (x, y) with radius r */ private GOval createFilledCircle(double x, double y, double r) { GOval circle = new GOval(x - r, y - r, 2 * r, 2 * r); circle.setFilled(true); return circle; } /* Private constants */ private static final double FRAME_WIDTH = 50; private static final double FRAME_HEIGHT = 100; private static final double LAMP_RADIUS = 10; /* Private instance variables */ private Color state; private GOval redLamp; private GOval yellowLamp; private GOval greenLamp; /** Sets the state of the stoplight */ public void setState(Color color) { if (color.equals(Color.RED)) { redLamp.setFillColor(Color.RED); yellowLamp.setFillColor(Color.GRAY); greenLamp.setFillColor(Color.GRAY); } else if (color.equals(Color.YELLOW)) { redLamp.setFillColor(Color.GRAY); yellowLamp.setFillColor(Color.YELLOW); } else if (color.equals(Color.GREEN)) { greenLamp.setFillColor(Color.GREEN); } state = color; /** Returns the current state of the stoplight */ public Color getState() { return state; page 3 of 3 skip code

49 GCompound Coordinate System
getCanvasPoint(x, y) Converts local point (x, y) to canvas coordinates getLocalPoint(x, y) Converts canvas point (x, y) to local coordinates

50 Graphical Object Decomposition
GCompound supports decomposition in domain of graphical objects Before (Chapter 5): decomposed train cars into hierarchy of methods Now: decompose into hierarchy of classes DrawTrain

51 TrainCar Caboose Engine Boxcar GCompound
DrawTrain 90

52 TrainCar import acm.graphics.*; import java.awt.*;
/** This abstract class defines what is common to all train cars */ public abstract class TrainCar extends GCompound { /** * Creates the frame of the car using the specified color. color The color of the new train car */ public TrainCar(Color color) { double xLeft = CONNECTOR; double yBase = -CAR_BASELINE; add(new GLine(0, yBase, CAR_WIDTH + 2 * CONNECTOR, yBase)); addWheel(xLeft + WHEEL_INSET, -WHEEL_RADIUS); addWheel(xLeft + CAR_WIDTH - WHEEL_INSET, -WHEEL_RADIUS); double yTop = yBase - CAR_HEIGHT; GRect r = new GRect(xLeft, yTop, CAR_WIDTH, CAR_HEIGHT); r.setFilled(true); r.setFillColor(color); add(r); } page 1 of 2 skip code

53 TrainCar /* Adds a wheel centered at (x, y) */ import acm.graphics.*;
private void addWheel(double x, double y) { GOval wheel = new GOval(x - WHEEL_RADIUS, y - WHEEL_RADIUS, 2 * WHEEL_RADIUS, 2 * WHEEL_RADIUS); wheel.setFilled(true); wheel.setFillColor(Color.GRAY); add(wheel); } /* Private constants */ protected static final double CAR_WIDTH = 75; protected static final double CAR_HEIGHT = 36; protected static final double CAR_BASELINE = 10; protected static final double CONNECTOR = 6; protected static final double WHEEL_RADIUS = 8; protected static final double WHEEL_INSET = 16; import acm.graphics.*; import java.awt.*; /** This abstract class defines what is common to all train cars */ public abstract class TrainCar extends GCompound { /** * Creates the frame of the car using the specified color. color The color of the new train car */ public TrainCar(Color color) { double xLeft = CONNECTOR; double yBase = -CAR_BASELINE; add(new GLine(0, yBase, CAR_WIDTH + 2 * CONNECTOR, yBase)); addWheel(xLeft + WHEEL_INSET, -WHEEL_RADIUS); addWheel(xLeft + CAR_WIDTH - WHEEL_INSET, -WHEEL_RADIUS); double yTop = yBase - CAR_HEIGHT; GRect r = new GRect(xLeft, yTop, CAR_WIDTH, CAR_HEIGHT); r.setFilled(true); r.setFillColor(color); add(r); } page 2 of 2 skip code

54 Boxcar /** * This class represents a boxcar. Like all TrainCar subclasses, * a Boxcar is a graphical object that you can add to a GCanvas. */ public class Boxcar extends TrainCar { * Creates a new boxcar with the specified color. color The color of the new boxcar public Boxcar(Color color) { super(color); double xRightDoor = CONNECTOR + CAR_WIDTH / 2; double xLeftDoor = xRightDoor - DOOR_WIDTH; double yDoor = -CAR_BASELINE - DOOR_HEIGHT; add(new GRect(xLeftDoor, yDoor, DOOR_WIDTH, DOOR_HEIGHT)); add(new GRect(xRightDoor, yDoor, DOOR_WIDTH, DOOR_HEIGHT)); } /* Dimensions of the door panels on the boxcar */ private static final double DOOR_WIDTH = 18; private static final double DOOR_HEIGHT = 32;

55 Nesting Compound Objects
Train train = new Train(); train.append(new Engine()); train.append(new Boxcar(Color.GREEN)); train.append(new Caboose()); DrawTrain

56 Train import acm.graphics.*;
/** This class defines a GCompound that represents a train. */ public class Train extends GCompound { /** * Creates a new train that contains no cars. Clients can add * cars at the end by calling append. */ public Train() { /* No operations necessary */ } * Adds a new car to the end of the train. car The new train car public void append(TrainCar car) { double width = getWidth(); double x = (width == 0) ? 0 : width - TrainCar.CONNECTOR; add(car, x, 0);

57 Summary Object decomposition is important design strategy Interfaces:
GCanvas GPoint GDimension GRectangle GMath GTurtle GCompound GObject GPen GLabel GRect GOval GLine GArc GImage GPolygon Interfaces: GFillable GResizable GScalable GContainer GRoundRect G3DRect Object decomposition is important design strategy


Download ppt "Five-Minute Review What are enumerated types? How can we represent enumerations in Java? What is character arithmetic? What is a string, conceptually?"

Similar presentations


Ads by Google