מיקסינים לעומת ממשק
חלק מהיחידה תכנות מונחה עצמים במסלול ה-Dart של Coddy. שיעור 55 מתוך 110.
ראית איך mixins משתווים לירושה. עכשיו נשווה בין mixins לממשקים, מכיוון ששניהם מאפשרים לך לעבוד עם כמה טיפוסים. ההבדל העיקרי הוא מה הם מספקים: ממשקים מגדירים חוזה, ואילו mixins מספקים מימוש.
כשאתה מממש ממשק, אתה מתחייב לספק מתודות מסוימות, אבל כותב את כל הקוד בעצמך. כשאתה משתמש ב-mixin, אתה מקבל את הקוד עצמו בחינם.
// גישת הממשק - יש לממש הכול בעצמכם
abstract class Printable {
void printInfo();
}
class Report implements Printable {
@override
void printInfo() {
print('Printing report...'); // את זה אתם כותבים
}
}
// גישת ה־Mixin - המימוש כלול
mixin Printable {
void printInfo() {
print('Printing...'); // כבר נכתב עבורכם
}
}
class Report with Printable {} // מקבל את printInfo() אוטומטיתאז מתי כדאי להשתמש בכל אחד מהם? השתמשו בממשק כשמחלקות שונות צריכות את אותן חתימות של מתודות, אך עם מימושים שונים. השתמשו במיקסין כשמחלקות צריכות לחלוק בדיוק את אותה התנהגות.
| תכונה | ממשק | מיקסין |
|---|---|---|
| מספק | חוזה בלבד | חוזה + קוד |
| מימוש | אתה כותב אותו | כלול |
| מילת מפתח | implements | with |
| הכי מתאים ל־ | התנהגויות שונות, אותו API | התנהגות משותפת |
אפשר אפילו לשלב בין שתי הגישות — להשתמש בממשקים כדי להגדיר מה מחלקה יכולה לעשות, ובמיקסינים כדי לספק מימושים משותפים כשזה מתאים. ב-Dart, כשמשלבים בין השניים, תמיד מציבים את with לפני implements:
class MyClass with SomeMixin implements SomeInterface {
// with מופיע לפני implements
}אתגר
קלבואו נבנה מערכת התראות שמדגימה מתי להשתמש בממשקים לעומת מיקסינים. תיצרו ממשק להתראות שדורשות מימושים שונים, ומיקסין להתנהגות רישום משותפת שאמורה להיות זהה בכל סוגי ההתראות.
תארגנו את הקוד בשלושה קבצים:
notifiable.dart: הגדירו מחלקה מופשטתNotifiableשמשמשת כממשק. עליה להצהיר על שתי מתודות מופשטות:send(String message)ו-getChannel(), שמחזירהString. סוגי התראות שונים יממשו את המתודות האלה בדרכים שונות בהתאם לערוץ הספציפי שלהם.logging.dart: צרו מיקסין בשםLoggingשמספק התנהגות משותפת שכל ההתראות אמורות לכלול. כללו מתודהlogSent(String channel, String message)שמדפיסה[LOG] Sent via [channel]: [message]ומתודהlogTimestamp()שמדפיסה[LOG] Timestamp recorded. ההתנהגות הזאת זהה בכל סוגי ההתראות, ולכן היא מתאימה במיוחד למיקסין.main.dart: ייבאו את שני הקבצים וצרו שתי מחלקות התראה שמשלבות את הממשק עם המיקסין:- מחלקה
EmailNotificationשמשתמשת במיקסיןLoggingומממשת אתNotifiable. המתודהgetChannel()שלה אמורה להחזיר'Email', והמתודהsend()שלה אמורה להדפיסSending email: [message], ואז לקרוא ל-logSent()עם הערוץ וההודעה, ואז לקרוא ל-logTimestamp() - מחלקה
SMSNotificationשמשתמשת במיקסיןLoggingומממשת אתNotifiable. המתודהgetChannel()שלה אמורה להחזיר'SMS', והמתודהsend()שלה אמורה להדפיסSending SMS: [message], ואז לקרוא ל-logSent()עם הערוץ וההודעה, ואז לקרוא ל-logTimestamp()
EmailNotificationוקראו ל-send('Hello World'), הדפיסו שורה ריקה, צרו מופע שלSMSNotificationוקראו ל-send('Quick update')- מחלקה
שימו לב איך הממשק מחייב כל מחלקה לכתוב מימושים משלה עבור send() ו-getChannel() (התנהגות שונה), בעוד שהמיקסין מספק את מתודות הרישום שפועלות באותו אופן בשתיהן (התנהגות משותפת).
פלט צפוי:
Sending email: Hello World
[LOG] Sent via Email: Hello World
[LOG] Timestamp recorded
Sending SMS: Quick update
[LOG] Sent via SMS: Quick update
[LOG] Timestamp recordedנסו בעצמכם
import 'notifiable.dart';
import 'logging.dart';
// TODO: צרו מחלקה EmailNotification ש:
// - משתמשת ב-mixin Logging (with)
// - מממשת את Notifiable (implements)
// - getChannel() מחזירה 'Email'
// - send() מדפיסה 'Sending email: [message]', ואז קוראת ל-logSent(), ואז ל-logTimestamp()
// TODO: צרו מחלקה SMSNotification ש:
// - משתמשת ב-mixin Logging (with)
// - מממשת את Notifiable (implements)
// - getChannel() מחזירה 'SMS'
// - send() מדפיסה 'Sending SMS: [message]', ואז קוראת ל-logSent(), ואז ל-logTimestamp()
void main() {
// TODO: צרו EmailNotification וקראו ל-send('Hello World')
// TODO: הדפיסו שורה ריקה
// TODO: צרו SMSNotification וקראו ל-send('Quick update')
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות התכנות מונחה העצמים
קבצים חיצונייםספריות וייבואמבוא לתכנות מונחה עצמיםמחלקות לעומת אובייקטיםמילת המפתח thisמתודותמשתני מופעיסודות הבנאיחזרה: מחשבון פשוט4בטיחות מפני ערכי null
מבוא לבטיחות מפני ערכי nullניתן להקצאה ל-null לעומת לא ניתן להקצאה ל-nullהאופרטורים ? ו-!מילת המפתח Late ובטיחות מפני ערכי nullאופרטורים מודעי nullבטיחות מפני ערכי null במחלקותסיכום – מערכת פרופילי משתמשים7מחלקות מופשטות וממשקים
מחלקות מופשטותמתודות מופשטותממשקים ב-Dartממשקים משתמעיםמימוש לעומת הורשהממשקים מרוביםחזרה – מחשבון צורות10אוספים וגנריות
סקירה כללית של List, Set ו-Mapאוספים בטוחים מבחינת טיפוסיםמחלקות גנריותמתודות גנריותאילוצים גנרייםIterable ו-Iteratorסיכום – אחסון גנרי13מושגי OOP מתקדמים
הרכבה לעומת הורשהמתודות הרחבהמחלקות קריאותמחלקות אטומות (Dart 3)רשומות (Dart 3)תבניות והתאמה (3.0)טיפוסים מונים עם מתודות16פרויקט: ניהול ספרייה
סקירת הפרויקטמחלקות ספר ומשתמש2בנאים ב-Dart
בנאי ברירת מחדלבנאים בעלי שםרשימות אתחולבנאים קבועיםבנאי Factoryבנאים מפניםסיכום - בונה צורות5כימוס
חברים ציבוריים לעומת פרטייםמוסכמת הקידומת _פרטיות ברמת הספרייההעמקה ב-getters וב-settersהסתרת מידעסיכום – רשומות תלמידים8מיקסינים
מבוא למיקסיניםיצירת מיקסיניםשימוש במספר מיקסיניםמילת המפתח on במיקסיניםמיקסינים לעומת ירושהמיקסינים לעומת ממשקסיכום – מערכת בעלי החיים3מאפייני מחלקה
איברי מופע לעומת איברים סטטייםשדות final ו-constמשתני lateמתודות ושדות סטטייםGetters ו-Settersסיכום – מנהל חשבונות בנק6ירושה
ירושה בסיסיתמילת המפתח superדריסת מתודותהאנוטציה @overrideמילת המפתח final למחלקהבנאים וירושהסיכום – היררכיית עובדים9פולימורפיזם
יסודות הפולימורפיזםפולימורפיזם באמצעות ממשקיםבדיקת טיפוסים בזמן ריצההאופרטורים is ו-asמילת המפתח קווריאנטיסיכום — מעבד תשלומים12תכנות מונחה עצמים אסינכרוני
Future-ים ו-async/awaitיסודות ה-Streamsבקרי Streamsבנאים אסינכרונייםאסינכרוניות במתודות של מחלקהסיכום - אחזור נתוניםתרגלו בעצמכם: קומפיילר Dart אונליין