powerpoint - cs.bgu.ac.ilintro111/wiki.files/class17_111.pdfבשחמה יעדמל אובמ...

33
מבוא למדעי המחשב הורשהInheritance Everything the light touches, Simba, will be yours

Upload: danghuong

Post on 02-May-2018

216 views

Category:

Documents


0 download

TRANSCRIPT

מבוא למדעי המחשב

Inheritance –הורשה

Everything the light touches, Simba, will be yours

מבוא למדעי המחשב

Inheritance –הורשה

:כללי בשיעור הקודם השתמשנו במושג הממשק על

מנת לגרום לאלגוריתם שפיתחנו לעבוד על

.מגוון רחב יותר של עצמים

מנגנון –בשיעור זה נכיר את מנגנון ההורשה

נוסף המאפשר התייחסות אחידה לעצמים

.ממחלקות שונות

נעמוד על הדמיון וההבדלים בין שני המנגנונים

.ל"הנ

class Student public class Student {

private static final int COURSE_PRICE = 1000;

private String name;

private int id;

private int numOfCourses;

public Student(int id, String name) {

this.name = name;

this.id = id;

numOfCourses= 0;

}

public int computeTuitionFee(){

return numOfCourses * COURSE_PRICE;

}

class Student public String getName() {return name;}

public int getID() {return id;}

public int getNumOfCourses() {

return numOfCourses;

}

public void setNumOfCourses(int numOfCourses)

{

this.numOfCourses = numOfCourses;

}

class Student public boolean equals(Object other) {

return ((other instanceof Student) &&

(id == ((Student)other).id));

}

public String toString() {

return "Student name: " + getName()

+ ", ID:" + getID()

+ ", No. of courses: " + getNumOfCourses();

}

} //class Student

:דוגמה

בתוכנית שלנו אנו משתמשים מחלקהStudent .

public intהתוכנית משתמשת בשיטה computeTuitionFee() בכדי לחשב את שכר

כפונקציה של מספר , הלימוד שהסטודנט צריך לשלם

. הקורסים שלו

:דוגמה

נניח כי אנו מעונינים ליצור מחלקה דומה , עתה

. סטודנט מילגאי -במקרה שלנו , למחלקה המקורית

למחלקה החדשה יש הרבה מן המשותף עם המחלקה

וגם לו מוגדרת כמות , .ז.גם למילגאי שם ות: המקורית

למילגאי נוספת גם , מאידך. הקורסים אותם הוא לוקח

ותהליך חישוב שכר , גובה המילגה אותה הוא מקבל

הלימוד שלו מעט שונה כי הוא מקבל את המלגה

.כהנחה על שכר הלימוד

Studentשינוי –אפשרות ראשונה

לשנות את המחלקה אנו יכוליםStudent . ניתן

במקרה של 0)להוסיף לכל סטודנט את המילגה שלו

public intונוסיף את השיטה ( סטודנט לא מילגאיgetMilga(); המחזירה את המילגה של הסטודנט.

Studentשינוי –אפשרות ראשונה :לגישה זו מספר חסרונות

ולקמפל , לשנות את המחלקה המקוריתיש צורך

אם יש שינוי . מחדש את כל הקוד המתאים לה

יש לעדכן גם את כל המחלקות – בחתימת הבונה

.Studentהמשתמשות במחלקה

וכל קוד חלק ניכר מהסטודנטים אינם מלגאים

, למרות זאת. המטפל במלגאים כלל לא נוגע להם

במחלקה המייצגת אותם תוקדש תשומת לב רבה

.לטיפול בנושא

המייצג סטודנט מלגאי שם הטיפוס היינו מעונינים ש

. המושג מלגהיזכיר את

יצירת מחלקה חדשה –אפשרות שנייה עבור מלגאי לבנות מחלקה חדשה :

class Milgay{

// cut & paste Student, add more methods, and

// change existing methods

}

חסרונות:

כל עדכון שנירצה לבצע בעתיד במחלקה , בנוסף. שכפול קוד

Student נאלץ לבצע פעמיים.

לא ניתן ליצור מצביע כללי עבור אובייקט שהוא או סטודנט

ואז חזרנו )אלא אם מגדירים ממשק משותף , או מילגאי

