H13-DecompExample.pdf

(97 KB) Pobierz
CS106
J Zelenski
Handout #13
Jan 16, 2008
Procedural decomposition examples
A note about graphics: The graphics library we use in CS106B/X supports simple pen-based
drawing in a Cartesian coordinate system scaled on inches. To familiarize yourself with it, check out
section 5.3 of the reader and peruse the
graphics.h
and
extgraph.h
header files in the reader
appendix or browse the "Documentation" link of our class web site. We will ask you to write some
simple graphics code this quarter but it's not a focus for us, so I'd advise picking up the details on an
as-needed basis. For assignments that use fancy graphics, we'll typically provide the graphics for
you since writing that code can be boring and tedious and we'd rather not bog you down.
This handout is adopted from our old CS106A (in C) course, where we used this as an example for
learning how to decompose a problem. Decomposition in the procedural paradigm means something
slightly different than the decomposition you practiced in the object-oriented world. An object-
oriented program is divided in classes, which then separate their operations into methods. The
approach in a procedural program is to divide the problem into its component tasks, implemented as
separate functions, each of which is potentially further subdivided into smaller functions. For
example, the
usher.cpp
program presented below is defined in terms of boxes, circles, grids, and
triangles. Identifying the general tools is part of the strategy of
bottom-up implementation.
Breaking the program down step by step is the process of
top-down design.
Both strategies are
essential to your success as a programmer.
This example is drawn from Exercise 8 of Chapter 7 of
The Art and Science of C,
which asks you to
draw a diagram of the Edgar Allen Poe's (very scary) House of Usher.
2
The point of this exercise was further practice with the graphics library and an exercise in top-down
design and stepwise refinement. Look the picture, try to decompose the picture into its components,
look for opportunities for unification and come up with a reasonable set of functions that can
accomplish the job.
Use our solution below as an illustrative example of simple, complete C++ program that
demonstrates good design and style. Note the many helper routines and how parameters allow those
helpers to be as general as possible. A good design should make it relatively straightforward to
make modifications — how would you change the program to make the towers taller or add panes to
the windows or change the towers to rounded instead of peaked tops? A clean design should also
allow you to quickly zero in on the troubled area when hunting bugs — if the door was being drawn
in the wrong location, where would you look to try to fix it?
/*
* File: usher.cpp
* ---------------
* Program to draw a representation of the house of Usher.
*/
#include "genlib.h"
#include "graphics.h"
/*
* Constants
* ---------
* The following constants control the sizes of elements in the display.
*/
const double HouseWidth = 1.5;
const double HouseHeight = 2.0;
const double HouseArch = 1.0;
const double TowerWidth = 0.4;
const double TowerHeight = 2.3;
const double TowerArch = 0.6;
const double TotalHouseHeight = (HouseHeight + HouseArch);
const double DoorWidth = 0.3;
const double DoorHeight =.5;
const double DoorArch = .25;
const double WindowLevel = 1.4;
const double WindowSize = 0.3;
/* Function prototypes */
void DrawBox(double x, double y, double width, double height);
void DrawTriangle(double x, double y, double base, double height);
void DrawPeakedBox(double x, double y,
double width, double height, double arch);
void DrawWindow(double x, double y);
void DrawDoor(double x, double y);
void DrawTower(double x, double y);
void DrawMainHouse(double x, double y);
void DrawHouseOfUsher(double x, double y);
3
/* Main program calculates where to place lower left corner so that
* entire house is centered within the graphics window.
*/
int main()
{
double centerX, centerY;
InitGraphics();
centerX = GetWindowWidth()/2;
centerY = GetWindowHeight()/2;
DrawHouseOfUsher(centerX - HouseWidth/2 - TowerWidth,
centerY - TotalHouseHeight/2);
return 0;
}
/*
* Function: DrawHouseOfUsher
* Usage: DrawHouseOfUsher(x, y);
* ------------------------------
* Draws the complete House of Usher with lower left corner at (x, y).
* The left tower is drawn based at (x,y), the main house is drawn right
* next to it, and the right tower is placed on the other side.
*/
void DrawHouseOfUsher(double x, double y)
{
DrawTower(x, y);
DrawMainHouse(x + TowerWidth, y);
DrawTower(x + TowerWidth + HouseWidth, y);
}
/*
* Function: DrawMainHouse
* Usage: DrawMainHouse(x, y);
* ---------------------------
* Draws the main part of the house, including the door and windows.
* door is centered and the two windows are balanced on either side.
* lower left corner of the house will be placed at (x, y).
*/
void DrawMainHouse(double x, double y)
{
DrawPeakedBox(x, y, HouseWidth, HouseHeight, HouseArch);
DrawDoor(x + (HouseWidth- DoorWidth)/2, y);
double windowX = x + HouseWidth/4 - WindowSize/2;
DrawWindow(windowX, y +WindowLevel);
windowX += HouseWidth/2;
DrawWindow(windowX, y + WindowLevel);
}
The
The
4
/*
* Function: DrawTower
* Usage: DrawTower(x, y);
* -----------------------
* Draws a side tower with lower left corner at (x, y). Uses
* the TowerHeight, TowerWidth, and TowerArch constants to set sizes.
*/
void DrawTower(double x, double y)
{
DrawPeakedBox(x, y, TowerWidth, TowerHeight, TowerArch);
}
/*
* Function: DrawDoor
* Usage: DrawDoor(x, y);
* ----------------------
* Draws the door at (x, y). Uses the DoorHeight, DoorWidth, and
* DoorArch constants to set sizes of the various door components.
*/
void DrawDoor(double x, double y)
{
DrawPeakedBox(x, y, DoorWidth, DoorHeight, DoorArch);
}
/*
* Function: DrawWindow
* Usage: DrawWindow(x, y);
* ------------------------
* Draws a single window with the lower left corner at (x, y).
* the WindowSize constant for the window width and height.
*/
void DrawWindow(double x, double y)
{
DrawBox(x, y, WindowSize, WindowSize);
}
Uses
/*
* Function: DrawPeakedBox
* Usage: DrawPeakedBox(x, y, width, height, arch);
* ------------------------------------------------
* Draws a rectangle with a triangular top. The arguments are as
* in DrawBox, with an additional arch parameter indicating the
* height of the triangle. This function is a common element in
* several parts of the picture.
*/
void DrawPeakedBox(double x, double y,
double width, double height, double arch)
{
DrawBox(x, y, width, height);
DrawTriangle(x, y + height, width, arch);
}
5
/*
* Function: DrawBox
* Usage: DrawBox(x, y, width, height);
* ------------------------------------
* This function draws a rectangle of the given width and height with
* its lower left corner at (x, y).
*/
void DrawBox(double x, double y, double width, double height)
{
MovePen(x, y);
DrawLine(width, 0);
DrawLine(0, height);
DrawLine(-width, 0);
DrawLine(0, -height);
}
/*
* Function: DrawTriangle
* Usage: DrawTriangle(x, y, base, height);
* ----------------------------------------
* This function draws an isosceles triangle (i.e., having two equal sides)
* with a horizontal base. The coordinate of the left endpoint of the base
* is (x, y), and the triangle has the indicated base length and height.
* If height is positive, the triangle points upward. If height is
* negative, the triangle points downward.
*/
void DrawTriangle(double x, double y, double base, double height)
{
MovePen(x, y);
DrawLine(base, 0);
DrawLine(-base/2, height);
DrawLine(-base/2, -height);
}
Zgłoś jeśli naruszono regulamin