۱۳۹۰/۰۷/۲۵

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


اين قسمت از آشنايي با Refactoring به كاهش cyclomatic complexity اختصاص دارد و خلاصه آن اين است: «استفاده از if هاي تو در تو بيش از سه سطح، مذموم است» به اين علت كه پيچيدگي كدهاي نوشته شده را بالا برده و نگهداري آن‌ها را مشكل مي‌كند. براي مثال به شبه كد زير دقت كنيد:

if
   if
     if
       if
         do something
       endif
     endif
   endif
 endif


كه حاصل آن شبيه به نوك يك پيكان (Arrow head) شده است. يك مثال بهتر:


namespace Refactoring.Day9.RemoveArrowhead.Before
{
    public class Role
    {
        public string RoleName { set; get; }
        public string UserName { set; get; }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;

namespace Refactoring.Day9.RemoveArrowhead.Before
{
    public class RoleRepository
    {
        private IList<Role> _rolesList = new List<Role>();

        public IEnumerable<Role> Roles { get { return _rolesList; } }

        public void AddRole(string username, string roleName)
        {
            if (!string.IsNullOrWhiteSpace(roleName))
            {
                if (!string.IsNullOrWhiteSpace(username))
                {
                    if (!IsInRole(username, roleName))
                    {
                        _rolesList.Add(new Role 
                        {
                            UserName=username, 
                            RoleName=roleName
                        }); 
                    }
                    else
                    {
                        throw new InvalidOperationException("User is already in this role.");
                    }
                }
                else
                {
                    throw new ArgumentNullException("username");
                }
            }
            else
            {
                throw new ArgumentNullException("roleName");
            }
        }

        public bool IsInRole(string username, string roleName)
        {
            return _rolesList.Any(x => x.RoleName == roleName && x.UserName == username);
        }
    }
}

متد AddRole فوق، نمونه‌ي بارز پيچيدگي بيش از حد حاصل از اعمال if هاي تو در تو است و ... بسيار متداول. براي حذف اين نوك پيكان حاصل از if هاي تو در تو، از بالاترين سطح شروع كرده و شرط‌ها را برعكس مي‌كنيم؛ با اين هدف كه هر چه سريعتر متد را ترك كرده و خاتمه دهيم:

using System;
using System.Collections.Generic;
using System.Linq;

namespace Refactoring.Day9.RemoveArrowhead.After
{
    public class RoleRepository
    {
        private IList<Role> _rolesList = new List<Role>();

        public IEnumerable<Role> Roles { get { return _rolesList; } }

        public void AddRole(string username, string roleName)
        {
            if (string.IsNullOrWhiteSpace(roleName))
                throw new ArgumentNullException("roleName");

            if (string.IsNullOrWhiteSpace(username))
                throw new ArgumentNullException("username");

            if (IsInRole(username, roleName))
                throw new InvalidOperationException("User is already in this role.");

            _rolesList.Add(new Role
            {
                UserName = username,
                RoleName = roleName
            });
        }

        public bool IsInRole(string username, string roleName)
        {
            return _rolesList.Any(x => x.RoleName == roleName && x.UserName == username);
        }
    }
}

اكنون پس از اعمال اين Refactoring ، متد AddRole بسيار خواناتر شده و هدف اصلي آن كه اضافه كردن يك شيء به ليست نقش‌ها است، واضح‌تر به نظر مي‌رسد. به علاوه اينبار قسمت‌هاي مختلف متد AddRole، فقط يك كار را انجام مي‌دهند و وابستگي‌هاي آن‌ها به يكديگر نيز كاهش يافته است.