יש צורך לשנות ולקמפל מחדש קוד –לבעיה הראשונה

(.קיים

:פתרון בעזרת הורשה

:Studentאת המחלקה תירש Milgayמחלקה

Milgay את ההתנהגות של " במתנה"תקבלStudent – ניתן

Studentלחשוב על כך כאילו שהשיטות הציבוריות של

ולכן ניתן לאמר כי ) Milgayי "מגדירות ממשק אשר ממומש ע

–" כל מלגאי הוא גם סטודנט"

" Every Milgay IS A Student .")

עם זאת ,Milgay גם מקבלת את מימוש ההתנהגות במתנה

.אין צורך לכתוב שנית את קוד השיטות המשותפות –

:פתרון בעזרת הורשה

:Studentאת המחלקה תירש Milgayמחלקה

ניתן להוסיף ל-Milgay וכן ניתן , שדות ושיטות נוספים

ולממש אותן , Studentשיטות של ( Overriding" )לדרוס"

-ו equalsבדומה למה שראינו לגבי דריסה של )באופן שונה

toString של המחלקהObject)

:המימוש הטכני

public class Milgay extends Student {

private int milga;

ניתן . את המחלקה סטודנט" מרחיבה"המחלקה מילגאי

". בתוך כל מילגאי נמצא סטודנט קטן: "לחשוב על זאת כך

.היחודי רק לו milgaלמלגאי יש שדה , בנוסף

:המימוש הטכני

בשביל . יש לבנות גם את הסטודנט שבתוכו, כאשר בונים מלגאי חדש

:(…)super נשתמש בצורה, להפעיל בנאי של מחלקה ממנה ירשנו

public Milgay(int id, String name, int

milga){

super(id, name);

this.milga= milga;

}

בדומה ל-this)…( ,קריאה ל-super)…( חייבת להיות הפקודה

אם לא ". מחלקת האב"ניתן להפעיל כל בנאי של . הראשונה בבנאי

ואם )תתבצע קריאה לבנאי ללא פרמטרים , כתבנו זאת באופן מפורש

