Graphic by Keith Ohlfs |
Notes on Programming Style |
[CS111 Home Page] [Syllabus] [Lecture Notes] [Assignments] [Labs] [Programs] [Documentation] [Software Installation] [FAQ] [CS Dept.] [CWIS]
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 which is easy for others to understand. Some general guidelines follow:
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 programming style takes up a lot 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 write
lines of code. 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.
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.