צימוד (Coupling) הוא מדד המתאר עד כמה קיימות תלויות הדדיות בין מודולים נפרדים.
צימוד גבוה בין מודולים מרמז על כך ששימוש חוזר (reuse) במודול אחד יביא להיגררות של מודולים נוספים אחריו ויחייב את התוכניתן לייבא גם אותם.
- צימוד נתונים (data coupling) - צימוד שבו הנתונים מתחלקים בין מודולים. זוהי צורת תכנות לא נכונה שמובילה לחוסר בהירות - "מי אחראי על כל נתון". למשל, אם נממש מודול שאחראי על חישוב ציוני סטודנט ומודול שיהיה אחראי על חישוב ממוצע לכלל הקורס - חייב להיות רק מודול אחד שיכיל בפועל את הציונים של הסטודנטים וכל שאר המודולים ייגשו אל אותו מודול כדי לקבל נתונים אלו.
- צימוד בקרה (control coupling) - צימוד שבו מודול אחד מעביר נתונים למודול אחר במטרה מפורשת להשפיע על פעולת המודול האחר.
int my_func (int Num1, int Num2, int what2do)
{
if (what2do = 1) // ADD
{
return Num1 + Num2;
}
else if (what2do = 2) // SUB
{
return Num1 - Num2;
}
}
ניתן לראות בדוגמה הנ"ל שהפונקציה תבצע פעם אחת חיבור ופעם אחת חיסור, כתלות בערך המועבר לה כפרמטר. העיקרון המנחה בכתיבת פונקציה וקביעת ייעודה הוא העיקרון הבא:
פונקציה תמיד תעשה אותו דבר ושמה מבטא פעולה זו. ראה דוגמת קוד תחת צימוד תוכן בעמוד זה.
- צימוד חיצוני (external coupling) - זהו סוג של צימוד שבו רכיב תוכנה כלשהו תלוי בתוכנה שנכתבה ע"י גורם שלישי או בסוג החומרה הספציפי עליו הוא אמור לרוץ.
לא נרחיב את המילים על סוג זה של צימוד, אך נגיד רק שזוהי תופעה נפוצה במערכות תוכנה גדולות בהן נדרש להחליט על ארכיטקטורה כתלות במוצרי המדף שבשימוש. דוגמאות למוצרי מדף שכאלה: ESRI MapObjects 8.0, Microsoft ADO וכו'.
- צימוד משותף (common coupling) - צימוד שבו מודולים או רכיבים שונים ניגשים (קוראים מ- או כותבים אל) משתנים או שטחי זיכרון משותפים ובדרך זו נעשים תלויים זה בזה. משתנים כאלה שניתן לגשת אליהם ממספר מקומות נקראים
משתנים גלובליים (Global Variables).
אם משתמשים בהם בחכמה, משתנים גלובליים יכולים להיות מאוד מועילים וזאת מבלי ליצור בלגאן בקוד. למשל, במערכות גדולות בהן נדרש לנהל קובץ לוג (Log) אליו כותבים במקרה של שגיאה בלתי צפויה, גישה ללוג דרך משתנה גלובלי היא מקובלת ונפוצה.
CLogFile GSystemLog; // Global log file
// Used for showing messages to the user
Class CMessager
{
public:
CMessager(const char* pMessageToShow)
{
if (pMessageToShow != NULL) // if the string is valid
m_pMessageToShow = pMessaheToShow; // saving it for later
else // invalid empty string
{
GSystemLog.WriteToLog ("invalid string recieved");
....
// of course, writing to the log is only a small part
// of handling such a problem...
}
}
void ShowMessage() const;
private:
char* m_MessageToShow;
}
// Second class using the same GSystemLog
Class CChessBoard
{
public:
CChessBoard ();
virtual ~CChessBoard();
void PlayGame ()
{
try
{
...
}
catch (...)
{
// in any exception that occurs, writing to the log that
// the error happened in this procedure
GSystemLog.WriteToLog ("Error in PlayGame");
}
}
private:
...
}
אך צימוד משותף בא להצביע דוקא על מקרה בו שיתוף המשתנים הוא מקור לבעיות וזה המקרה בדרך כלל. למשל, אם קובץ הלוג היה גם שומר נתונים כמו שם הפרוצדורה הפעילה ולא רק מבצע את פעולת הכתיבה לקובץ, אז בכל פרוצדורה שתרצה לכתוב ללוג נצטרך לזכור לאתחל את שם הפרוצדורה שהלוג שומר לפני ביצוע פעולת הכתיבה וזה המקום שבו נולדים באגים!
- צימוד תוכן (content coupling) - זהו צימוד שבו מודול אחד מתייחס למודול אחר או משנה את הנתונים הפנימיים של מודול אחר מבלי שהדבר נלקח בחשבון במודול האחרון.
מקרה זה לא אמור כלל להתאפשר אם הקפדנו להגדיר משתנים כ-public ו-private ובכך מנענו את גישת המודולים השכנים לנתונים של המודול שלנו.
class COneActionMath
{
public:
COneActionMath(const long m_Action);
long m_Action; // HERE IS THE MISTAKE! m_Action MUST BE PRIVATE
}
// Some other place in the code...
{
// Creating an object that exists only for doing adding actions
COneActionMath AddMath (ADD_ACTION);
AddMath.Compute (2,3); // should add 2 to 3
// Here comes the bug - AddMath will now subtract instead of add
AddMath.m_Action = SUB_ACTION;
AddMath.Compute (2,3); // now, instead of 5, we will get -1...
}