!(.שגיאת קומפילציה –לא קיים כזה בנאי

:המימוש הטכני

public int getMilga() {

return milga;

}

השיטהgetMilga() היא יחודית למלגאים בלבד .

שאר השיטות של סטודנט אין צורך לכתוב את(getName() ,וכו' )

ברצוננו לעדכן , מאידך. הן יתווספו באופן אוטומטי –במחלקה מלגאי ניתן . בכדי שתתאים למלגאי ()computeTuitionFeeהשיטה

כפי שעשינו , (Overriding" )הדריסה"לעשות זאת בעזרת מנגנון

.toString-ו equalsבעבר עם השיטות

ניסיון , שמעצם שמו)ניסיון ראשון

( :! כנראה עתיד להיכשל, ראשון

// this is the wrong first attempt that fails

public int computeTuitionFee(){

return numOfCourses * COURSE_PRICE - milga;

}

אין גישה למשתנים הפרטיים Milgayבמחלקה ? מה הבעיה

numOfCourses ו-COURSE_PRICE של המחלקהStudent.

:פתרון

public int computeTuitionFee(){

return Math.max(0,

super.computeTuitionFee() – milga);

}

} //class Milgay

באותו אופן כמו ש-this מצביע על האובייקט הקורא לשיטה ,super

superבדוגמה הנוכחית . אשר ממנו ירשנו" אבייקט האב"מצביע על

ניתן עתה לפנות לשיטות . מצביע על הסטודנט הקטן בתוך המלגאי

.ולמשתנים הציבוריים שלו

נשים לב כי הקריאהsuper.computeTuitionFee() מפעילה את

.Studentהקוד הכתוב במחלקה

דריסת שיטה של האב ()public int computeTuitionFeeנתבונן בשיטה

Milgayאך במימוש המחלקה , Studentהשיטה קיימת במחלקה

המימוש . שיטה זו וסיפקנו מימוש אלטרנטיבי (override)דרסנו טיפוס הוא זה שיופעל כאשר Milgayהחדש הכתוב במחלקה

.המפעיל את השיטה הוא מילגאי האובייקט

חייבת Milgayהשיטה המופיעה במחלקה חתימתשימו לב כי כי אחרת לא נקבל Studentלזו המופיעה במחלקה זההלהיות .דריסה אלא שתי שיטות נפרדות

"מודל הבצל"

Student getName(), computeTuitionFee(), addCourse(), getID(), etc…

Milgay getMilga(), computeTuitionFee()

Object toString(), equals(…), etc…

לשיטות כאשר פונים של ציבוריות לא סטטיות

אזי הקוד שיבוצע , אובייקטיםבזמן ריצה ולפי טיפוס נקבע

ולא לפי ( האובייקט בפועלמבחוץ ", )טיפוס המצביע

".פנימה

ניסיון להפעיל את , לכןעל ()getNameהשיטה

אובייקט מסוג מילגאי יגרום להפעלת השיטה הכתובה

לעומת . Studentבמחלקה נסיון להפעיל את , זאת

computeTuitionFee() יפעיל .Milgayאת הקוד במחלקה

מוטיבציה להורשה עץ )הורשה מגדירה יחס היררכי בין המחלקות השונות

Objectבראש ההיררכיה נמצאת המחלקה (. מחלקות

אם אנו מגדירים מחלקה שאינה מרחיבה אף -כלומר )

(.extends Objectפרוש הדבר הוא שהיא , מחלקה אחרת

Object

Point String Student ...

Milgay

הורשה מול ממשקיםהורשה מאפשרת להתייחס למופעים , בדומה לממשקים

כאשר מעונינים לפעול על , ממחלקות שונות באופן אחיד

יכול Xעל אובייקט ממחלקה . המכנה המשותף שלהם

על המסלול "מחלקה Y-כך ש, Yלהצביע מצביע מכל טיפוס

על אוביקט מהמחלקה , לדוגמה. Object-ל X-מ" בעץ

Milgay יכולים להצביע מצביעים מהטיפוסיםMilgay ,

Student ,ו-Object.

בשונה ממשקים,

כלומר שימוש , במימוש ההתנהגותהורשה מאפשרת שיתוף גם

.חוזר בקוד קיים

בעוד שהיא , לרשת מחלקה אחת בלבדמחלקה מסוימת יכולה

.מספר ממשקיםיכולה לממש

הפעלת שיטה שנדרסה

המקרה הפשוט

public static void main(String[] args) {

Milgay m = new Milgay(11,"Moshe", 1000);

m.setNumOfCourses(2);

System.out.println("The milga of " + m.getName()

+ ” is " + m.getMilga());

System.out.println("The TF of “ + m.getName()

+ ” is " + m.computeTuitionFee());

במקרה זה אנו יודעים כי השיטהcomputeTuitionFee של המחלקה

.מלגאי היא שתרוץ

Student s1 = new Milgay(11,"Dani", 500);

s1.setNumOfCourses(2);

System.out.println("The TF of s1 is " +

s1.computeTuitionFee());

טיפוס המצביע הואStudent , בעוד שטיפוס האובייקט בפועל הוא

Milgay . השיטה !!!( ורק עבורן) שיטות ציבוריות לא סטטיותעבור

.לפי טיפוס האובייקט בפועל, המופעלת נקבעת בזמן ריצה

הפעלת שיטה שנדרסה

המקרה המסובך

הפעלת שיטה יחודית למילגאי

Student s1 = new Milgay(11,"Dani", 500);

s1.setNumOfCourses(2);

System.out.println("The milga of s1 is “

+ s1.getMilga());

לא עבר

הפעלת שיטה יחודית למילגאי

(מתוקנת)

Student s1 = new Milgay(11,"Dani", 500);

s1.setNumOfCourses(2);

System.out.println("The milga of s1 is “

+ ((Milgay) s1).getMilga());

עבר

"דוד, הם עבדו עלי"

Student s2 = new Student(11,"Dani");

s2.setNumOfCourses(2);

System.out.println("The milga of (the non-milgay)

s2 is " + ((Milgay) s2).getMilga());

המשמעות שלCasting היא שהמתכנת לוקח על עצמו את האחריות לתקינות

.הטיפוסים

התרסק

public class Point{

private double x;

private double y;

public void printPoint1(){

System.out.println("(" + x + "," +

y + ")");

}

public static void printPoint2(Point p){

System.out.println("(" + p.x + "," +

p.y + ")");

}

} //class Point 28

סטטי-סטטי מול לא

ולכן כאשר מבצעים , אינן משויכות לאובייקט מסוים פרוצדורות סטטיות•קריאה לפרוצדורה כזו יש צורך להעביר מידע פנימי על האובייקט עליו

.או הצבעה לאובייקט עצמו, היא מופעלת

Point p1 = new Point(1,2); Point.printPoint2(p1);

הפונקציות של , למשל. פרוצדורות סטטיות משויכות למחלקה מסוימת•מכיוון שאין הגיון ליצור אובייקט אך ורק בשביל ביצוע . Mathהמחלקה

פעולות אלו אינן תלויות במצב של אובייקט כלשהו שיכול )פעולה מתמטית הפונקציות מוגדרות כסטטיות וכך ניתן , (להשתנות במהלך הריצה

נהוג (. ()Math.pow(…), Math.random)להשתמש בהן באופן ישיר לאגד במחלקה מסוימת אוסף של פרוצדורות סטטיות הקשורות לאותו

ובכך לאפשר שימוש באותו אוסף מתוך מספר רב של תוכניות , התחום .שונות

הקוד שיופעל נקבע כבר בזמן – לא ניתן לדרוס פרוצדורות סטטיות• .קומפילציה

29

סטטי-סטטי מול לא

משויכות באופן ישיר לאובייקט אשר , שיטות מחלקה לעומת זאת :למשל. יזם את הקריאה להן

public static void printPoint3(Point p){ System.out.println("(" + p.getX() + "," + p.getY() + ")"); } public static void main(String[] args){ Point p = new Point(1,2); p.printPoint1(); printPoint3(p); Point.printPoint2(); //Compilation Error Point.printPoint2(p); p.printPoint2(p); //works – but it is the wrong way to write code

} 30

סטטי-סטטי מול לא

משתנים סטטיים מול שדות

לכל אובייקט יש את , כאשר אנו עובדים עם אובייקטים•יש סוג נוסף של משתנים הנקראים java-ב. השדות שלו

.משתנים סטטים

Math.PI: דוגמה למשתנה סטטי•

, משתנים סטטים משויכים למחלקה עצמה, בניגוד לשדות•

לרוב משתמשים במשתנים סטטים . ולא למופעים שלה-ואז הם מוגדרים כ, (Math.PIכדוגמת )לתיאור קבועים

static final .כ לא נשתמש במשתנים מסוג זה באופן "בד

:אך בכל זאת, אחר

שהוגדרו Pointלמנות את מספר האובייקטים מסוג , אם נרצה לדוגמה•כלומר לספור את מספר הקריאות שבוצעו לבנאי המחלקה , בתכנית

Point , נגדיר משתנה סטטיnumberOfPoints , ונגדיל את ערכו בכל :קריאה לבנאי

class Point{ public static int numberOfPoints = 0; public Point(){ x=0; y=0; numberOfPoints = numberOfPoints + 1; } … }//class

:עתה נוכל לכתוב בתכנית•

System.out.println(Point.numberOfPoints);