מחלקה מופשטת ב-PHP מוצהרת עם abstract class Name ואי אפשר ליצור אותה עם new. היא בסיס למחלקות אחרות: יכולים להיות לה מאפיינים, בנאי ומתודות גמורות, ועוד מתודות abstract בלי גוף, שכל מחלקת בת חייבת לממש.
describe() נכתבת פעם אחת ב-Shape וקוראת ל-area(), שכל בת מספקת. המחלקה המופשטת מבטיחה שהמתודה קיימת, ולכן describe() יכולה לסמוך עליה.
אי אפשר ליצור מחלקה מופשטת עם new
מחלקה מופשטת לא שלמה בכוונה: ל-Shape::area() אין גוף, ולכן לאובייקט Shape רגיל לא היה מה להריץ. PHP מסרבת ליצור כזה וזורקת Error:
מתודות מופשטות: הכללים
למתודה מופשטת יש חתימה ונקודה פסיק במקום גוף. כל בת חייבת לממש אותה עם חתימה תואמת ועם אותה רמת גישה או רחבה יותר: מתודה abstract protected יכולה להפוך ל-protected או ל-public בבת, אף פעם לא ל-private. מחלקה עם אפילו מתודה מופשטת אחת חייבת בעצמה להיות מוצהרת מופשטת.
בת שמדלגת על מתודה מופשטת עוצרת את הסקריפט לפני שחלק כלשהו ממנו רץ, אלא אם גם הבת מוצהרת מופשטת ומשאירה את המתודה לבנות שלה:
PHP Fatal error: Class Square contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (Shape::area) in /home/index.php on line 3
תבנית template method
הסיבה הנפוצה ביותר לכתוב מחלקה מופשטת: רצף קבוע של צעדים שבו כמה צעדים שונים. מחלקת הבסיס מחזיקה את הרצף במתודה final, והבנות ממלאות את הצעדים. כאן אותו דוח רץ כטקסט פשוט וכטבלת HTML:
הריצו את זה והלשונית Page תציג את טבלת ה-HTML מעל גרסת הטקסט הפשוט. ל-open() ול-close() יש גוף ברירת מחדל, ולכן רק HtmlReport טורחת לדרוס אותן; row() מופשטת כי אין לה ברירת מחדל הגיונית. הפיכת render() ל-final מונעת מבת לשנות את סדר הצעדים.
מחלקה מופשטת יכולה לממש ממשק
מחלקה מופשטת רשאית להצהיר implements ולהשאיר חלק מהמתודות של הממשק לבנות שלה. זו הדרך הסטנדרטית לחבר ממשק (הטיפוס) עם מחלקת בסיס (הקוד המשותף):
הקוד משתמש ברמז הטיפוס של הממשק Cache, ולכן מחלקה שלא יכולה להרחיב את BaseCache (כי היא כבר מרחיבה משהו אחר) עדיין יכולה לממש את Cache ישירות.
מתי להשתמש במחלקה מופשטת, ומתי לא
פנו למחלקה מופשטת כשהמחלקות במשפחה חולקות קוד אמיתי: מאפיינים, בנאי, מתודות שזהות לכולן. אם הבסיס יכיל רק מתודות מופשטות, הוא ממשק בתחפושת, וממשק הוא הבחירה הטובה יותר כי מחלקה יכולה לממש הרבה ממשקים אבל להרחיב רק מחלקה אחת. אם אתם רוצים רק לשתף כמה מתודות עזר בין מחלקות שאינן קשורות, השתמשו ב-trait במקום לדחוק אותן תחת אב אחד.
בדיקה מהירה לפני שכותבים abstract class: האם כל בת באמת תהיה סוג של הבסיס (Circle היא Shape)? אם התשובה הכנה היא "היא יודעת לעשות את זה" ולא "היא אחת מאלה", כתבו ממשק.
שאלות נפוצות
מה זו מחלקה מופשטת ב-PHP?
מחלקה שמוצהרת עם abstract class Name ואי אפשר ליצור אותה עם new. היא בסיס למחלקות אחרות: היא יכולה להכיל מאפיינים, בנאי ומתודות רגילות, והיא יכולה להצהיר על מתודות abstract שכל מחלקת בת חייבת לממש.
מה זו מתודה מופשטת ב-PHP?
מתודה שמוצהרת עם abstract ובלי גוף: abstract protected function render(array $rows): string;. מחלקה שמכילה אחת כזו חייבת בעצמה להיות מוצהרת abstract, וכל מחלקת בת שאינה מופשטת חייבת להגדיר מתודה עם חתימה תואמת.
האם למחלקה מופשטת ב-PHP יכול להיות בנאי?
כן. אי אפשר לקרוא ל-new על המחלקה המופשטת, אבל הבנאי שלה רץ כשנוצרת מחלקת בת, או אוטומטית (אם לבת אין בנאי) או דרך parent::__construct(...).
אפשר ליצור מופע של מחלקה מופשטת ב-PHP?
לא. new Shape() על מחלקה מופשטת זורק Error: Cannot instantiate abstract class Shape. צרו במקום זאת אובייקט של מחלקת בת קונקרטית, למשל new Circle(2).
מתי כדאי להשתמש במחלקה מופשטת ב-PHP?
כשכמה מחלקות קשורות חולקות קוד אמיתי (מאפיינים, בנאי, מתודות משותפות) ונבדלות בכמה צעדים. שימו את הקוד המשותף במחלקה המופשטת והצהירו על הצעדים השונים כמופשטים. אם אין קוד משותף ואתם צריכים רק חוזה, השתמשו בממשק.