במאמר קודם דיברתי על MVC וקיבלתי לא מעט תגובות טובות, לפיכך החלטתי להסביר מעט גם על Factory שלמרות שהיא פחות הוליסטית מ-MVC, שווה להכיר אותה כדי לפחות לא להיות מובכים בפעם הבאה שתגיעו לאיזה כנס ומישהו יעלה לבמה וידבר על זה.
לגבי אלו שלמדו מדעי המחשב וכבר מכירים את Factory – המאמר הזה יהיה מאד טריוויאלי עבורכם. אולי למעט הדוגמה ב-PHP.
אז מה זה Factory? אם נפתח ויקיפדיה בערך של תבנית עיצוב: factory נגלה שמדובר בתבנית עיצוב ב-OOP שמאפשרת לנו לבנות אובייקטים בלי להכיר את המחלקות שלהם.
טוב, זו ההגדרה הרשמית, מה זה בת'כלס אומר? בואו ונסתכל למשל על קוד שמופיע בדוקומנטציה הרשמית של PHP:
class Example
{
// The parameterized factory method
public static function factory($type)
{
if (include_once 'Drivers/' . $type . '.php') {
$classname = 'Driver_' . $type;
return new $classname;
} else {
throw new Exception('Driver not found');
}
}
}
מדובר ב-Class חביב ופשוט למדי. יש לו מתודה אחת פומבית (כך שאפשר לקרוא לה מחוץ ל-Class) וסטטית (כך שאי אפשר לשנות אותה באמצעות הורשה) שמה שהיא עושה זה לבדוק אם יש דרייברים מסוג מסוים בספרית Drivers כאשר שם הדרייבר אמור להנתן באמצעות המשתנה type. במידה ויש דרייבר, היא יוצרת אובייקט ומחזירה אותו.
במידה ואין – היא מחזירה Exception.
איך מפעילים את ה-Class הזה? יש לנו שתי דוגמאות: אחת עם דרייבר של MySQL והשניה עם SQLite:
// Load a MySQL Driver
$mysql = Example::factory('MySQL');
// Load an SQLite Driver
$sqlite = Example::factory('SQLite');
עד כאן הקוד פשוט להבנה – אז מה הקטע של ה-Factory? שימו לב שאני יוצר כאן אובייקטים בצורה מובנית בלי להתעסק עם התהליך של היצירה שלהם. כל מה שאני צריך לעשות זה לספק את השם של הדרייבר שלי – כמו למשל MySQL. ברגע שסיפקתי את השם – אין יותר ביג'אראס, לא צריך לעשות include לקובץ שמכיל את התיקיה שבה יש את ה-class, לא צריך לקרוא לאופרטור new, אני מקבל בחזרה רפרנס לאובייקט שלי וזה הכל!
יתרה מכך, במידה ואין את הדרייבר מסיבה כלשהי, אני מקבל שגיאה שקל לי לעבוד איתה ולעשות פולבק במידת הצורך.
למה זה טוב? זה טוב במיוחד אם יש לנו פעולות זהות שאנו קוראים להם במהלך הקוד ואנו לא רוצים להתחיל בכל פעם להגדיר את ה-Class ואת הפרמטרים השונים שלו. כך אנחנו מרכזים בעצם את יצירת האובייקטים הרלוונטיים במקום אחד וקוראים להם בכל פעם לפי הצורך עם הפרמטרים המתאימים. כך למשל הקוד הקודם יכול להתאים לתוכנה שמתחברת לכמה מסדי נתונים שונים בהתאם לסוג הדף או לסוג הקריאה.
אולי זה יהיה מובן באמצעות דוגמה מוחשית יותר. בואו ונניח שיש לי אפליקציה שמציגה מוצרים שונים. לכל מוצר יש Class משלו – למשל Computer או Tablet. יש באפליקציה שלנו מספר דפים שכל אחד מהם אני צריך לבצע קריאה ל-Class המתאים. בשיטה הסטנדרטית, אם למשל יש לי tablet.php אני אצטרך לקרוא ל-class בשם tablet וכך הלאה. ברור לכם שאם יש לי דף קטלוגי אני צריך לבצע קריאות ל-Classים השונים בהתאם למוצרים שיש בדף. אם אני עובד בתבנית עיצוב מסוג factory, אני לא צריך לשבור את הראש ולבצע קריאות נפרדות ל-class כל פעם אלא פשוט לקרוא רק ל-factory שתבצע את כל העבודה בהתאם למה שאני מעביר לה.
בואו ונסתכל על הקוד שוב:
class ProductFactory
{
public static function build($type) {
// assumes the use of an autoloader
$product = "Product_" . $type;
if (class_exists($product)) {
return new $product();
}
else {
throw new Exception("Invalid product type given.");
}
}
}
מה שה-class הזה עושה זה לקבל פרמטר, לבצע instance ולהחזיר את האובייקט החדש והנוצץ למי שקרא אותו. במידה ואין מוצר כזה – Exception יבשר לנו על זה בצורה מסודרת.
כמובן שזו רק הדגמה שטחית ומאד מאד בסיסית ל-factory, במקור ה-factory יכול לעשות הרבה יותר מאשר טעינה בלבד של קובץ ויצירה של אובייקט – מה בדיוק? זה תלוי בכם ובמה שאתם צריכים. אבל כדאי מאד להשתמש ב-Factory שמלבד MVC היא אחת מתבניות העיצוב הפופולריות ביותר. יש הגדרות יותר מתקדמות ל-Factory שבהם מגדירים מה 'מותר' ומה 'אסור', אני אוהב להראות דברים בת'כלס – ובת'כלס ככה עובד Factory. אם להשתמש בו או לא, זו הבחירה שלכם.