Controllerها به نوعی رابط بین View و Model هستند. ساده ترین محل برای قرار دادن کدهای تصمیم گیری (decision-making code) ، قرار دادن منطق تجاری و یا فراهم ساختن داده برای View مثل ایجاد یک لیست از Select List برای یک DropDownList میباشند. اما انجام این کارها به نرم افزار ما پیچیدگی تحمیل میکند. Controllerها باید در طول زمان توسعهی یک نرم افزار کم حجم و سبک باقی بمانند. در این مطلب بحث شد که یکی از اهداف استفاده از ASP.NET MVC نوشتن نرم افزار هایی تست پذیر میباشد. نوشتن آزمون واحد و نگهداری Controller هایی که مسئولیت زیادی بر عهده دارند سخت میباشد. Controllerها باید به نحوی توسعه پیدا کنند که نگهداری آنها ساده باشد ، تست پذیر باشند و اصل SRP را رعایت کرده باشند.
نگهداری آسان
کدی که درک آن سخت باشد ، تغییر دادن آن نیز سخت است ، هنگامی که درک کدی سخت باشد زمینه برای به وجود آمدن خطاها ، دوباره کاری و سردرد فراهم میشود. هنگامی که مسئولیت یک Action به صورت شفاف مشخص نباشد و انواع و اقسام کارها به آن سپرده شده باشد تغییر در آن سخت میشود ، ممکن است این تغییر باید چند جای دیگر هم داده شود در نتیجه فاز نگهداری هزینه و زمان اضافی به نرم افزار تحمیل میکند.
تست پذیری
بهترین راه برای اطمینان از این که درک و تست پذیری سورس کد ما ساده هست انجام و تمرین توسعهی تست محور (TDD) میباشد. هنگامی که از روش TDD استفاده میکنیم با سورسی کدی کار میکنیم که هنوز وجود ندارد. در این مرحله از به وجود آمدن کلاس هایی که تست آنها دشوار یا غیر ممکن است (به دلیل داشتن مسئولیتهای اضافی) از جمله Controllerها جلوگیری میشود. نوشتن آزمونهای واحد برای Controllerهای کم حجم ساده میباشد.
تمرکز بر روی یک مسئولیت
بهترین راه برای ساده سازی Controllerها گرفتن مسئولیتهای اضافی از آن میباشد به Action زیر توجه کنید :
public RedirectToRouteResult Ship(int orderId) { User user = _userSession.GetCurrentUser(); Order order = _repository.GetById(orderId); if (order.IsAuthorized) { ShippingStatus status = _shippingService.Ship(order); if (!string.IsNullOrEmpty(user.EmailAddress)) { Message message = _messageBuilder .BuildShippedMessage(order, user); _emailSender.Send(message); } if (status.Successful) { return RedirectToAction("Shipped", "Order", new {orderId}); } } return RedirectToAction("NotShipped", "Order", new {orderId}); }
این Action کار زیادی انجام میدهد ، تقریبا میتوان تعداد مسئولیتهای این Action را با شمارش تعداد Ifها پیدا کرد . مسئولیت تصمیم گیری درباره این که آیا Order مورد نظر آماده برای تحویل است یا خیر به این Action سپرده شده ، همچنین تصمیم گیری درباره اینکه آیا کاربر دارای آدرس ایمیل جهت ارسال ایمیل میباشد یا خیر به این Action سپرده شده است.
منطقهای domain logic و business logic نباید در کلاسهای presentation همانند Controllerها قرار داده شود. تست و نگهداری کدی مثل کد بالا دشوار خواهد بود. Refactoring که باید در این Code اعمال شود Refactor Architecture by Tiers نام دارد. این Refactoring به توسعه دهنده میگوید که منطقی که در لایهی نمایش (Presentation) پیاده کرده را به لایهی Business انتقال دهد. پس از انتقال منطق کد بالا به OrderShippingService ، کد Action ما سادهتر میشود :
public RedirectToRouteResult Ship(int orderId) { var status = _orderShippingService.Ship(orderId); if (status.Successful) { return RedirectToAction("Shipped", "Order", new {orderId}); } return RedirectToAction("NotShipped", "Order", new {orderId}); }
پس از انتقال منطق تجاری به محل مناسب خودش تنها مسئولیتی که برای برای Controller باقی مانده این است که کاربر را به کجا Redirect کند. پس از این Refactoring علاوه برا اینکه مسئولیتها در جای مناسب خود قرار گرفتند ، اکنون میتوان به سادگی منطق کار را بدون تحت تاثیر قرار گرفتن کدهای لایهی نمایش تغییر داد.
در آینده به تکنیکهای ساده سازی Controllerها خواهیم پرداخت.