Design Patterns – Strategy Pattern

بابک فخریلو

کمتر پيش اومده که بخوام در مورد توسعه ي برنامه ها (Development) بنويسم. دليل خاصي نداشته. ولي خوب تصميم دارم در اين حوزه مطالب بيشتر روي منتشر کنم. يه مدتي هست که به Design Pattern ها خيلي توجه دارم. البته خواه يا نا خواه ما همگي داريم ازشون استفاده مي کنيم، مثلا همين MVC که يه جورايي Pattern محسوب ميشه (خيلي ها ميگن Framework يا هر چيزي، به هر حال نظم ميده به توسعه ي برنامه و همين مهمه)، و من مدتي هست باهاش دارم کار مي کنم.

حالا اصلا Design Pattern به چه دردي مي خوره ؟ ترجمه لغت به لغت اين عبارت، توصيف خيلي مقدماتي و ساده اي از اون به ما ارائه مي ده. «الگوي طراحي»، يعني طوري برنامه نويسي و توسعه رو پيش ببريم که بر اساس الگوهاي موفق تجربه شده در گذشته باشه، تا بدين ترتيب، Modularity و Extensibility و Ease of re-use برنامه اي که در حال توسعه اش هستيم، رو بيشتر کنيم. قبل همه چي بگم که براي خوندن مقالاتي از اين دست بايد به خوبي مفهوم شي گرايي رو درک کرده باشيد و دست کم به يک زبان شي گرا (مثل C#، Java يا Python و …) تسلط در حد برنامه نوسي شي گرا داشته باشيد.

مقاله هايي که در نظر دارم، از سرچشمه هاي معتبري از اينترنت هستند، و سعي کردم صرف ترجمه نباشه و از تجربيات خودم هم به مطلب اضافه کنم.

مطلب اول در اين زمينه رو با بررسي الگوي Strategy  شروع مي کنيم. به خاطر مثال خيلي ساده و قابل فهمي که تو مقاله اصلي اين مطلب ديدم، اين الگو رو براي شروع انتخاب کردم.

پيش نياز:

  • زبان برنامه نويسي شي گرا (ما در اينجا با C# کار مي کنيم)
  • آشنايي با وراثت و چند ريختي
  • آشنايي با Interface

تعريف

شايد بشه به طور غير رسمي، اين الگو رو به اين ترتيب شرح داد: «خانواده اي از الگوريتم ها تعريف کن، هر کدام را کپسوله کن، و آنها را قابل تعويض (interchangeable) کن». اين الگو به يک الگوريتم اجازه مي  دهد فارغ از client (در اينجا منظور استفاده کننده از آن الگوريتم است) آن الگوريتم، به طور مستقل تغيير کند.

به عبارتي ديگر، اين الگو مجموعه اي از توابع که کارهاي مشابه و نه يکسان انجام مي دهند را کپسوله مي کند. براي مثال، تصميم داريم يک فايل را encrypt کنيم، اما پيش از انتخاب يک روش براي رمز کردن آن، الگوريتم هاي مختلف encryption را تست مي کنيم.

ساده تر بخواهيم تعريف کنيم، اين الگو به ما اجازه مي دهد تا بدون ترس از اتخاذ تصميم هايي که در آينده کد ما را دچار تغيير کنند، برنامه ي خود را توسعه دهيم. با بهره گيري از اين الگو، با کمترين تلاش، بيشترين تغييرات را مي توانيم اعمال کنيم.

دست به کار شويم

من هم مثل شما تا اينجاي کار خسته شدم از تعريف و   توضيح ها. بايد دست به کار شد و در عمل کارکرد اين الگو رو ديد. Hash کردن يک رشته در برنامه نويسي کار متداولي به حساب مي آيد. اما به خاطر روش هاي مختلفي که حتي مثل من ممکنه اسم شون رو هم نديده باشيد، روش هاي مختلفي براي پياده سازي اون وجود داره.

فرض کنيم ما براي يک شرکت کار مي کنيم، و حالا اين شرکت براي امنيت محصولات اش، نياز به اين داره که رشته ها رو بر اساس الگوريتم MD5 Hash، رمز کنه.

براي اين که در همه جاي برنامه بتونيم کار خودمون رو تست بکنيم، يک متد کمکي به شکل زير تعريف مي کنيم:

string ByteToString(Byte[] bytes)
{
StringBuilder builder = new StringBuilder();
// Loop through each byte of the data
// and format each one as a hexadecimal string.
for (int i = 0; i < bytes.Length; i++)
{
builder.Append(bytes[i].ToString("x2"));
}
return builder.ToString();
}

و يک متد هم به شکل زير رشته ي «Web Biscuit» رو به عنوان رشته ي تست براي رمزگذاري، بررسي مي کنه. (تو مقاله ي اصلي به استفاده از Unit Testing پرداخته بود که من به خاطر کاهش پيچيدگي، از توضيح اين کار صرف نظر مي کنم)

public bool TestMD5()

{

//MySecurity obj = new MySecurity();

string md5WebBiscuit = "26c6d2e0cfbbe42fff4bb9c1e8dece7d";

string hashed = ByteToString(Hash("Web Biscuit"));

return md5WebBiscuit.Equals(hashed);

}

راه حل اول

خوب شروع مي کنيم به پياده سازي روش MD5 :

public byte[] Hash(string input)

{

MD5 hasher = MD5.Create();

return hasher.ComputeHash(Encoding.Default.GetBytes(input));

}

در حقيقت ما کدي پياده سازي نکرديم، مايکروسافت قبلا زحمت اين کار رو کشيده و ما صرفا ازش استفاده مي کنيم. کلاس MD5 رو در فضا نام System.Security.Cryptography مي تونيد پيدا کنيد.

خوب، مي تونيم از متد TestMD5 استفاده کنيم و ببينيم که آيا کار مي کنه يا نه. و کار کرد.

شب سياه يک برنامه نويس

مشکل راه حل اول اين که سيستم ما توسعه پذير (extensible) نيست. بعد از چند ماه، کتابخانه ي حاوي اين الگوريتم رو توزيع مي کنيم و به دست ميليون ها نفر مي رسونيم.

اما بعد مشخص ميشه که زياد نمي شه به MD5 اعتماد کرد!

حالا رئيس شرکت به ما دستور ميده که از الگوريتم SHA256 استفاده کنيم. البته همچنان بايد از تابع قبلي هم در برنامه پشتيباني کنيم تا مشکلي پيش نياد، و مجبور ميشيم چنين چيزي بنويسم:

public byte[] Hash(string input, string type)

{

if (type == "MD5")

{

MD5 hasher = MD5.Create();

return hasher.ComputeHash(Encoding.Default.GetBytes(input));

}

else if (type == "SHA256")

{

SHA256 hasher = new SHA256Managed();

return hasher.ComputeHash(Encoding.Default.GetBytes(input));

}

else

{

return input; // Or throw exception?

}

}

و متد پيش فرض هم به اين شکل تغيير مي کنه

public byte[] Hash(string input)

{

return Hash(input, "MD5");

}

با گذشت زمان، مجبور هستيم الگوريتم هاي hashing بيشتري رو پشتيباني کنيم. اما با استفاده از روش بالا، تا بخوايم به خودمون بجنبيم، کد ما پر ميشه از دستورات else/if.

راه حل Strategy Pattern

خوب قانون طلايي همه ي الگوهاي طراحي، اينجا هم مطرح مي شود، کدي که قرار است تغيير کند را کپسوله کنيد. و آنچه که در مساله ما تغيير مي کند، الگوريتم رمز کردن است. شايد عادت نداشته باشيد که به الگويتم ها به عنوان اشيايي از سيستم نگاه کنيد، اما با استفاده از الگوي strategy ، مي توانيم يک الگوريتم به يک شي تبديل کنيم.

کاري که انجام مي دهيم، انتزاع پياده سازي از خود عمل است!!! يعني چي ؟! يعني عمل، بايد فقط بداند چه کاري را بايد انجام دهد، نه اين که چطور کار خواسته از آن را انجام دهد!! اين کار را با فرستادن يک پارامتر ديگر به متد Hash انجام مي دهيم. خوب بدين ترتيب همه ي الگوريتم ها را مي توانيم تحت پوشش قرار دهيم، حتي آنهايي که هنوز توسعه پيدا نکرده اند!

از يک Interface براي انجام اين کار استفاده مي کنيم. با ورود بيشتر به دنياي الگو هاي طراحي متوجه خواهيد شد که اين الگو ها از مفهموم انتزاع (abstraction) استفاده ي زيادي مي کنند و يکي از روش هاي پياده سازي انتزاع، استفاده از Interface است.

خوب بياييد نگاهي به شکل جديد متد Hash داشته باشيم:

public byte[] Hash(string input, IHasher hasher)
{
    return hasher.Hash(Encoding.Default.GetBytes(input));
}

خوب اين با چيزي که قبلا ايجاد کرديم خيلي فرق دارد، اما نه خيلي زياد. ديگر متدي از کلاس MD5 را فراخواني نمي کنيم. اين تابع اصلا هيچ چيزي در مورد الگوريتم  MD5 نمي داند. چرا که ما از طريق انتزاع مي خواهيم رشته خود را Hash کنيم. در اينجا وظيفه ي شي hasher است که مشخص کند با چه الگوريتمي سر و کار دارد.

خوب واسطي که تعريف مي کنيم، به سادگي زير است:

public interface IHasher

{

byte[] Hash(byte[] input);

}

حالا نوبت به کلاس هاي مي رسد که با بهره گيري از Interface بالا، به پياده سازي روش هاي مختلف hashing مي پردازند (اصلاحا اين کلاس ها را که وظيفه ي پياده سازي عملکردهاي تعريف شده در Interface را دارند، Concrete مي گويند):

public class MD5Hasher : IHasher

{

#region IHasher Members

public byte[] Hash(byte[] input)

{

MD5 hasher = MD5.Create();

return hasher.ComputeHash(input);

}

#endregion

}

الگوريتم SHA256

public class SHA256Hasher : IHasher

{

#region IHasher Members

public byte[] Hash(byte[] input)

{

SHA256 hasher = new SHA256Managed();

return hasher.ComputeHash(input);

}

#endregion

}

حالا متد مخصوص تست کردن را تغيير مي  دهيم، تا به شکل زير شود:

public bool TestMd5StrategyPattern()

{

           string md5WebBiscuit = "26c6d2e0cfbbe42fff4bb9c1e8dece7d";

            string hashed=Hash("Web Biscuit", new MD5Hasher()));

            return md5WebBiscuit.Equals(hashed);

}

و يک متد هم براي تست کردن روش SHA256 مي نويسيم:

public bool TestSHA256StrategyPattern()

{
string sha256WebBiscuit = "2072a868e8c5fa8331cad489" + "bc63e6aff3ba8ccc057142c284ad0379e5becf8a";
string hashed=Hash("Web Biscuit", new SHA256Hasher()));

return sha256WebBiscuit.Equals(hashed);
}

