תכנות מתקדם ב-Java/בנאים/תרגילים
הקשקשן
עריכהבתרגיל זה נלמד להשתמש בבנאים וגם נראה בפעם הראשונה שימוש בממשק הגרפי שמציעה ג'אווה. מטרת התרגיל הוא לבנות תוכנית פשוטה שתדע להציג מצולעים שונים על המסך.
המצולעים
עריכהאת המצולעים שנצייר נייצג בעזרת המחלקה Shape
כל מצולע שנרצה להציג יכיל (לכל הפחות) את הנתונים הבאים:
- מיקומי הקדקודים של המצולע. חישבו על החלון בו נציג את הצורות כמערכת צירים שמתחילה ב-(0,0) ומסתיימת ב-(640,480). את המיקומים של הקדקודים יכילו שני מערכים מטיפוס int, כאשר כל מיקום מורכב מזוג משתנים - מערך אחד יכיל מיקום אנכי והשני יכיל מיקום אפקי. כך, לדוגמה, אם נרצה שהקדקוד הראשון של המצולע יהיה במיקום (100, 100), נכניס לאיבר הראשון במערך הראשון ולאיבר השני במערך השני את הערך 100.
- צבע המצולע. לשם ההצגה נשתמש באובייקט מסוג
Color
. בשביל שנוכל להשתמש בו, נוסיף את הפקודה הבאה בתחילת המחלקה:import java.awt.Color;
- מספר הקדקודים במצולע.
אם כך, שדות אפשריים עבור המחלקה הם:
// Arrays holding vertices locations
private int[] _xLocs;
private int[] _yLocs;
// Number of vertices
private int _vertices;
// Shape color
private Color _col;
ניתן יהיה לאתחל מצולע בכמה צורות:
/**
* Constructor. Create the shape with the default color
* @param xLocs X locations
* @param yLocs Y locations
*/
public Shape(int[] xLocs, int[] yLocs)
/**
* Constructor
* @param xLocs X locations
* @param yLocs Y locations
* @param color Shape color
*/
public Shape(int[] xLocs, int[] yLocs, Color color)
/**
* Copy constructor
* @param other Other shape to copy
*/
public Shape(Shape other)
בנוסף לבנאים, המחלקה Shape
צריכה לממש כמה שיטות פשוטות נוספות:
/**
* @return Shape color
*/
public Color getColor()
/**
* @return X locations
*/
public int[] getXLocations()
/**
* @return Y locations
*/
public int[] getYLocations()
/**
* @return Number of vertices
*/
public int getVertices()
שיטות אלה אינן מבצעות דבר פרט להחזרה של הנתונים המבוקשים, ומטרתן - לשמור על עיקרון הכימוס. דאגו שבמידת הצורך יוחזר עותק של השדה המבוקש, כדי למנוע שינויים לא רצויים.
לוח השרטוט
עריכהכדי לשרטט את הצורות על המסך נשתמש במחלקה הבאה:
import java.awt.*;
import javax.swing.*;
@SuppressWarnings("serial")
public class Animator extends JFrame {
// Prefered window size
private static final int HEIGHT = 640;
private static final int WIDTH = 480;
// Shapes to draw
private Shape[] _shapes;
/**
* Constructor. Creates the window.
* @param shapes Shapes to draw
*/
public Animator(Shape[] shapes) {
super("Animator");
_shapes = shapes;
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBackground(Color.WHITE);
setPreferredSize(new Dimension(HEIGHT, WIDTH));
pack();
setResizable(false);
setVisible(true);
}
/**
* Draws the shapes. Ignores null shapes.
*/
public void paint(Graphics g) {
for(int i=0; i<_shapes.length; i++) {
if(_shapes[i] != null) {
g.setColor(_shapes[i].getColor());
g.drawPolygon(
_shapes[i].getXLocations(),
_shapes[i].getYLocations(),
_shapes[i].getVertices());
}
}
}
}
המחלקות הגרפיות בג'אווה פועלות בצורה מסובכת למדיי, ואין צורך להתעמק ולהבין כיצד מחלקה זו פועלת. באופן כללי, המחלקה שלנו מרחיבה את המחלקה JFrame, ולכן - כאשר יוצרים אובייקט שלה, נוצר חלון. בעזרת השיטה paint ניתן לצייר על החלון שיצרנו. כאמור, אלו נושאים מתקדמים שנראה רק בהמשך, כעת נסתפק בהעתקה של המחלקה בשלמותה ובשימוש בה כ"קופסה שחורה".
שיטות נוספות
עריכהשיטות נוספות שנרצה לממש הן שיטות שיאפשרו שינויים פשוטים בצורות שיצרנו:
/**
* Moves the shape
* @param dx horizontal alignment
* @param dy vertical alignment
*/
public void move(int dx, int dy)
/**
* Sets the shape color
* @param c new color for that shape
*/
public void setColor(Color c)
שיטות אלה יאפשרו לנו להזיז צורה ולשנות את צבעה. הערה: שיטת ההזזה צריכה לקבל שני מספרים שלמים (יכולים להיות שליליים), ולהזיז את הצורה על פיהם. למשל, אם השיטה מקבלת את הארגומנטים 5 ו-10, כל ערכי ה-X צריכים לגדול ב-5, וכל ערכי ה-Y - ב-10.
מימוש
עריכהמשימתנו, אם כך, היא:
- ליצור את המחלקה
Shape
, כפי שתוארה בתחילת הקטע, ובה יהיו:- משתנים שיחזיקו את הנתונים הדרושים. ניתן להשתמש במשתנים שפורטו בתחילת הקטע, אך זה לא הכרחי: כל עוד השיטות הציבוריות יחזירו את הערכים הרצויים - לא משנה באיזה דרך בחרתם להחזיק את הנתונים (כמובן, כל עוד היא לא בלתי-יעילה לחלוטין).
- בנאים - כל שלושת הבנאים שפורטו. הערה: ערך ברירת המחדל של הצבע הוא שחור, שמיוצג באמצעות
Color.BLACK
. אל תשכחו לייבא את המחלקה המתאימה באמצעות השורהimport java.awt.Color;
בתחילת המחלקה Shape. - כל השיטות הציבוריות שפורטו.
- ליצור את המחלקה
ShapeDriver
, שתכיל את ה-main
של התוכנית.- המחלקה תיצור כמה צורות, תכניס אותן למערך, ואז, בהנחה ששם המערך הוא myShapes, תיצור מופע של אובייקט מסוג
Animator
(המחלקה הגרפית) באמצעות השורהnew Animator(myShapes);
.
- המחלקה תיצור כמה צורות, תכניס אותן למערך, ואז, בהנחה ששם המערך הוא myShapes, תיצור מופע של אובייקט מסוג
- בעת הכתיבה, נסו להקפיד על כמה דברים:
- תיעוד נכון - השתמשו ב-Javadoc.
- ממשו את כל השיטות הציבוריות הדרושות. בשעת הצורך השתמשו בשיטות ובמשתנים פרטיים.
- במימוש הבנאים, אל תשכפלו קוד. בשעת הצורך, פנו מבנאי לבנאי. זכרו את הכללים הנוגעים לכך שפורטו בפרק.
- אל תתעלמו ממקרי הקצה; אם משתמש ינסה ליצור צורה לא הגיונית (לדוגמה - ייתן מערכים שהם Null, מערכים שלא תואמים באורכם עבור ערכי ה-X וה-Y, או כל מקרה חריג אחר) - הדפיסו הודעה מתאימה וצאו מהתוכנית.
המחלקה Shape
עריכהimport java.awt.Color;
/**
* Class: Shape.java
*
* Represents a single colored polygon
*/
public class Shape {
// Arrays holding vertices locations
private int[] _xLocs;
private int[] _yLocs;
// Number of vertices
private int _vertices;
// Shape color
private Color _col;
// Default color
private static final Color DEFAULT_COLOR = Color.BLACK;
/**
* Constructor. Create the shape with the default color
* @param xLocs X locations
* @param yLocs Y locations
*/
public Shape(int[] xLocs, int[] yLocs) {
this(xLocs, yLocs, DEFAULT_COLOR);
}
/**
* Constructor
* @param xLocs X locations
* @param yLocs Y locations
* @param color Shape color
*/
public Shape(int[] xLocs, int[] yLocs, Color color) {
if(xLocs == null || yLocs == null)
reportError("Null array");
if(xLocs.length != yLocs.length)
reportError("Non-matching X and Y arrays");
_vertices = xLocs.length;
_xLocs = new int[_vertices];
_yLocs = new int[_vertices];
for(int i=0; i<_vertices; i++) {
_xLocs[i] = xLocs[i];
_yLocs[i] = yLocs[i];
}
_col = color;
}
/**
* Copy constructor
* @param other Other shape to copy
*/
public Shape(Shape other) {
this(other._xLocs, other._yLocs, other._col);
}
/**
* @return Shape color
*/
public Color getColor() {
return _col;
}
// Clone a given integer array
private static int[] cloneArr(int[] arr) {
int[] clone = new int[arr.length];
for(int i=0; i<arr.length; i++) {
clone[i] = arr[i];
}
return clone;
}
/**
* @return X locations
*/
public int[] getXLocations() {
int[] temp = cloneArr(_xLocs);
return temp;
}
/**
* @return Y locations
*/
public int[] getYLocations() {
int[] temp = cloneArr(_yLocs);
return temp;
}
/**
* @return Number of vertices
*/
public int getVertices() {
return _vertices;
}
/**
* Moves the shape
* @param dx horizontal alignment
* @param dy vertical alignment
*/
public void move(int dx, int dy) {
for(int i=0; i<_vertices; i++) {
_xLocs[i]+=dx;
_yLocs[i]+=dy;
}
}
/**
* Sets the shape color
* @param c new color for that shape
*/
public void setColor(Color c) {
_col = c;
}
// Report critical error and quit
private void reportError(String err) {
System.err.println("Critical error: "+err);
System.exit(1);
}
}
המנוע
עריכהimport java.awt.Color;
/**
* Simple driver for the animator program
*
*/
public class ShapeDriver {
public static void main(String[] args) {
int[] xLocs1 = {100, 200, 200, 100};
int[] yLocs1 = {100, 100, 200, 200};
int[] xLocs2 = {50, 0, 100};
int[] yLocs2 = {50, 100, 100};
Shape s1, s2, s3;
s1 = new Shape(xLocs1, yLocs1, Color.BLUE);
s2 = new Shape(s1);
s2.move(100, 100);
s2.setColor(Color.GREEN);
s3 = new Shape(xLocs2, yLocs2);
Shape[] myShapes = {s1, s2, s3};
new Animator(myShapes);
}
}