In this lesson we’ll address the issue of encapsulation (encapsulate: “to enclose in or as if in a capsule,” Merriam-Webster). The subject of encapsulation can be addressed at many different levels. In Java, it can refer to breaking systems into packages, packages into objects, objects into properties and methods, methods into sub-methods, etc. In this discussion we are mainly looking at properties and methods.
GitHub repository: Cartesian Plane Part 3
Previous lesson: Cartesian Plane Lesson 2: Drawing the Grid Lines
Pieces and Properties

This figure to the right shows what our final version of the Cartesian Plane graphic might look like before we plot any equations. Let’s break it down into pieces:
grid (includes the property pixels-per-unit)
main window
+-- margins
| +-- top
| +-- right
| +-- bottom
| +-- left
+-- grid lines
+-- axes
+-- tic marks (the lines across the x- and y-axes)
| +-- minor (the shorter lines)
| +-- major (the longer lines)
+-- labels (on the tic marks)
+-- text (in the margins)
+-- top
+-- right
+-- bottom
+-- left
A couple of notes on the above:
- The grid lines, axes, and tic marks could be further broken into horizontal and vertical, but let’s not distinguish between orientations.
- We still need to discuss the parts of the graphic that represent plots, but let’s leave that for later.
- For now, let’s also leave out a discussion of marginal text.
So, leaving out text and plot, we have six categories of beasts to describe. Each category is going to have specific properties. A list of the properties of each category follows:
- General grid properties
- Pixels-per-unit (the grid unit)
- Main window properties
- Width
- Height
- Background color
- Top margin properties
Note: A horizontal margin extends the entire width of the component that contains it (the main window in this case); its width describes the distance between its minimum and maximum y-coordinates. A vertical margin extends the full height of the component that contains it; its width is the distance between its minimum and maximum x-coordinates.- Width
- Background color
- Right margin properties
- Width
- Background color
- Bottom margin properties
- Width
- Background color
- Left margin properties
- Width
- Background color
- Grid line properties
Note 1: horizontal and vertical lines have the same properties (color, weight, and spacing).
Note 2: horizontal lines span the width of the rectangle containing the grid, while vertical lines span the height of the rectangle containing the grid.- Weight (the thickness of the line)
- Color
- Lines per unit
- Draw grid lines (true or false; false means the grid lines won’t be visible)
- Axis properties
Note 1: the horizontal and vertical axes have the same properties (the same color and weight).
Note 2: The horizontal axis spans the width of the rectangle containing the grid, and the vertical axis spans the height of the rectangle containing the grid.- Weight (the thickness of the axis, a.k.a stroke)
- Color
- Minor tic properties
Note: the horizontal and vertical tics have the same properties (color, weight, length, and spacing).- Weight (the thickness of the tic mark)
- Color
- Length
- Marks per unit
- Draw tic marks (true or false; false means the minor tic marks won’t be visible)
- Major tic properties
- Weight (the thickness of the tic mark)
- Color
- Length
- Marks per unit
- Draw tic marks (true or false; false means the major tic marks won’t be visible)
- Label properties
Note 1: For simplicity’s sake, the position of a label will always correspond to the position of a major tic mark; minor tic marks will not be labeled.- Font name
- Font style (bold, italic, plain).
- Font size
- Draw labels (true or false; false means the labels won’t be visible)
Before we start declaring variable names, let’s establish some naming conventions:
Don’t forget!!
Speaking of naming conventions, don’t forget that there are some general Java naming conventions that you should be following. They include:
- Names should not begin with an underscore (_).
- Package names should consist of lowercase characters and underscores.
- Class and interface names should begin with a capital letter.
- Method names should begin with a lowercase character.
- Non-constant variable names should begin with a lowercase letter.
- Constant variable names should be written in uppercase with underscores separating name components (more about “constant variables,” below).
- Variable names associated directly with grid properties (so far, only grid units) begin with grid or GRID.
- Variable names associated with the main window (background color, for example) begin with mw or MW.
- Variable names associated with a margin begin with margin or MARGIN.
- Variable names associated with a specific margin reference their orientation immediately following margin (e.g., marginTop or MARGIN_TOP).
- Variable names associated with minor tic marks begin with ticMinor or TIC_MINOR.
- Variable names associated with major tic marks begin with ticMajor or TIC_MAJOR.
- Variable names associated with grid lines begin with gridLine or GRID_LINE.
- Variable names associated with the x- or y-axis begin with axis or AXIS.
- Variable names associated with labels begin with label or LABEL.
- “Background” is abbreviated bg or BG.
- “Default value” is abbreviated DV.
- “Lines per unit” is abbreviated LPU.
- “Marks per unit” is abbreviated MPU
Default Values; Constant Variables
Each of our properties will require a default value. We will encapsulate each default value in a constant variable. Now, where do we put our constant variable declarations? One common strategy is to declare them in the class that contains their associated properties, so, for example, BOLD, ITALIC, and MONOSPACED are declared in the java.awt.Font class. If you have many such constants associated with properties across many classes, you might also collect all of them in a single class, as with javax.swing.SwingConstants. We will put all of our constants into a class called CPConstants. We’ll introduce many more constants in later lessons and put them all into this class. Since they are constant variables, we’ll follow the convention of giving them all names that consist of nothing but underscores and uppercase characters.
What is a constant variable?
A constant variable is a public class variable (declared static) that cannot be changed after being initialized (declared final). Constant variables serve a special purpose: their values can be extracted from a class without first loading the class (a time-consuming operation).
Are constant variables ever private?
Constants can certainly be private. In fact, private constants are an excellent idea since they can be used to replace so-called “magic numbers.” For example, instead of writing code like this:
renderAt( 3.3 )
You can write more readable code like this:
private static final float MAXIMUM_PSI = 3.3;
renderAt( MAXIMUM_PSI )
But if it’s private, is it a constant variable with a name spelled in uppercase? Well, if it’s private, it makes no sense to be able to extract its value from a class without first loading the class. I tell my students if it’s public, static, and final, it’s a constant variable and must be spelled in uppercase. If it’s private and feels like a constant (MAXIMUM_PSI, for instance), spell it in uppercase, but feel free to follow the naming convention for non-constant variables.
One more thing about the default values: they will all be implemented as Strings. For example, the default value for font size, a float value, will be implemented as:
String LABEL_FONT_SIZE_DV = "8";
This may initially seem non-intuitive, but it will make more sense later when we extend our treatment of default values to places like property files, command line arguments, and environment variables. The CPConstants class will contain class methods for converting strings to appropriate types. Here are the string-to-X conversion methods.
1 2 3 4 5 6 7 8 9 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 96 97 98 99 100 101 102 103 | /** * Convert a String to an int and return the int. * * @param sVal the String to convert * * @return the converted int * * @throws NumberFormatException if sVal * cannot be converted to an int */ public static int asInt( String sVal ) { int iVal = Integer.parseInt( sVal ); return iVal; } /** * Convert a String to an float and return the float. * * @param sVal the String to convert * * @return the converted float * * @throws NumberFormatException if sVal * cannot be converted to a float */ public static float asFloat( String sVal ) { float fVal = Float.parseFloat( sVal ); return fVal; } /** * Convert a String to a boolean and return the boolean. * The operation is case-insensitive. * Any value other than "true" is converted to false. * * @param sVal the String to convert * * @return the converted boolean */ public static boolean asBoolean( String sVal ) { boolean bVal = Boolean.parseBoolean( sVal ); return bVal; } /** * Convert a String to a Color and return the Color. * The String must be encoded as an integer value. * Decimal integer and Hexadecimal integer values * are accepted. * (A hexadecimal string value begins with "0x" or "#".) * * @param sVal the String to convert * * @return the converted Color * * @throws NumberFormatException if sVal * cannot be converted to an integer */ public static Color asColor( String sVal ) { int iVal = Integer.decode( sVal ); Color cVal = new Color( iVal ); return cVal; } /** * Convert a String to a font style and return the result. * Integer values for font styles are defined in the Font class. * Input is case-insensitive; valid values are * PLAIN, BOLD and ITALIC. * * @param sVal the String to convert * * @return the converted Color * * @throws IllegalArgumentException if sVal * cannot be converted to a font style. */ public static int asFontStyle( String sVal ) { String cisVal = sVal.toUpperCase(); int iVal = -1; switch ( cisVal ) { case "PLAIN": iVal = Font.PLAIN; break; case "BOLD": iVal = Font.BOLD; break; case "ITALIC": iVal = Font.ITALIC; break; default: String err = "\"" + sVal + "\"" + "is not a valid font style"; throw new IllegalArgumentException( err ); } return iVal; } |
And here are the (many) default value declarations.
1 2 3 4 5 6 7 8 9 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 96 | ///////////////////////////////////////////////// // General grid properties ///////////////////////////////////////////////// /** Grid units (pixels-per-unit) default value: float. */ public static final String GRID_UNIT_DV = "65"; ///////////////////////////////////////////////// // Main window properties ///////////////////////////////////////////////// /** Grid units (pixels-per-unit) default value: float. */ /** Main window width default value: int. */ public static final String MW_WIDTH_DV = "500"; /** Main window height default value: int. */ public static final String MW_HEIGHT_DV = "500"; /** Main window background color default value: int. */ public static final String MW_BG_COLOR_DV = "0xE6E6E6"; ///////////////////////////////////////////////// // Margin properties ///////////////////////////////////////////////// /** Top margin width default value: float. */ public static final String MARGIN_TOP_WIDTH_DV = "20"; /** Top background color default value: int. */ public static final String MARGIN_TOP_BG_COLOR_DV = "0x008080"; /** Right margin width default value: float. */ public static final String MARGIN_RIGHT_WIDTH_DV = "20"; /** Right margin background color: int. */ public static final String MARGIN_RIGHT_BG_COLOR_DV = "0x008080"; /** Bottom margin width default value: float. */ public static final String MARGIN_BOTTOM_WIDTH_DV = "60"; /** Bottom margin background color: int. */ public static final String MARGIN_BOTTOM_BG_COLOR_DV = "0x008080"; /** Left margin width default value: float*/ public static final String MARGIN_LEFT_WIDTH_DV = "60"; /** Left margin background color: int. */ public static final String MARGIN_LEFT_BG_COLOR_DV = "0x008080"; ///////////////////////////////////////////////// // Tic mark properties ///////////////////////////////////////////////// /** Minor tic mark color default value default value: int. */ public static final String TIC_MINOR_COLOR_DV = "0X000000"; /** Minor tic mark weight default value: float. */ public static final String TIC_MINOR_WEIGHT_DV = "3"; /** Minor tic mark length default value: float. */ public static final String TIC_MINOR_LEN_DV = "7"; /** Minor tic marks per unit default value: float. */ public static final String TIC_MINOR_MPU_DV = "10"; /** Draw minor tic marks default value: boolean */ public static final String TIC_MINOR_DRAW_DV = "true"; /** Minor tic mark color default value: int. */ public static final String TIC_MAJOR_COLOR_DV = "0X000000"; /** Major tic mark weight default value: float. */ public static final String TIC_MAJOR_WEIGHT_DV = "3"; /** MAJOR tic mark length default value: float. */ public static final String TIC_MAJOR_LEN_DV = "7"; /** Major tic marks per unit default value: float. */ public static final String TIC_MAJOR_MPU_DV = "1"; /** Draw major tic marks default value: boolean */ public static final String TIC_MAJOR_DRAW_DV = "true"; ///////////////////////////////////////////////// // Grid line properties ///////////////////////////////////////////////// /** Grid line weight default value: float. */ public static final String GRID_LINE_WEIGHT_DV = "1"; /** Grid lines per unit default value: float. */ public static final String GRID_LINE_LPU_DV = TIC_MAJOR_MPU_DV; /** Left margin background color: int. */ public static final String GRID_LINE_COLOR_DV = "0x4B4B4B"; /** Draw grid lines default value: boolean */ public static final String GRID_LINE_DRAW_DV = "true"; ///////////////////////////////////////////////// // Axis properties ///////////////////////////////////////////////// /** Axis color default value: int. */ public static final String AXIS_COLOR_DV = "0X000000"; /** Axis weight default value: float. */ public static final String AXIS_WEIGHT_DV = "3"; ///////////////////////////////////////////////// // Label properties ///////////////////////////////////////////////// /** Label font color default value: int. */ public static final String LABEL_FONT_COLOR_DV = "0X000000"; /** Label font name default value: String. */ public static final String LABEL_FONT_NAME_DV = "Monospaced"; /** * Label font style default value: String. * One of the Font class constants: * BOLD, ITALIC or PLAIN */ public static final String LABEL_FONT_STYLE_DV = "PLAIN"; /** Label font size default value: float. */ public static final String LABEL_FONT_SIZE_DV = "8"; |
Implementing the Properties
So, almost all of our properties will be implemented as instance variables. The two exceptions are the main window default width and height. These are only used in a constructor and never changed. We’ll implement them as class variables. Speaking of constructors, we will also add a default constructor that uses the default values (the default constructor is a constructor with no parameters; it is also called a no-argument constructor because you don’t have to provide an argument when invoking it). Here are the declarations and the new constructor.
1 2 3 4 5 6 7 8 9 10 11 | private static final int mainWindowWidthDV = CPConstants.asInt( CPConstants.MW_WIDTH_DV ); private static final int mainWindowHeightDV = CPConstants.asInt( CPConstants.MW_HEIGHT_DV ); // ... public CartesianPlane() { this( mainWindowWidthDV, mainWindowHeightDV ); } // ... } |
Note that the new constructor makes use of constructor chaining. The code: this( mainWindowWidthDV, mainWindowHeightDV );
causes the overloaded constructor CartesianPlane(int, int) to be invoked. Constructor chaining is a convenient way to avoid writing and maintaining the same code in many constructors. If you use constructor chaining, the invocation of this(…) must be the first line of code in the constructor. Here’s a slightly more complex example of constructor chaining:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | public class SmartRect { private static final Color DEFAULT_COLOR = Color.BLUE; private static final float DEFAULT_WIDTH = 127.3f; private static final float DEFAULT_HEIGHT = 99.6f; private final Color color; private final float width; private final float height; public SmartRect() { this( DEFAULT_COLOR, DEFAULT_WIDTH, DEFAULT_HEIGHT ); } public SmartRect( Color color ) { this( color, DEFAULT_WIDTH, DEFAULT_HEIGHT ); } public SmartRect( float width, float height ) { this( DEFAULT_COLOR, width, height ); } public SmartRect( Color color, float width, float height ) { this.color = color; this.width = width; this.height = height; } // ... } |
Next come the declarations of the instance variables to hold all the other properties:
1 2 3 4 5 6 7 8 9 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 | ///////////////////////////////////////////////// // Main window properties // Note: "width" and "height" are included as // main window properties in CPConstants, // but it is not necessary to encapsulate // their values in instance variables. // See the default constructor. ///////////////////////////////////////////////// private Color mwBGColor = CPConstants.asColor( CPConstants.MW_BG_COLOR_DV ); ///////////////////////////////////////////////// // Margin properties ///////////////////////////////////////////////// private float marginTopWidth = CPConstants.asFloat( CPConstants.MARGIN_TOP_WIDTH_DV ); private Color marginTopBGColor = CPConstants.asColor( CPConstants.MARGIN_TOP_BG_COLOR_DV ); private float marginRightWidth = CPConstants.asFloat( CPConstants.MARGIN_RIGHT_WIDTH_DV ); private Color marginRightBGColor = CPConstants.asColor( CPConstants.MARGIN_RIGHT_BG_COLOR_DV ); private float marginBottomWidth = CPConstants.asFloat( CPConstants.MARGIN_BOTTOM_WIDTH_DV ); private Color marginBottomBGColor = CPConstants.asColor( CPConstants.MARGIN_BOTTOM_BG_COLOR_DV ); private float marginLeftWidth = CPConstants.asFloat( CPConstants.MARGIN_LEFT_WIDTH_DV ); private Color marginLeftBGColor = CPConstants.asColor( CPConstants.MARGIN_LEFT_BG_COLOR_DV ); ///////////////////////////////////////////////// // Tic mark properties ///////////////////////////////////////////////// private Color ticMinorColor = CPConstants.asColor( CPConstants.TIC_MINOR_COLOR_DV ); private float ticMinorWeight = CPConstants.asFloat( CPConstants.TIC_MINOR_WEIGHT_DV ); private float ticMinorLen = CPConstants.asFloat( CPConstants.TIC_MINOR_LEN_DV ); private float ticMinorMPU = CPConstants.asFloat( CPConstants.TIC_MINOR_MPU_DV ); private boolean ticMinorDraw = CPConstants.asBoolean( CPConstants.TIC_MINOR_DRAW_DV ); private Color ticMajorColor = CPConstants.asColor( CPConstants.TIC_MAJOR_COLOR_DV ); private float ticMajorWeight = CPConstants.asFloat( CPConstants.TIC_MAJOR_WEIGHT_DV ); private float ticMajorLen = CPConstants.asFloat( CPConstants.TIC_MAJOR_LEN_DV ); private float ticMajorMPU = CPConstants.asFloat( CPConstants.TIC_MAJOR_MPU_DV ); private boolean ticMajorDraw = CPConstants.asBoolean( CPConstants.TIC_MAJOR_DRAW_DV ); ///////////////////////////////////////////////// // Grid line properties ///////////////////////////////////////////////// private Color gridLineColor = CPConstants.asColor( CPConstants.GRID_LINE_COLOR_DV ); private float gridLineWeight = CPConstants.asFloat( CPConstants.GRID_LINE_WEIGHT_DV ); private float gridLineLPU = CPConstants.asFloat( CPConstants.GRID_LINE_LPU_DV ); private boolean gridLineDraw = CPConstants.asBoolean( CPConstants.GRID_LINE_DRAW_DV ); ///////////////////////////////////////////////// // Axis properties ///////////////////////////////////////////////// private Color axisColor = CPConstants.asColor( CPConstants.AXIS_COLOR_DV ); private float axisWeight = CPConstants.asFloat( CPConstants.AXIS_WEIGHT_DV ); ///////////////////////////////////////////////// // Label properties (these are the labels that // go on the x- and y-axes, e.g., 1.1, 1.2) ///////////////////////////////////////////////// private Color labelFontColor = CPConstants.asColor( CPConstants.LABEL_FONT_COLOR_DV ); private String labelFontName = CPConstants.LABEL_FONT_NAME_DV; private int labelFontStyle = CPConstants.asFontStyle( CPConstants.LABEL_FONT_STYLE_DV ); private float labelFontSize = CPConstants.asFloat( CPConstants.LABEL_FONT_SIZE_DV ); |
Occasionally, our users will want to ask what the current value of a property is or change the value of a property. Since our instance variables are private (as they should be!), we will need methods to provide access to them. By convention, a setter ( a.k.a mutator) is a method used to change a property value, and a getter ( a.k.a accessor) is a method used to get the value. The format of setters and getters follows a simple pattern. Given a property prop of a given type (int, float, etc.) a setter has this declaration:
public void setProp( type prop )
and a getter looks like this:
public type getProp()
Here are examples based on our top margin width property:
1 2 3 4 5 6 7 8 9 | public float getMarginTopWidth() { return marginTopWidth; } public void setMarginTopWidth(float marginTopWidth) { this.marginTopWidth = marginTopWidth; } |
Exceptions to the pattern can be made for boolean property getters. While the getProperty pattern is acceptable, you can substitute is for get. Here are the setter and getter for one of our boolean properties.
1 2 3 4 5 6 7 8 9 | public boolean isGridLineDraw() { return gridLineDraw; } public void setGridLineDraw(boolean gridLineDraw) { this.gridLineDraw = gridLineDraw; } |
A couple of more notes about properties:
Note 1: Not every property is encapsulated in an instance variable.
While it is true that every property in this project (at least so far) is associated with an instance variable, that is not always true. For example, area and perimeter are properties of a rectangle, but they are not typically stored in variables:
1 2 3 4 5 6 7 8 9 10 11 | public float getArea() { float area = width * height; return area; } public float getPerimeter() { float perimeter = 2 * width + 2 * height; return perimeter; } |
Note 2: Not every property has a getter and a setter.
Some read-only properties have only getters (such as area and perimeter in the above example). It is also possible (if rare) to have a write-only property that has a setter but no getter.
Note 3: Not every instance (or class) variable represents a public property.
Some of the instance variables we have so far, for example, currWidth and currHeight, exist purely for the convenience of our code and are of no interest to our users.
Note 4: Completing the setter/getter pattern.
There’s one more bit about setters and getters that we haven’t discussed: setters and getters for properties implemented as arrays. Such properties often have two pairs of setters and getters: one pair to set/get the entire array and another to set/get a single element. The setter for an array element has a second parameter that describes the index of the element to set; the getter has a parameter that describes the element to get. Here is an example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | private double[] results; public double[] getResults() { return results; } public void setResults(double[] results) { this.results = results; } public double getResult( int index ) { return results[index]; } public void setResult( double value, int index ) { results[index] = value; } |
If you’re not looking forward to writing the setter and getter for every one of our properties, you’re in luck. Chances are your IDE will do it for you. If you’re using Eclipse, for instance, pull down the source menu and select Generate Setters and Getters. In the resulting dialog, choose the variables for which you want setters and getters; if you wish, for a given variable you can select just a setter or just a getter. I also suggest you check the Generate Method Comments toggle button; we haven’t started talking about documentation yet, but once we do this will save a lot of typing. When you’re ready, push the generate button.

Here are some of the results for our Cartesian plane project.
1 2 3 4 5 6 7 8 9 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 | /** * @return the ticMajorColor */ public Color getTicMajorColor() { return ticMajorColor; } /** * @param ticMajorColor the ticMajorColor to set */ public void setTicMajorColor(Color ticMajorColor) { this.ticMajorColor = ticMajorColor; } /** * @return the ticMajorWeight */ public float getTicMajorWeight() { return ticMajorWeight; } /** * @param ticMajorWeight the ticMajorWeight to set */ public void setTicMajorWeight(float ticMajorWeight) { this.ticMajorWeight = ticMajorWeight; } /** * @return the ticMajorLen */ public float getTicMajorLen() { return ticMajorLen; } /** * @param ticMajorLen the ticMajorLen to set */ public void setTicMajorLen(float ticMajorLen) { this.ticMajorLen = ticMajorLen; } /** * @return the ticMajorMPU */ public float getTicMajorMPU() { return ticMajorMPU; } /** * @param ticMajorMPU the ticMajorMPU to set */ public void setTicMajorMPU(float ticMajorMPU) { this.ticMajorMPU = ticMajorMPU; } /** * @return the ticMajorDraw */ public boolean isTicMajorDraw() { return ticMajorDraw; } /** * @param ticMajorDraw the ticMajorDraw to set */ public void setTicMajorDraw(boolean ticMajorDraw) { this.ticMajorDraw = ticMajorDraw; } |
Breaking a Task into Manageable Units
You never want to mash hundreds (or tens or thousands) of lines of code into a small space to accomplish multiple tasks. Suppose you go to your paintComponent method and try to write one long stream of code to draw every bit of your plane. Afterward, you find it doesn’t work quite right. Do you have an error in your code to draw the grid lines? Or maybe the tic marks? If your labels don’t look right, how are you going to refine the label drawing code if it’s mashed together with all the rest of your code?

We’ve already encapsulated some of our tasks in helper methods, such as drawing the grid lines and painting the margins. Eventually, we will also break out the code for drawing the axes, tic marks, labels, and (several lessons from now) the user’s graphs. For now, let’s write the code for drawing the axes (at last! some visible progress!). We must also adapt the existing code for drawing grid lines and painting margins to use our new variable names. The code to do that follows; the result can be seen in the figure at right.
Note: It’s a common saying among object-oriented programmers that “a class should do one thing and do it well.” But that can be applied to every other level of programming, too. A system should do one thing and do it well; a package should do one thing and do it well; a method, even a simple loop should do one thing and do it well.
1 2 3 4 5 6 7 8 9 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 | public void paintComponent( Graphics graphics ) { // ... drawGrid(); drawAxes(); paintMargins(); // ... } private void drawGrid() { gtx.setColor( gridLineColor ); gtx.setStroke( new BasicStroke( gridLineWeight ) ); float gridSpacing = gridUnit / gridLineLPU; float numLeft = (float)Math.floor( gridWidth / 2 / gridSpacing ); float leftXco = centerXco - numLeft * gridSpacing; for ( float xco = leftXco ; xco <= maxXco ; xco += gridSpacing ) { Line2D gridLine = new Line2D.Float( xco, minYco, xco, maxYco ); gtx.draw( gridLine ); } float numTop = (float)Math.floor( gridHeight / 2f / gridSpacing ); float topYco = centerYco - numTop * gridSpacing; for ( float yco = topYco ; yco <= maxYco ; yco += gridSpacing ) { Line2D gridLine = new Line2D.Float( minXco, yco, maxXco, yco ); gtx.draw( gridLine ); } } private void drawAxes() { gtx.setColor( axisColor ); gtx.setStroke( new BasicStroke( axisWeight ) ); Line2D line = new Line2D.Float(); // x axis line.setLine( centerXco, minYco, centerXco, maxYco ); gtx.draw( line ); // y axis line.setLine( minXco, centerYco, maxXco, centerYco ); gtx.draw( line ); } private void paintMargins() { Rectangle2D rect = new Rectangle2D.Float(); // top margin rect.setRect( 0, 0, currWidth, marginTopWidth ); gtx.setColor( marginTopBGColor ); gtx.fill( rect ); // right margin float marginRightXco = currWidth - marginRightWidth; rect.setRect( marginRightXco, 0, marginRightWidth, currHeight ); gtx.setColor( marginRightBGColor ); gtx.fill( rect ); // bottom margin float marginBottomXco = currHeight - marginBottomWidth; rect.setRect( 0, marginBottomXco, currWidth, marginBottomWidth ); gtx.setColor( marginBottomBGColor ); gtx.fill( rect ); // left margin rect.setRect( 0, 0, marginLeftWidth, currHeight ); gtx.setColor( marginLeftBGColor ); gtx.fill( rect ); } |
Summary
In this lesson, we attempted to identify some of our project’s individual properties and tasks. Each property is now encapsulated in a named variable and associated setters and getters. Many tasks were described at a low level (draw axes, draw tic marks, etc.) and encapsulated in dedicated methods. In the coming lessons we’ll spend a lot more time trying to encapsulate individual properties and tasks, and other features of our project, such unit tests.
In our next lesson, we’ll make a little more visible progress as we implement the code to draw more components of our grid.