۱۳۹۰/۰۹/۰۵

آشنايي با Refactoring - قسمت 14


در بسياري از زبان‌هاي برنامه نويسي امكان null بودن Reference types وجود دارد. به همين جهت مرسوم است پيش از استفاده از آن‌ها، بررسي شود آيا شيء مورد استفاده نال است يا خير و سپس براي مثال متد يا خاصيت مرتبط با آن فراخواني گردد؛ در غير اينصورت برنامه با يك استثناء خاتمه خواهد يافت.
مشكلي هم كه با اين نوع بررسي‌ها وجود دارد اين است كه پس از مدتي كد موجود را تبديل به مخزني از انبوهي از if و else ها خواهند كرد كه هم درجه‌ي پيچيدگي متدها را افزايش مي‌دهند و هم نگهداري ‌آن‌ها را در طول زمان مشكل مي‌سازند. براي حل اين مساله، الگوي استانداردي وجود دارد به نام null object pattern؛ به اين معنا كه بجاي بازگشت دادن null و يا سبب بروز يك exception شدن، بهتر است واقعا مطابق شرايط آن متد يا خاصيت، «هيچ‌كاري» را انجام نداد. در ادامه، توضيحاتي در مورد نحوه‌ي پياده سازي اين «هيچ‌كاري» را انجام ندادن، ارائه خواهد شد.


الف) حين معرفي خاصيت‌ها از محافظ استفاده كنيد.

براي مثال اگر قرار است خاصيتي به نام Name را تعريف كنيد كه از نوع رشته‌ است، حالت امن آن رشته بجاي null بودن، «خالي» بودن است. به اين ترتيب مصرف كننده مدام نگران اين نخواهد بود كه آيا الان اين Name نال است يا خير. مدام نياز نخواهد داشت تا if و else بنويسد تا اين مساله را چك كند. نحوه پياده سازي آن هم ساده است و در ادامه بيان شده است:

private string name = string.Empty;
public string Name
{
    get { return this.name; }
    set 
    {
        if (value == null)
        {
            this.name = "";
            return;
        }
        this.name = value; 
    }
}

دقيقا در زمان انتساب مقداري به اين خاصيت، بررسي مي‌شود كه آيا مثلا null است يا خير. اگر بود، همينجا و نه در كل برنامه، مقدار آن «خالي» قرار داده مي‌شود.

ب) سعي كنيد در متدها تا حد امكان null بازگشت ندهيد.

براي نمونه اگر متدي قرار است ليستي را بازگشت دهد:

public IList<string> GetCultures()
{
    //...
}

و حين تهيه اين ليست، عضوي مطابق منطق پياده سازي آن يافت نشد، null را بازگشت ندهيد؛ يك new List خالي را بازگشت دهيد. به اين ترتيب مصرف كننده ديگري نيازي به بررسي نال بودن خروجي اين متد نخواهد داشت.


ج) از متدهاي الحاقي بجاي if و else استفاده كنيد.

پياده سازي حالت الف زماني ميسر خواهد بود كه طراح اصلي ما باشيم و كدهاي برنامه كاملا در اختيار ما باشند. اما در شرايطي كه امكان دستكاري كدهاي يك كتابخانه پايه را نداريم چه بايد كرد؟ مثلا دسترسي به تعاريف كلاس XElement دات نت فريم ورك را نداريم (يا حتي اگر هم داريم، تغيير آن تا زمانيكه در كدهاي پايه اعمال نشده باشد، منطقي نيست). در اين حالت مي‌شود يك يا چند extension method را طراحي كرد:

public static class LanguageExtender
{
        public static string GetSafeStringValue(this XElement input)
        {
            return (input == null) ? string.Empty : input.Value;
        }

        public static DateTime GetSafeDateValue(this XElement input)
        {
            return (input == null) ? DateTime.MinValue : DateTime.Parse(input.Value);
        }
}

به اين ترتيب مي‌توان امكانات كلاس پايه‌‌اي را بدون نياز به دسترسي به كدهاي اصلي آن مطابق نياز‌هاي خود تغيير و توسعه داد.