اين قسمت از آشنايي با 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، فقط يك كار را انجام ميدهند و وابستگيهاي آنها به يكديگر نيز كاهش يافته است.