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