یکی دیگر از کاربردهای Action و Func، امکان حذف و بازنویسی switch statements بسیار حجیم و طولانی به نحوی شکیل است؛ و در ادامه این نوع Refactoring را بررسی خواهیم کرد.
در ابتدا مثال زیر را که از یک سوئیچ، برای انتخاب نوع حرکت و اعمال آن استفاده میکند، در نظر بگیرید:
یک کلاس ساده که بر اساس مقدار enum دریافتی، حرکت به چهار جهت را سبب خواهد شد. اکنون میخواهیم با استفاده از Actionها نحوه تعریف متدهای حرکت را به فراخوان واگذار (کلاسی بسته برای تغییر اما باز برای توسعه) و به علاوه switch را نیز کلا حذف کنیم:
توضیحات:
استفاده از Dictionary برای حذف سوئیچ بسیار مرسوم است. خصوصا زمانیکه توسط switch صرفا قرار است یک مقدار ساده بازگشت داده شود. در این حالت میتوان کل سوئیچ را حذف کرد. caseهای آن تبدیل به keyهای یک Dictionary و مقادیری که باید بازگشت دهد، تبدیل به valueهای متناظر خواهند شد.
اما در اینجا مساله اندکی متفاوت است و خروجی خاصی مد نظر نیست؛ بلکه در هر قسمت قرار است کار مفروضی انجام شود. بنابراین اینبار کلیدهای Dictionary بازهم بر اساس مقادیر caseهای تعریف شده (در اینجا enum ایی به نام Direction) و مقادیر آن نیز Action تعریف خواهند شد. همچنین برای اینکه بتوان امکان تعریف قالبی (کلاسی) را برای تعاریف متدهای متناظر با اعضای enum نیز میسر کرد، این Dictionary را در یک interface قرار دادهایم تا کلاسهای پیاده سازی کننده آن، تعریف متدها را نیز دربر داشته باشند.
همانطور که ملاحظه میکنید، اینبار تعاریف متدهای Move از کلاس Sample5 خارج شدهاند، به علاوه برای دسترسی به آنها نیز switch ایی تعریف نشده و از Action استفاده شده است.
نهایتا اینبار متد جدید Move تعریف شده، با اینترفیس IMove کار میکند که یک Dictionary حاوی متدهای متناظر با اعضای enum جهت را ارائه میدهد.
یک نکته تکمیلی:
متد NewMove را به شکلهای زیر نیز میتوان پیاده سازی کرد:
در ابتدا مثال زیر را که از یک سوئیچ، برای انتخاب نوع حرکت و اعمال آن استفاده میکند، در نظر بگیرید:
using System; namespace ActionFuncSamples { public enum Direction { Up, Down, Left, Right } public class Sample5 { public void Move(Direction direction) { switch (direction) { case Direction.Up: MoveUp(); break; case Direction.Down: MoveDown(); break; case Direction.Left: MoveLeft(); break; case Direction.Right: MoveRight(); break; } } private void MoveRight() { Console.WriteLine("MoveRight"); } private void MoveLeft() { Console.WriteLine("MoveLeft"); } private void MoveDown() { Console.WriteLine("MoveDown"); } private void MoveUp() { Console.WriteLine("MoveUp"); } } }
public interface IMove { Dictionary<Direction, Action> MoveMap { get; } } public class Move : IMove { public Dictionary<Direction, Action> MoveMap { get { return new Dictionary<Direction, Action> { { Direction.Up, MoveUp}, { Direction.Down, MoveDown}, { Direction.Left, MoveLeft}, { Direction.Right, MoveRight} }; } } private void MoveRight() { Console.WriteLine("MoveRight"); } private void MoveLeft() { Console.WriteLine("MoveLeft"); } private void MoveDown() { Console.WriteLine("MoveDown"); } private void MoveUp() { Console.WriteLine("MoveUp"); } } public class Sample5 { public void NewMove(IMove move, Direction dirction) { foreach (var item in move.MoveMap) { if (item.Key == dirction) { item.Value(); //run action return; } } }
توضیحات:
استفاده از Dictionary برای حذف سوئیچ بسیار مرسوم است. خصوصا زمانیکه توسط switch صرفا قرار است یک مقدار ساده بازگشت داده شود. در این حالت میتوان کل سوئیچ را حذف کرد. caseهای آن تبدیل به keyهای یک Dictionary و مقادیری که باید بازگشت دهد، تبدیل به valueهای متناظر خواهند شد.
اما در اینجا مساله اندکی متفاوت است و خروجی خاصی مد نظر نیست؛ بلکه در هر قسمت قرار است کار مفروضی انجام شود. بنابراین اینبار کلیدهای Dictionary بازهم بر اساس مقادیر caseهای تعریف شده (در اینجا enum ایی به نام Direction) و مقادیر آن نیز Action تعریف خواهند شد. همچنین برای اینکه بتوان امکان تعریف قالبی (کلاسی) را برای تعاریف متدهای متناظر با اعضای enum نیز میسر کرد، این Dictionary را در یک interface قرار دادهایم تا کلاسهای پیاده سازی کننده آن، تعریف متدها را نیز دربر داشته باشند.
همانطور که ملاحظه میکنید، اینبار تعاریف متدهای Move از کلاس Sample5 خارج شدهاند، به علاوه برای دسترسی به آنها نیز switch ایی تعریف نشده و از Action استفاده شده است.
نهایتا اینبار متد جدید Move تعریف شده، با اینترفیس IMove کار میکند که یک Dictionary حاوی متدهای متناظر با اعضای enum جهت را ارائه میدهد.
یک نکته تکمیلی:
متد NewMove را به شکلهای زیر نیز میتوان پیاده سازی کرد:
// or ... move.MoveMap[dirction](); // or ... Action action; if(move.MoveMap.TryGetValue(dirction, out action)) action();