הזרקת תלות (DI) היא מושג לא קל להבנה, והיישום שלו על יישומים חדשים או קיימים הוא אפילו יותר מבלבל. ג'ס סמית' מראה לך כיצד לבצע הזרקת תלות ללא מיכל הזרקה בשפות התכנות C# ו-Java. במאמר זה, אני אראה לך כיצד ליישם הזרקת תלות (DI) ביישומי NET ו-Java. הרעיון של הזרקת תלות הגיע לתשומת לבם של מפתחים לראשונה בשנת 2000, כאשר רוברט מרטין כתב את המאמר "עקרונות עיצוב ודפוסים" (שנודע מאוחר יותר בראשי התיבות
SOLID ). ה-D ב-SOLID מתייחס לתלות של היפוך (DOI), שלימים נודע בשם הזרקת תלות. ההגדרה המקורית והנפוצה ביותר: היפוך תלות הוא היפוך של האופן שבו מחלקת בסיס מנהלת תלות. המאמר המקורי של מרטין השתמש בקוד הבא כדי להמחיש את התלות של מחלקה
Copy
במחלקה ברמה נמוכה יותר
WritePrinter
:
void Copy()
{
int c;
while ((c = ReadKeyboard()) != EOF)
WritePrinter(c);
}
הבעיה הברורה הראשונה היא שאם אתה משנה את רשימת הפרמטרים או סוגי השיטה
WritePrinter
, אתה צריך ליישם עדכונים בכל מקום שיש תלות בשיטה זו. תהליך זה מגדיל את עלויות התחזוקה ומהווה מקור פוטנציאלי לטעויות חדשות.
בעיה נוספת: כיתת העתק אינה עוד מועמדת פוטנציאלית לשימוש חוזר. לדוגמה, מה אם אתה צריך פלט תווים שהוזנו מהמקלדת לקובץ במקום למדפסת? כדי לעשות זאת, אתה יכול לשנות את המחלקה
Copy
באופן הבא (תחביר שפת C++):
void Copy(outputDevice dev)
{
int c;
while ((c = ReadKeyboard()) != EOF)
if (dev == printer)
WritePrinter(c);
else
WriteDisk(c);
}
למרות כניסתה של תלות חדשה
WriteDisk
, המצב לא השתפר (אלא הוחמר) מכיוון שהופר עיקרון אחר: "ישויות תוכנה, כלומר מחלקות, מודולים, פונקציות וכדומה, צריכות להיות פתוחות להרחבה, אך סגורות עבור שינוי." מרטין מסביר שהצהרות אם/אחרות החדשות הללו מפחיתות את היציבות והגמישות של הקוד. הפתרון הוא להפוך את התלות כך ששיטות הכתיבה והקריאה תלויות ב-
Copy
. במקום "לפוצץ" תלות, הן מועברות דרך הבנאי. הקוד שהשתנה נראה כך:
class Reader
{
public:
virtual int Read() = 0;
};
class Writer
{
public:
virtual void Write(char) = 0;
};
void Copy(Reader& r, Writer& w)
{
int c;
while((c=r.Read()) != EOF)
w.Write(c);
}
Copy
כעת ניתן לעשות שימוש חוזר בקלות במחלקה עם יישומים שונים של שיטות מחלקות
Reader
ו-
Writer
. למחלקה
Copy
אין מידע על המבנה הפנימי של הסוגים
Reader
ו-
Writer
, מה שמאפשר לעשות בהם שימוש חוזר עם יישומים שונים. אבל אם כל זה נראה לך כמו סוג של גובלדיגוק, אולי הדוגמאות הבאות ב-Java ו-C# יבהירו את המצב.
דוגמה ב-Java ו-C#
כדי להמחיש את קלות הזרקת התלות ללא מיכל תלות, נתחיל בדוגמה פשוטה שניתן להתאים אישית לשימוש
DI
בכמה שלבים בלבד. נניח שיש לנו מחלקה
HtmlUserPresentation
שכאשר השיטות שלה נקראות, מייצרת ממשק משתמש HTML. הנה דוגמה פשוטה:
HtmlUserPresentation htmlUserPresentation = new HtmlUserPresentation();
String table = htmlUserPresentation.createTable(rowTableVals, "Login Error Status");
לכל פרויקט המשתמש בקוד המחלקה הזה תהיה תלות במחלקה
HtmlUserPresentation
, וכתוצאה מכך בעיות השימושיות והתחזוקה שתוארו לעיל. שיפור מיידי מציע את עצמו: יצירת ממשק עם חתימות של כל השיטות הזמינות כיום בכיתה
HtmlUserPresentation
. הנה דוגמה לממשק זה:
public interface IHtmlUserPresentation {
String createTable(ArrayList rowVals, String caption);
String createTableRow(String tableCol);
}
לאחר יצירת הממשק, אנו משנים את המחלקה
HtmlUserPresentation
לשימוש בו. אם נחזור לאינסטציה של הסוג
HtmlUserPresentation
, נוכל כעת להשתמש בסוג הממשק במקום בסוג הבסיס:
IHtmlUserPresentation htmlUserPresentation = new HtmlUserPresentation();
String table = htmlUserPresentation.createTable(rowTableVals, "Login Error Status");
יצירת ממשק מאפשרת לנו להשתמש בקלות ביישומים אחרים של ה-
IHtmlUserPresentation
. לדוגמה, אם ברצוננו לבדוק סוג זה, נוכל בקלות להחליף את סוג הבסיס
HtmlUserPresentation
בסוג אחר בשם
HtmlUserPresentationTest
. השינויים שבוצעו עד כה הופכים את הקוד לקל יותר לבדיקה, תחזוקה וקנה מידה, אך לא עושים דבר לשימוש חוזר מכיוון שכל
HtmlUserPresentation
המחלקות המשתמשות בסוג עדיין מודעות לקיומו. כדי להסיר את התלות הישירה הזו, אתה יכול להעביר סוג ממשק
IHtmlUserPresentation
לבנאי (או לרשימת פרמטרי המתודה) של המחלקה או המתודה שישתמשו בו:
public UploadFile(IHtmlUserPresentation htmlUserPresentation)
לבנאי
UploadFile
יש כעת גישה לכל הפונקציונליות של הסוג
IHtmlUserPresentation
, אך אינו יודע דבר על המבנה הפנימי של המחלקה המיישמת את הממשק הזה. בהקשר זה, הזרקת סוג מתרחשת כאשר נוצר מופע של המחלקה
UploadFile
. סוג ממשק
IHtmlUserPresentation
הופך לשימוש חוזר על ידי העברת יישומים שונים למחלקות או שיטות שונות הדורשות פונקציונליות שונה.
מסקנה והמלצות לאיחוד החומר
למדת על הזרקת תלות ועל כך שנאמר כי מחלקות תלויות ישירות זו בזו כאשר אחת מהן מבצעת מופע אחר כדי לקבל גישה לפונקציונליות של סוג היעד. כדי לנתק את התלות הישירה בין שני הסוגים, עליך ליצור ממשק. ממשק נותן לסוג את היכולת לכלול יישומים שונים, בהתאם להקשר של הפונקציונליות הנדרשת. על ידי העברת סוג ממשק לבנאי מחלקה או שיטה, המחלקה/שיטה שצריכה את הפונקציונליות אינה יודעת פרטים על הסוג המיישם את הממשק. בשל כך, ניתן לעשות שימוש חוזר בסוג ממשק על פני מחלקות שונות הדורשות התנהגות דומה, אך לא זהה.
- כדי להתנסות בהזרקת תלות, בדוק את הקוד שלך מיישום אחד או יותר ונסה להמיר סוג בסיס בשימוש רב לממשק.
- שנה את המחלקות המייצרות ישירות את סוג הבסיס הזה כדי להשתמש בסוג הממשק החדש הזה ולהעביר אותו דרך הבנאי או רשימת הפרמטרים של שיטת המחלקה שתשתמש בו.
- צור מימוש בדיקה כדי לבדוק את סוג הממשק הזה. ברגע שהקוד שלך יעבור מחדש,
DI
זה יהפוך קל יותר ליישום, ותבחין עד כמה האפליקציה שלך נהייתה גמישה יותר מבחינת שימוש חוזר ותחזוקה.
GO TO FULL VERSION