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

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

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

מדוע כיתות נקראות "מופשטות"

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

דוגמאות לכיתה מופשטת של Java

בואו נסתכל על דוגמה פשוטה עם מכוניות:
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 accelerates!");
   }

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

}
זה דומה מאוד למה שדיברנו עליו בהרצאות על ירושה. רק שם הייתה לנו מחלקה Carוהשיטות שלה לא היו מופשטות. אבל לפתרון הזה יש מספר חסרונות, המתוקנים בשיעורים מופשטים. בראש ובראשונה, לא ניתן ליצור מופע של מחלקה מופשטת:
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 OK, the machine has been created
   }
}
עכשיו יש לנו סוג של מכונית לא מובנת בתוכנית שלנו - לא משאית, לא מכונית מירוץ, לא מכונית סדאן, אלא משהו באופן כללי. אותה "סתם מכונה" שלא קיימת בטבע. ניתן לתת את אותה דוגמה עם בעלי חיים. תאר לעצמך שאובייקטים הופיעו בתוכנית שלך Animal- " רק חיה ". לא ברור איזה סוג הוא, לאיזו משפחה הוא שייך, אילו מאפיינים יש לו. זה יהיה מוזר לראות אותו בתוכנית. אין "סתם חיות" בטבע. רק כלבים, חתולים, שועלים, שומות ואחרים. שיעורים מופשטים משחררים אותנו מ"סתם חפצים ". הם נותנים לנו מצב והתנהגות בסיסיים. לדוגמה, כל המכוניות חייבות להיות עם דגם , צבע ומהירות מרבית , והן חייבות גם להיות מסוגלות לגזול ולבלום . זה הכל. זוהי תכנית מופשטת כללית, ואז אתה בעצמך מעצב את השיעורים שאתה צריך. שימו לב: שתי שיטות במחלקה מופשטת מוגדרות גם הן כמופשטות , והן אינן מיושמות כלל. הסיבה זהה: מחלקות מופשטות אינן יוצרות "התנהגות ברירת מחדל" עבור "מכונות בלבד". הם רק אומרים שהם צריכים להיות מסוגלים לייצר את כל המכוניות. עם זאת, אם אתה עדיין זקוק להתנהגות ברירת המחדל, אתה יכול ליישם שיטות במחלקה מופשטת. Java לא אוסרת את זה:
public abstract class Car {

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

   public void gas() {
       System.out.println("Let's go!");
   }

   public abstract void brake();

   //getters and setters
}


public class Sedan extends Car {

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

}

public class Main {

   public static void main(String[] args) {

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

מדוע אין ירושה מרובה מחלקות ב-Java?

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


 public void on() {

       System.out.println("The toaster is on, the toast is getting ready!");
   }

   public void off() {

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


public class NuclearBomb {

   public void on() {

       System.out.println("Взрыв!");
   }
}
כפי שאתה יכול לראות, לשניהם יש שיטה on(). במקרה של טוסטר הוא מתחיל לבשל טוסט, ובמקרה של פצצה גרעינית הוא גורם לפיצוץ. אה :/ עכשיו תאר לעצמך שהחלטת (אני לא יודע למה פתאום!) ליצור משהו באמצע. והנה זה הכיתה שלך - MysteriousDevice! הקוד הזה, כמובן, לא עובד, ואנחנו מציגים אותו פשוט כדוגמה ל"איך זה יכול להיות":
public class MysteriousDevice extends Toster, NuclearBomb {

   public static void main(String[] args) {

       MysteriousDevice mysteriousDevice = new MysteriousDevice();
       mysteriousDevice.on(); // And what should happen here? Will we get a toast, or a nuclear apocalypse?
   }
}
בוא נראה מה יש לנו. המכשיר המסתורי מגיע גם מהטוסטר וגם מהפצצה הגרעינית. לשניהם יש שיטה on(), וכתוצאה מכך, לא ברור איזו שיטה on()צריכה לירות על האובייקט MysteriousDeviceאם נקרא לזה. האובייקט לא יוכל להבין זאת. ובכן, כדובדבן על העוגה: לפצצה הגרעינית אין שיטה off(), אז אם ניחשנו לא נכון, לא תהיה דרך לכבות את המכשיר. שיעורים מופשטים בג'אווה עם דוגמאות קונקרטיות - 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