خودتان تست کنيد ببينيد درست کار مي کند يا نه؟!

مي بينيد که ديگر نيازي به استفاده از خطوط متوالي و خسته کننده ي else/if براي تعيين نوع الگوريتم مورد نظر، نيست. از طرفي تغييرات به سادگي و با کم ترين هزينه مي توانند اعمال شوند. براي بهينه تر کردن روش خود و همچنين پايبندي به همان اصل کپسوله سازي مي توانيم هنگام تعيين الگوريتم، از الگوي Factory استفاده کنيم. توضيح در مورد اين الگو رو به مقاله هاي بعدي موکول مي کنم.

پيروز باشيد.

برگرفته از:

 

دربارهٔ Persian Developer

I Love Developing applications

Posted on ژوئن 12, 2012, in توسعه نرم افزار and tagged , , . Bookmark the permalink. 4 دیدگاه.

  1. بسیار عالی…

  2. فوق العاده بود و بی صبرانه منتظر مقاله های بعدی هستم .

    فقط اگر کد ها رو بتونی توی تگ code بنویسی خیلی عالی میشه اگر یادت باشه قبلا با هم تست کردیم نشد. اگر راهش رو پیدا کردی اطلاع بده تا کدهایی که می خواهیم بنویسیم درست نمایش داده بشه .

    ممنون و خسته نباشی

  3. سپاس وحید.
    سعی ام رو می کنم کدها رو طوری بنویسم که واضح تر باشه و بشه کپی اش کرد.

  4. سلام
    ممنون از مقاله خوبتون

پاسخی بگذارید

در پایین مشخصات خود را پر کنید یا برای ورود روی شمایل‌ها کلیک نمایید:

نشان‌وارهٔ وردپرس.کام

شما در حال بیان دیدگاه با حساب کاربری WordPress.com خود هستید. بیرون رفتن / تغییر دادن )

تصویر توییتر

شما در حال بیان دیدگاه با حساب کاربری Twitter خود هستید. بیرون رفتن / تغییر دادن )

عکس فیسبوک

شما در حال بیان دیدگاه با حساب کاربری Facebook خود هستید. بیرون رفتن / تغییر دادن )

عکس گوگل+

شما در حال بیان دیدگاه با حساب کاربری Google+ خود هستید. بیرون رفتن / تغییر دادن )

درحال اتصال به %s

%d وب‌نوشت‌نویس این را دوست دارند: