381. Null Object pattern

נראה הפעם איזשהו Pattern שMartin Fowler כתב עליו ב1999.

הPattern נקרא Null Object והמטרה שלו היא לפתור את הבעיה הזאת:

לעתים איזושהי פונקציה בקוד שלנו שמחזירה אובייקט, מחזירה null.

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

דיברנו על וואריאציות של הבעיה הזו כבר בעבר (טיפים 106-110, טיפ 48).

כעת נראה פתרון אלטרנטיבי.


הפתרון הוא ליצור אובייקט שמממש את הממשק של החתימה של הפונקציה, אבל עם התנהגות “טיפשה”, למשל נניח ויש לנו ממשק כזה:

1
2
3
4
5
6
public abstract class PeopleProvider
{
public abstract IEnumerable<Person> RetrievePeopleByAge(int age);
public abstract void InsertPerson(Person person);
public abstract void UpdatePerson(Person person);
}

נוכל לממשו כך:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class NullPeopleProvider : PeopleProvider
{
public override IEnumerable<Person> RetrievePeopleByAge(int age)
{
return Enumerable.Empty<Person>();
}
public override void InsertPerson(Person person)
{
}
public override void UpdatePerson(Person person)
{
}
}

כך אם יש לנו פונקציה שמקבלת PeopleProvider נוכל לסכם מוסכמה שאנחנו לא מוכנים לקבל null, וכאשר המשתמש מעוניין שלא יתבצע כלום, שיעביר לנו NullPeopleProvider.


בFramework יש כמה Null Objectים שיצא לנו להכיר:

  • מערך ריק: בC# קיים מערך שהוא ריק – זה בעצם סוג של "Null Object" כי קריאה לכל המתודות של המערך לא עושה כלום
  • מחרוזת ריקה: דומה למה שהוזכר קודם – במידה ונרצה למשל לגשת לLength של הString, לא נקרוס אם מדובר בString.Empty, וכך הקוד שלנו דומה לשני המקרים.

למה זה טוב:

  • נניח שיש לנו מערכת שמורכבת מכמה מודולים שתלויים אחד בשני, ואנחנו מעוניינים להריץ את המערכת בלי אחד המודולים, אז נוכל להחליף את אחד המודולים במימוש NullObject שלו מבלי לבדוק בכל שורה שנייה בכל מודול האם אחד המודולים הוא Null או לא.
    • לדוגמה, נניח שיש לנו עמדת משתמש שמכילה מפה גיאוגרפית, וכל המערכת יודעת לגשת למפה בכדי להציג עליה נתונים – נוכל במקום שבכל מקום במערכת יבדקו האם המפה קיימת או לא (מה שמסרבל מאוד את הקוד), נחליף את המימוש של המפה במימוש NullObject.
  • הNullObjectים האלה יכולים לשמש כMocking לUnit Tests. (ראו גם טיפ מספר 350)

עם זאת:

  • כדאי מאוד להזהר בשימוש של הPattern הזה – הוא עשוי ליצור באגים לא צפויים במידה ומשתמשים בו לא בזהירות.
  • שימו לב שלפעמים כן נרצה לבצע התנהגות שונה במידה וקיבלנו NullObject – את זה נוכל לבצע ע"י בדיקה על האובייקט (כמו השוואתו לstring.Empty, בדיקת סוגו וכו’)

המשך יום עם אובייקטים ריקים טוב.

שתף