![]() |
Notes on Programming Style |
A programming task involves many decisions. We get to choose names for variables, parameters, and methods. We figure out what the patterns are and whether or not we create methods for them. A good programming style produces code that is easy for others to understand. Some general guidelines follow:
backgroundColor
and starColor
, which
need no additional documentation since the names are self-explanatory.
It is a Java convention to name things by running
words together and capitalizing the start of words except for the
very first word. If you have programmed in other languages before,
you may be used to using underscores between words like so,
background_color
and star_color
. We don't care which
convention you follow, but you should try to be consistent.
school
, a good name would be isAtSchool()
which
evaluates to true if the buggle is at school and false otherwise.
If we want a method that draws a star, a good name would be
star()
.
a
,
B
, or xyz
. While it may be fun to pick
names like bonkers
to represent a method like
isAtSchool
, our code will be hard to read,
since it's hard to remember what the name stood for. That's why we
should always give names that are meaningful. Other people should
be able to read our names and understand almost instantly what
they stand for.
if (this.getPosition().equals(new Point(4,6))) { this.right(); this.right(); this.right(); this.right(); this.setPosition(new Point(1,1)); }
no one can understand what is going on unless they read each
line of code. Also, it's hard to get the "big picture" of what is
going on. Instead, the following code is much clearer and easy to
understand
// ... Point home = new Point(1,1); Point school = new Point(4,6); if (this.getPosition().equals(school)) { dance(); goHome(); } // ... public void dance() // spin in a circle { this.right(); this.right(); this.right(); this.right(); } public void goHome() { this.setPosition(home); }
Although the second version takes up a more lines of code, it is much easier to read. Programming is an art. We don't want to overdo creating methods, but we need to be willing to use them when it will greatly help our understanding of what is going on.
public class MyBuggleWorld extends BuggleWorld { public void run() { Buggle barb = new Buggle(); barb.setPosition(new Point(4,5)); barb.setHeading(Direction.SOUTH); barb.setColor(Color.cyan); barb.forward(4); barb.setPosition(new Point(2,1)); barb.setHeading(Direction.NORTH); barb.setColor(Color.green); barb.forward(6); barb.setPosition(new Point(5,7)); barb.setColor(Color.red); barb.setHeading(Direction.WEST); barb.forward(3); } }
This works. However, it is better to capture the pattern
involved in drawing a line into a method and to use that method in
our run()
method. So, our new code will create a method
line()
which we will use.
public class MyBuggleWorld extends BuggleWorld { public void run() { LineBuggle barb = new LineBuggle(); // a LineBuggle knows how to draw lines barb.line(new Point(4,5), Direction.SOUTH, Color.cyan, 4); barb.line(new Point(2,1), Direction.NORTH, Color.green, 6); barb.line(new Point(5,7), Direction.WEST, Color.red, 3); } } public class LineBuggle extends Buggle { // draws a line from the specified point, in the specified direction // and color by moving a buggle forward in the specified steps public void line(Point lineStart, Direction lineDirection, Color lineColor, int lineLength) { this.setPosition(lineStart); this.setHeading(lineDirection); this.setColor(lineColor); this.forward(lineLength); } }
By capturing the pattern in a method, it allows us to easily create the pattern over and over again without having to rewrite the same lines of code over and over again. It also makes it easier for people to understand what our code is doing. Most importantly, if we have to change the way the pattern works, we only have to change it in one place and not in all the places the pattern is actually used.
patch()
method that allows us to draw a
square in PictureWorld. Now we want to create a
redGreenFrame
that looks like a four by four grid with red
around the edges and green inside. Let's also assume we have the
fourPics()
method available to us (but not the
fourSame()
or corner()
or
rotations()
methods). How can we do this? Here's one way:public Picture redGreenFrame () { return fourPics(fourPics(patch(Color.red), patch(Color.red), patch(Color.red), patch(Color.green)), fourPics(patch(Color.red), patch(Color.red), patch(Color.green), patch(Color.red)), fourPics(patch(Color.red), patch(Color.green), patch(Color.red), patch(Color.red)), fourPics(patch(Color.green), patch(Color.red), patch(Color.red), patch(Color.red))); }
Yes, that works, but what a mess! It also doesn't make obvious
that all four quadrants are essentially the same pattern rotated
in a different way. We should write the code so that people who
read the code can see the patterns without having to work out
every line of code in their mind. Here's another way to do
this:
public Picture redGreenFrame () { return fourPics(fourPics(patch(Color.red), patch(Color.red), patch(Color.red), patch(Color.green)), flipVertically(fourPics(patch(Color.red), patch(Color.red), patch(Color.red), patch(Color.green))), flipHorizontally(fourPics(patch(Color.red), patch(Color.red), patch(Color.red), patch(Color.green))), flipDiagonally(fourPics(patch(Color.red), patch(Color.red), patch(Color.red), patch(Color.green)))); }
This also works. However,the pattern is still not clear since
people need to check if the same colors are being used in the same
places. What we need to do is give the pattern a name. Here's
another try:
public Picture redGreenFrame () { return fourPics(redGreenCorner(), flipVertically(redGreenCorner()), flipHorizontally(redGreenCorner()), flipDiagonally(redGreenCorner())); } public Picture redGreenCorner () { return fourPics(patch(Color.red), patch(Color.red), patch(Color.red), patch(Color.green)); }
Ok, this works. However, it's really overkill to use a method
here. Methods should really be used to define general
patterns that you use over and over again. By general, we
mean that we should be able to change characteristics of the
pattern like color, width, size, etc. patch is a good
method because we can use it to create patches of any color. A
method that returns a red patch is not a good idea. Instead, we
should be using local variables. Here's our final code for
how to create our redGreenFrame showing good programming
style:
public Picture redGreenFrame () { Picture redPatch = patch(Color.red); // local variable to name the red patch since it is used multiple times Picture greenPatch = patch(Color.green); // optional use of local variable since the green patch is used only once, // but it makes the code easier to read and understand Picture redGreenCorner = fourPics(redPatch,redPatch,redPatch,greenPatch); // local variable to name the pattern return fourPics(redGreenCorner, flipVertically(redGreenCorner), flipHorizontally(redGreenCorner), flipDiagonally(redGreenCorner)); }
Note: What is the difference between
redGreenCorner()
in the next to last sample code and
redGreenCorner
in the last sample? The first is a method
invocation, the second is referencing a local variable.
Don't get confused! If we define methods, even if they don't have
parameters, we must put empty parenthesis after the method name to
use the method. Variables are just names for things and therefore
are never used with parenthesis.
Indent the components of a compound statement. A compound
statement is a statement that contains other statements, like an
if
, while
, for
, or a block
(a group of statements enclosed in curly braces). For example:
but notif (isOverBagel()) numBagels++;
if (isOverBagel()) numBagels++;
If arguments to a method appear on separate lines, line them up:
return fourPics(fourSame(p3), empty(), empty(), sierpinski(4, 200));
Close braces go on a line by themselves, possibly followed by a comment that tells us what they are closing (if it may have scrolled off the screen):
notif (isOverBagel()) { numBagels++; }
if (isOverBagel()) { numBagels++;}
This makes it easier to update code and allows the readers' eyes to
see the region of the program executed when the if
test is true.
can help if the class definition goes over several pages. The reader knows the close curly brace closes the definition ofclass MyClass { . . . } // class MyClass
MyClass
(or is intended to in the advent that a
compiler error suggests something else is going on).
The exception is for else
clauses (and else if
), which are so
common, they are worth paying special attention to:
if (isOverBagel()) { numBagels++; } else { numSpaces++; }
Follow a consistent standard for open braces. There are two accepted standards. Either is fine, but don't mix them, certainly not in the same program! The following rule applies to both standards:
For blocks used as part of a compound statement, the open brace
goes on the end of the line of the introduction of the compound
statement. That is, for an if
, else
,
while
, the code will look like this:
int numBagels = 0; while (!isFacingWall()) { if (isOverBagel()) numBagels++; forward(); } if (isOverBagel()) numBagels++; // Test the last square
For open curly braces that are not starting blocks as part of a compound statement, i.e., for braces the begin a class or method definition, there are two approaches:
This emphasizes that classes and methods are really different things (declarations rather than compound statements with a block). It also provides some visual space between the header and the body of the method or class. And it works nicely when we have more complex definitions that include exceptions: the extra lines are set off from the code body so they obviously belong to the header:public class Magic8Ball extends Applet { private Image testImage; public void init() { testImage = getImage(getDocumentBase(), "pic1.gif"); } public void paint (Graphics g) { g.drawImage(testImage,50,50,this); g.drawString(StringChooser.chooseLine(getCodeBase() + "answers.txt"), 80, 215); } }
public static void copyFile(BufferedReader input, BufferedWriter output) throws IOException { // File manipulation code here }
Note that the above code still leaves the line after the header blank. While programmers often do not do this, you might consider making it a habit. Definitions with many parameters followed closely by lots of code can look pretty dense: allow the reader to process information in manageable chunks.public class Magic8Ball extends Applet { private Image testImage; public void init() { testImage = getImage(getDocumentBase(), "pic1.gif"); } public void paint (Graphics g) { g.drawImage(testImage,50,50,this); g.drawString(StringChooser.chooseLine(getCodeBase() + "answers.txt"), 80, 215); } }