۱۳۹۰/۰۷/۱۵

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



قسمت چهار آشنايي با Refactoring به معرفي روش «انتقال متدها» اختصاص دارد؛ انتقال متدها به مكاني بهتر. براي نمونه به كلاس‌هاي زير پيش از انجام عمل Refactoring دقت كنيد:

namespace Refactoring.Day3.MoveMethod.Before
{
    public class BankAccount
    {
        public int AccountAge { get; private set; }
        public int CreditScore { get; private set; }

        public BankAccount(int accountAge, int creditScore)
        {
            AccountAge = accountAge;
            CreditScore = creditScore;
        }
    }
}


namespace Refactoring.Day3.MoveMethod.Before
{
    public class AccountInterest
    {
        public BankAccount Account { get; private set; }

        public AccountInterest(BankAccount account)
        {
            Account = account;
        }

        public double InterestRate
        {
            get { return CalculateInterestRate(); }
        }

        public bool IntroductoryRate
        {
            get { return CalculateInterestRate() < 0.05; }
        }

        public double CalculateInterestRate()
        {
            if (Account.CreditScore > 800)
                return 0.02;

            if (Account.AccountAge > 10)
                return 0.03;

            return 0.05;
        }
    }
}


قسمت مورد نظر ما در اينجا، متد AccountInterest.CalculateInterest است. كلاس AccountInterest مرتبا نياز دارد كه از اطلاعات فيلدها و خواص كلاس BankAccount استفاده كند (نكته تشخيص نياز به اين نوع Refactoring). بنابراين بهتر است كه اين متد را به همان كلاس تعريف كننده‌ي فيلدها و خواص اصلي آن انتقال داد. پس از اين نقل و انتقالات خواهيم داشت:

namespace Refactoring.Day3.MoveMethod.After
{
    public class BankAccount
    {
        public int AccountAge { get; private set; }
        public int CreditScore { get; private set; }

        public BankAccount(int accountAge, int creditScore)
        {
            AccountAge = accountAge;
            CreditScore = creditScore;
        }

        public double CalculateInterestRate()
        {
            if (CreditScore > 800)
                return 0.02;

            if (AccountAge > 10)
                return 0.03;

            return 0.05;
        }
    }
}

namespace Refactoring.Day3.MoveMethod.After
{
    public class AccountInterest
    {
        public BankAccount Account { get; private set; }

        public AccountInterest(BankAccount account)
        {
            Account = account;
        }

        public double InterestRate
        {
            get { return Account.CalculateInterestRate(); }
        }

        public bool IntroductoryRate
        {
            get { return Account.CalculateInterestRate() < 0.05; }
        }
    }
}



به همين سادگي!

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

namespace Refactoring.Day4.MoveMethod.Ex2.Before
{
    public class CarEngine
    {
        public float LitersPerCylinder { set; get; }
        public int NumCylinders { set; get; }

        public CarEngine(int numCylinders, float litersPerCylinder)
        {
            NumCylinders = numCylinders;
            LitersPerCylinder = litersPerCylinder;
        }
    }
}


namespace Refactoring.Day4.MoveMethod.Ex2.Before
{
    public class Car
    {
        public CarEngine Engine { get; private set; }

        public Car(CarEngine engine)
        {
            Engine = engine;
        }

        public float ComputeEngineVolume()
        {
            return Engine.LitersPerCylinder * Engine.NumCylinders;
        }
    }
}

در اينجا هم متد Car.ComputeEngineVolume چندين‌بار به اطلاعات داخلي كلاس CarEngine دسترسي داشته است؛ بنابراين بهتر است اين متد را به جايي منتقل كرد كه واقعا به آن تعلق دارد:

namespace Refactoring.Day4.MoveMethod.Ex2.After
{
    public class CarEngine
    {
        public float LitersPerCylinder { set; get; }
        public int NumCylinders { set; get; }

        public CarEngine(int numCylinders, float litersPerCylinder)
        {
            NumCylinders = numCylinders;
            LitersPerCylinder = litersPerCylinder;
        }

        public float ComputeEngineVolume()
        {
            return LitersPerCylinder * NumCylinders;
        }
    }
}

namespace Refactoring.Day4.MoveMethod.Ex2.After
{
    public class Car
    {
        public CarEngine Engine { get; private set; }

        public Car(CarEngine engine)
        {
            Engine = engine;
        }
    }
}

بهبودهاي حاصل شده:
يكي ديگر از اصول برنامه نويسي شيء گرا "Tell, Don't Ask" است؛ كه در مثال‌هاي فوق محقق شده. به اين معنا كه: در برنامه نويسي رويه‌اي متداول، اطلاعات از قسمت‌هاي مختلف كد جاري جهت انجام عملي دريافت مي‌شود. اما در برنامه نويسي شيء گرا به اشياء گفته مي‌شود تا كاري را انجام دهند؛ نه اينكه از آن‌ها وضعيت يا اطلاعات داخلي‌اشان را جهت اخذ تصميمي دريافت كنيم. به وضوح، متد Car.ComputeEngineVolume پيش از Refactoring ، اصل كپسوله سازي اطلاعات كلاس CarEngine را زير سؤال برده است. بنابراين به اشياء بگوئيد كه چكار كنند و اجازه دهيد تا خودشان در مورد نحوه‌ي انجام آن، تصميم گيرنده نهايي باشند.