تولید نرم افزار با الگو

وبلاگ شخصی ابراهیم خانی

۲ مطلب در ارديبهشت ۱۳۹۵ ثبت شده است

Type code

Type Code یا همان Enumeration ها، نوع داده هایی هستند که تعداد محدودی مقدار دارند که این مقادیر برای مشخص کردن یک حالت خاص به کار می روند. به هنگام استفاده از Type code ها سه وضعیت به وجود می آید؛

  •  Type code مبنای تصمیم گیری نیست
  •  Type code مبنای تصمیم گیری است و پس از انتصاب آن به یک شی، تغییر نمی کند
  •  Type code مبنای تصمیم گیری است و پس از انتصاب آن به یک شی، امکان تغییر آن وجود دارد

با توجه به هریک از این وضعیت ها الگوی خاصی برای بازآرایی [1] در کتاب ارزشمند [2] آورده شده است.

برای روشن شدن بحث از یک مثال ملموس استفاده شده است. انسان ها در جامعه یا ثروتمند هستند و یا فقیر. یک راه کار برای نشان دادن این وضعیت تصویر کردن دامنه این حالت به اعداد است. (مثلا عدد 1 نشان دهنده وضعیت ثروتمند و عدد 2 نشان دهنده وضعیت فقیر). یک راه کار برای آشکار کردن این اطلاعات ضمنی نام دادن به هر یک از این حالات است. راه حلی مانند آنچه در شکل زیر آمده است، با توجه به اینکه دامنه مقادیر ورودی به سازنده این کلاس محدود به اعداد 1و2 نیست، منشا بسیاری از مشکلات است.


public class Person
{
    public static int Wealthy => 1;
    public static int Poor => 2;
    public int Status { get; set; }

    private Person()
    {
    }

    public Person(int status) : this()
    {
        Status = status;
    }
}


پیشنهاد Fowler برای غلبه بر این مشکل (محدود کردن دامنه انتخاب مقدار) به این صورت است. در واقع در این راه حل با بهره گیری از Type system دامنه انتخاب مقدار برای وضعیت مالی محدود شده است.


public class Person
{
    public Status Status { get; set; }
    private Person()
    {

    }
    public Person(Status status) : this()
    {
        Status = status;
    }
}

public class Status
{
    public int StatusNumber { get; }
    public static Status Wealthy = new Status(1);
    public static Status Poor = new Status(2);
    private Status()
    {
    }
    private Status(int statusNumber) : this()
    {
        StatusNumber = statusNumber;
    }
}


اما در زبان #C  با توجه به اینکه Enumeration ها strongly typed هستند، برای وضعیتی که Type code تغییر نمی کند و مبنای تصمیم گیری نیست می توان با کد زیر این وضعیت را مدل کرد.


public class Person
{
    public Status Status { get; set; }

    private Person()
    {
    }

    public Person(Status status) : this()
    {
        Status = status;
    }
}

public enum Status
{
    Wealthy,
    Poor
}

اما در وضعیت دوم، یعنی زمانی که Type code مبنای تصمیم گیری است (یارانه افراد بر اساس وضعیت مالی آنها پرداخت می شود) و امکان تغییر آن وجود ندارد (فرد فقیر ثروتمند نمی شود و برعکس). یک پیاده سازی برای این وضعیت در شکل زیر آمده است. ایراد این پیاده سازی استفاده از Switch روی حالت است. این کار باعث نقض اصل OCP می شود چرا که با اضافه شدن یک حالت دیگر (مثلا حالت متوسط اقصادی) تابع GetSubsidy تغییر می کند.


public class Person
{
    public Status Status { get; set; }
    private Person()
    {
    }
    public Person(Status status) : this()
    {
        Status = status;
    }
    public decimal GetSubsidy()
    {
        switch (Status)
        {
            case Status.Wealthy:
                return 0m;
            case Status.Poor:
                return 100m;
            default:
                throw new ArgumentOutOfRangeException();
        }
    }
}

در واقع در این وضعیت با رفتار مبتنی بر نوع رو به رو هستیم که در دنیای شی گرا معادل تعریف یک شی جدید است. در این حالت با توجه به اینکه مقدار داده شمارشی (وضعیت مالی) پس از انتصاب تغییر نمی کند، یک نوع انسان جدید برای وضعیت مالی تعریف و رفتار وابسته به این نوع در آن قرار داده می شود. کد بازآرایی شده برای این وضعیت در شکل زیر آورده شده است. در این صورت با اضافه کردن وضعیت مالی جدید، کلاس های موجود تغییر نمی کند.


public abstract class Person
{
    public abstract decimal GetSubsidy();
}

public class WealtyPerson : Person
{
    public override decimal GetSubsidy()
    {
        return 0m;
    }
}

public class PoorPerson : Person
{
    public override decimal GetSubsidy()
    {
        return 100m;
    }
}

اما در وضعیت سوم که Type code مبنای تصمیم گیری است و مقدار آن برای شی ای که به آن تعلق دارد تغییر می کند (امکان ثروتمند شدن فقرا و برعکس وجود دارد) استفاده از پیاده سازی قبلی باعث می شود برای هر تغییر حالت، یک شی جدید ایجاد شود. به علاوه با توجه به اینکه شی انسان قاعدتا از نوع Entity است، می بایست mutable باشد (توصیه DDD) که با این پیاده سازی Immutable خواهد بود. در این وضعیت با رفتار وابسته به حالت رو به رو هستیم که معرف Polymorphism در شی گرایی است. برای باز آرایی این حالت از الگوی State و یا Strategy استفاده می شود.  در این حالت تغییر وضعیت مالی با استفاده از Interface از کلاس Person پنهان می شود.


public abstract class Person
{
    private IPersonState _personState;

    private Person()
    {
    }

    protected Person(IPersonState personState) : this()
    {
        _personState = personState;
    }
}

public interface IPersonState
{
    decimal GetSubsidy();
}

public class WealtyPerson : IPersonState
{
    public decimal GetSubsidy()
    {
        return 0m;
    }
}

public class PoorPerson : IPersonState
{
    public decimal GetSubsidy()
    {
        return 100m;
    }
}



[1] - Refactoring

[2] - Refactoring: Improving the Design of Existing Code

۰ نظر موافقین ۰ مخالفین ۰
ابراهیم خانی

software design patterns

الگوها برای اولین بار توسط Christopher Alexander معرفی شدند. او که استاد بازنشته دانشگاه کالیفرنیا در رشته معماری است، در سال 1977 کتاب خود با عنوان [1] مفهوم زبان الگو را اینگونه معرفی می کند : "فرآیند ساخت همه بنا های زیبا یکسان است. برای آشکار کردن آن می بایست دو کار انجام داد. ابتدا باید هر سازه را به کوچکترین جزء سازنده آن تجزیه کرد و سپس فرایندی برای کنار هم گذاشتن این اجزا تعریف کرد." او این اجزاء را الگو و فرایند کنار هم قرار دادن آنها را زبان الگو می نامد. (تاثیر این دیدگاه را به وضوح در کتاب [2] مشاهده کرد.) در نگاه Alexander الگوها منشا زیبایی هستند و زیبایی را نه با ابداع بلکه با ترکیب الگوها به وسیله یک زبان الگو ایجاد کرد که حاصل این ترکیب پدیده ای است که Alexander آن را "کیفیت بدون نام" [3] می نامد. Kent Beck در سال 1987 در مقاله ای به منظور مطرح کردن استفاده از ایده Alexander در نرم افزار، پنج الگو را در زبان Smalltalk پیاده سازی کرد. Eric Gamma در رساله دکترای خود در سال 1991 به کاربرد الگوها در طراحی نرم افزار اشاره کرد. اما نقطه عطف استفاده از الگوها در نرم افزار بدون تردید چاپ کتاب [4] است.

در مهندسی نرم افزار تعاریف مختلفی برای الگوها وجود دارد :

  •      Eric Gamma : پاسخی برای یک مشکل تکرار شونده در یک حوزه خاص است.
  •      Dirk Riehle : انتزاعی از یک شکل محسوس که در یک زمینه خاص به صورت مکرر اتفاق می افتد.
  •      Brad Appleton : قطعه ای از دانش که ماهیت یک خانواده از پاسخ ها به مشکلات تکرار شونده در یک دامنه خاص را در بر می گیرد.

با نگاه مجرد به این تعاریف، می توان یک تعریف ساده و فراگیر برای الگوها ارایه کرد. در این تعریف هر الگو از سه قسمت تشکیل شده است، وضعیت بد [5]، وضعیت خوب [6] و مجموعه ای از الزامات [7]. در واقع الگوها راهکاری برای تبدیل وضعیت بد به وضعیت خوب، تحت الزماتی مشخص است. الگوها در سطوح مختلفی از نظر ریزدانگی [8] وجود دارند.

  •      الگوهای فرایند
  •      الگوهای مهندسی مجدد
  •      الگوهای معماری
  •      الگوهای طراحی
  •      الگوهای پیاده سازی

یکی از مشکلات اصلی در زمینه الگوها مربوط به مستند سازی آنها است. الگوها در متون مختلف با قالب های مختلف مستند شده اند که این کار جمع آوری و ترکیب آنها را مشکل می کند.

اما یک سوال مهم. اصولا فایده استفاده از الگوها در مهندسی نرم افزار چیست؟ در مرحله طراحی نرم افزار تصمیمات مهمی راجع به نرم افزار اتخاذ می شود که کیفیت نرم افزار نهایی به شدت به صحت و سقم این تصمیمات وابسته است.  با توجه به اینکه این تصمیم ها در سطح کلان مطرح می شوند، بالطبع اتخاذ تصمیم ها نادرست، هزینه های سنگینی به دنبال خواهد داشت. چرا که نتیجه این تصمیمات زمانی مشخص می شود که نرم افزار تولید شده است. برای اطمینان از صحت تصمیمات اتخاذ شده دو راه کار وجود دارد :

  •  آزمودن تصمیم های مطرح شده :  با استفاده از رویکرد تکراری افزایشی [9] و نمونه سازی اولیه [10].
  •   اتخاذ تصمیم های آزموده شده : استفاده مجدد [11] روش دیگر جهت رسیدن به این هدف است.

رویکرد اول در واقع همان رویکرد متدولوژی های چابک [12] است که صحبت در مورد آن موضوع کتاب ها مختلف است. اما در مورد رویکرد دوم باید گفت، استفاده مجدد در سطح پایین انتزاع (استفاده از مولفه [13]) مشکلاتی به دنبال خواهد داشت. از طرفی یافتن مولفه ای که تمامی نیازها را برآورده کند به ندرت عملی می شود و از طرف دیگر تغییر دادن مولفه در صورت امکان هم، خطراتی برای جامعیت مولفه به دنبال خواهد داشت. در واقع استفاده از الگوها، استفاده مجدد در سطح طراحی (سطحی مجرد تر از پیاده سازی) را فراهم می کنند. نکته ای که نباید از آن غافل شد اصالت الگوهاست (راه حل جدید الگو نیست) چرا که همانطور که اشاره شد، الگوها راه حل های شناخته شده هستند.



[1] - The Timeless Way of Building

[2] - Pattern Oriented Software Architecture Volume 5: On Patterns and Pattern Languages

[3] - Quality without a name

[4] - Design Patterns Elements Of Reusable Software Development

[5] - Bad smell

[6] - Good smell

[7] - Force

[8] - Granularity

[9] - Iterative-incremental

[10] - Prototyping 

[11] - Reuse

[12] - Agile

[13] - Module

۰ نظر موافقین ۰ مخالفین ۰
ابراهیم خانی