CodeGym /בלוג Java /Random-HE /דוגמאות ספציפיות של שיעורים מופשטים ב-Java
John Squirrels
רָמָה
San Francisco

דוגמאות ספציפיות של שיעורים מופשטים ב-Java

פורסם בקבוצה
היי! בשיעורים קודמים פגשנו ממשקים והבנו למה הם מיועדים. הנושא של היום יהדהד את הנושא הקודם. בואו נדבר על שיעורים מופשטים בג'אווה. דוגמאות ספציפיות של שיעורים מופשטים ב-Java - 1

מדוע שיעורים נקראים 'מופשטים'

אתה בטח זוכר מה זה 'הפשטה' - כבר עברנו על זה. :) אם שכחת, אל תפחד. זכור: זהו עיקרון של OOP שאומר בעת תכנון מחלקות ויצירת אובייקטים, עלינו לזהות רק את המאפיינים העיקריים של הישות ולבטל את הקטין. לדוגמה, אם אנחנו מעצבים SchoolTeacherמחלקה, אנחנו כמעט לא צריכים תכונה ' גובה '. אכן, נכס זה אינו רלוונטי עבור מורה. אבל אם אנחנו יוצרים BasketballPlayerמחלקה, אז צמיחה תהיה מאפיין חשוב. אז תקשיב. מחלקה מופשטת היא מופשטת כפי שהיא מגיעה - 'ריק' לא גמור עבור קבוצת כיתות עתידיות. לא ניתן להשתמש בחסר כפי שהוא. זה 'גולמי' מדי. אבל הוא מתאר מצב מסוים והתנהגות כללית שיהיו ברשותם של מעמדות עתידיים שיורשים את המעמד המופשט.

דוגמאות לשיעורי ג'אווה מופשטים

שקול דוגמה פשוטה עם מכוניות:

public abstract class Car {

   private String model;
   private String color;
   private int maxSpeed;

   public abstract void gas();

   public abstract void brake();

   public String getModel() {
       return model;
   }

   public void setModel(String model) {
       this.model = model;
   }

   public String getColor() {
       return color;
   }

   public void setColor(String color) {
       this.color = color;
   }

   public int getMaxSpeed() {
       return maxSpeed;
   }

   public void setMaxSpeed(int maxSpeed) {
       this.maxSpeed = maxSpeed;
   }
}
כך נראית הכיתה המופשטת הפשוטה ביותר. כפי שאתה יכול לראות, זה לא משהו מיוחד :) למה שנצטרך את זה? קודם כל, הוא מתאר את הישות הנדרשת שלנו, מכונית, בצורה הכי מופשטת שאפשר. יש סיבה מדוע אנו משתמשים במילה אבסטרקטית . בעולם האמיתי, אין 'מכוניות מופשטות'. יש משאיות, מכוניות מירוץ, מכוניות סדאן, קופה ורכבי שטח. הכיתה המופשטת שלנו היא פשוט 'תוכנית' שבה נשתמש בהמשך כדי ליצור כיתות רכב.

public class Sedan extends Car {

   @Override
   public void gas() {
       System.out.println("The sedan is accelerating!");
   }

   @Override
   public void brake() {
       System.out.println("The sedan is slowing down!");
   }

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

public class Main {

   public static void main(String[] args) {

       Car car = new Car(); // Error! The Car class is abstract!
   }
}
יוצרי Java עיצבו במיוחד את ה'תכונה' הזו. שוב, כתזכורת: שיעור מופשט הוא רק תוכנית לשיעורים 'רגילים' עתידיים . אתה לא צריך עותקים של השרטוט, נכון? ואתה לא יוצר מופעים של מחלקה מופשטת :) אבל אם המחלקה Carלא הייתה מופשטת, היינו יכולים בקלות ליצור מופעים שלה:

public class Car {

   private String model;
   private String color;
   private int maxSpeed;

   public void gas() {
       // Some logic
   }

    public void brake() {
       // Some logic
   }
}


public class Main {

   public static void main(String[] args) {

       Car car = new Car(); // Everything is fine. A car is created.
   }
}
עכשיו לתוכנית שלנו יש סוג של מכונית לא מובנת - זו לא משאית, לא מכונית מירוץ, לא מכונית סדאן, ולא ברור לחלוטין מהי. זו ה'מכונית המופשטת' מאוד שלא קיימת בטבע. אנו יכולים לספק את אותה דוגמה באמצעות בעלי חיים. תארו לעצמכם אם Animalשיעורים ( בעלי חיים מופשטים ). לא ברור באיזה סוג של חיה מדובר, לאיזו משפחה הוא שייך ואיזה מאפיינים יש לו. זה יהיה מוזר לראות את זה בתוכנית שלך. אין 'חיות מופשטות' בטבע. רק כלבים, חתולים, שועלים, שומות וכו'. שיעורים מופשטים מוציאים אותנו מחפצים מופשטים. הם נותנים לנו מצב והתנהגות בסיסיים. לדוגמה , לכל המכוניות צריך להיות דגם , צבע ומהירות מרבית , ואתה אמור להיות מסוגל להפעיל את הגז והבלם . זהו זה. זוהי תוכנית מופשטת כללית. בשלב הבא אתה מעצב את השיעורים שאתה צריך. הערה: שתי שיטות במחלקה המופשטת מוגדרות גם הן כמופשטות , ואין להן יישום כלשהו. הסיבה זהה: מחלקות מופשטות אינן יוצרות התנהגות ברירת מחדל עבור מכוניות מופשטות. הם רק מציינים מה כל מכונית צריכה להיות מסוגלת לעשות. עם זאת, אם אתה צריך התנהגות ברירת מחדל, אתה יכול ליישם שיטות במחלקה מופשטת. Java לא אוסרת את זה:

public abstract class Car {

   private String model;
   private String color;
   private int maxSpeed;

   public void gas() {
       System.out.println("Gas!");
   }

   public abstract void brake();

   // Getters and setters
}


public class Sedan extends Car {

   @Override
   public void brake() {
       System.out.println("The sedan is slowing down!");
   }

}

public class Main {

   public static void main(String[] args) {

       Sedan sedan = new Sedan();
       sedan.gas();
   }
}
פלט מסוף: "גז!" כפי שאתה יכול לראות, הטמענו את השיטה הראשונה במחלקה המופשטת, ולא את השנייה. כתוצאה מכך, Sedanההתנהגות של המחלקה שלנו מחולקת לשני חלקים: אם אתה קורא לשיטה gas(), הקריאה 'עולה' עד Carלמחלקת האב המופשטת, אבל התגברנו על brake()השיטה בכיתה Sedan. זה מתברר כמאוד נוח וגמיש. אבל עכשיו המחלקה שלנו לא כל כך מופשטת ? הרי חצי מהשיטות שלה מיושמות. זה הוא למעשה תכונה חשובה מאוד - מחלקה היא מופשטת אם לפחות אחת מהשיטות שלה היא מופשטת . אחת משתי שיטות, או לפחות אחת מאלף שיטות - זה לא משנה. אנחנו אפילו יכולים ליישם את כל השיטות ולא להשאיר אף אחת מהם מופשטים. אז זה יהיה מחלקה אבסטרקטית ללא שיטות מופשטות. באופן עקרוני, זה אפשרי, והמהדר לא יפיק שגיאות, אבל עדיף להימנע מכך: המילה אבסטרקט מאבדת את משמעותה, ועמיתיך המתכנתים יעשו זאת . להיות מאוד מופתע :/ יחד עם זאת, אם שיטה מסומנת במילה אבסטרקט, כל כיתת ילד חייבת ליישם אותה או להכריז עליה כמופשטת. אחרת, המהדר יפיק שגיאה. כמובן שכל מחלקה יכולה לרשת רק מחלקה מופשטת אחת, כך שמבחינת הירושה אין הבדל בין מחלקות מופשטות לרגילות. זה לא משנה אם אנחנו יורשים מחלקה מופשטת או מחלקה רגילה, יכולה להיות רק מחלקה אב אחת.

מדוע לג'אווה אין ירושה מרובה של מחלקות

כבר אמרנו שלג'אווה אין ירושה מרובה, אבל לא ממש בדקנו למה. בואו ננסה לעשות את זה עכשיו. העובדה היא שאם לג'אווה הייתה ירושה מרובה, כיתות ילדים לא היו מסוגלות להחליט באיזו התנהגות ספציפית עליהן לבחור. נניח שיש לנו שתי מחלקות - Toasterו NuclearBomb:

public class Toaster {


 public void on() {

       System.out.println("The toaster is on. Toast is being prepared!");
   }

   public void off() {

       System.out.println("The toaster is off!");
   }
}


public class NuclearBomb {

   public void on() {

       System.out.println("Boom!");
   }
}
כפי שאתה יכול לראות, לשניהם יש on()שיטה. עבור טוסטר, הוא מתחיל לצלות. עבור פצצה גרעינית, זה מעורר פיצוץ. אופס: / עכשיו תאר לעצמך שהחלטת (אל תשאל אותי למה!) ליצור משהו באמצע. וכך יש לך MysteriousDeviceשיעור! הקוד הזה, כמובן, לא עובד, ואנחנו מספקים אותו רק כדוגמה 'אבל זה יכול להיות':

public class MysteriousDevice extends Toaster, NuclearBomb {

   public static void main(String[] args) {

       MysteriousDevice mysteriousDevice = new MysteriousDevice();
       mysteriousDevice.on(); // So what should happen here? Do we get toast or a nuclear apocalypse?
   }
}
בואו נסתכל על מה שיש לנו. המכשיר המסתורי יורש בו זמנית את טוסטר ו- NuclearBomb. לשניהם יש on()שיטות. כתוצאה מכך, אם נקרא לשיטה on(), לא ברור איזו מהן יש להפעיל על MysteriousDeviceהאובייקט. אין סיכוי שהאובייקט יוכל אי פעם לדעת. ולסיום הכל: ל- NuclearBomb אין off()שיטה, כך שאם לא ניחשנו נכון, אי אפשר יהיה להשבית את המכשיר. דוגמאות ספציפיות של שיעורים מופשטים ב-Java - 2בדיוק בגלל ה'בלבול' הזה, שבו האובייקט לא יודע איזו התנהגות להפגין, נטשו יוצרי ג'אווה את הירושה המרובה. עם זאת, תזכרו ששיעורי Java יכולים ליישם ממשקים מרובים. אגב, בלימודים כבר נתקלת בשיעור מופשט אחד לפחות! למרות שאולי אפילו לא שמת לב :)

public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar>
זה החבר הוותיק שלך, Calendarהכיתה. זה מופשט ויש לו כמה ילדים. אחד מהם הוא GregorianCalendar. כבר השתמשת בו בשיעורים על תאריכים. :) הכל נראה ברור מספיק. יש רק שאלה אחת: מה ההבדל המהותי בין מחלקות מופשטות לממשקים בכלל? מדוע הם הוסיפו את שניהם ל-Java במקום רק להגביל את השפה לאחת? אחרי הכל, זה היה מספיק לגמרי. נדבר על זה בשיעור הבא ! עד אז :)
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION