在程式碼區塊使用不同的冗長的if/else/switch判斷條件, 該函數的複雜度越來越大.
假設有一個作業系統, 帳號有管理員與普通使用者兩種角色. 當普通使用者要求權限, 需要經過層層的權限狀態判斷, 才能轉換到下一個權限狀態, 或者權限狀態被拒絕轉換.
以下是SystemPermission
與相關類別的範例
public class SystemPermission
{
public static readonly string REQUESTED = "REQUESTED";
public static readonly string CLAIMED = "CLAIMED";
public static readonly string GRANTED = "GRANTED";
public static readonly string DENIED = "DENIED";
public static readonly string UNIX_REQUESTED = "UNIX_REQUESTED";
public static readonly string UNIX_CLAIMED = "UNIX_CLAIMED";
private SystemAdmin _admin;
private SystemProfile _profile;
private SystemUser _requestor;
public SystemPermission(SystemAdmin admin, SystemUser requestor, SystemProfile profile)
{
_admin = admin;
_requestor = requestor;
_profile = profile;
State = REQUESTED;
IsGranted = false;
NotifyAdminOfPermissionRequest();
}
public string State { get; private set; }
public bool IsGranted { get; private set; }
public bool IsUnixPermissionGranted { get; private set; }
private void NotifyAdminOfPermissionRequest()
{
}
public void ClaimedBy(SystemAdmin admin)
{
if (State != REQUESTED && State != UNIX_REQUESTED)
{
return;
}
WillBeHandledBy(admin);
if (State == REQUESTED)
{
State = CLAIMED;
}
else if (State == UNIX_REQUESTED)
{
State = UNIX_CLAIMED;
}
}
private void WillBeHandledBy(SystemAdmin admin)
{
_admin = admin;
}
public void DeniedBy(SystemAdmin admin)
{
if (State != CLAIMED && State != UNIX_CLAIMED) return;
if (_admin != admin) return;
IsGranted = false;
IsUnixPermissionGranted = false;
State = DENIED;
NotifyUserOfPermissionRequestResult();
}
private void NotifyUserOfPermissionRequestResult()
{
}
public void GrantedBy(SystemAdmin admin)
{
if (State != CLAIMED && State != UNIX_CLAIMED) return;
if (_admin != admin) return;
if (_profile.IsUnixPermissionRequired && State == UNIX_CLAIMED)
{
IsUnixPermissionGranted = true;
}
else if (_profile.IsUnixPermissionRequired && IsUnixPermissionGranted)
{
State = UNIX_REQUESTED;
NotifyUserOfPermissionRequestResult();
return;
}
State = GRANTED;
IsGranted = true;
NotifyUserOfPermissionRequestResult();
}
}
public class SystemAdmin
{
}
public class SystemProfile
{
public bool IsUnixPermissionRequired { get; set; } = false;
}
public class SystemUser
{
}
可以看到, GrantedBy()
函數需要檢查State、是否為_admin、_profile的IsUnixPermissionRequired與變數IsUnixPermissionGranted等參數判斷.ClaimedBy()
也是依據State條件做這種狀態轉換.
所以, 以這種範例來看, 如果今天狀態更多種, 上述的狀態轉移函數是不是就更複雜?
我們將狀態轉換邏輯成各自的處理類別, 以管理員授權權限的範例, 可以分出1種基礎類別與6種子類別:
public abstract class PermissionState
{
private readonly string _name;
protected PermissionState(string name)
{
_name = name;
}
public static readonly PermissionState REQUESTED = new PermissionRequested();
public static readonly PermissionState CLAIMED = new PermissionClaimed();
public static readonly PermissionState GRANTED = new PermissionGranted();
public static readonly PermissionState DENIED = new PermissionDenied();
public static readonly PermissionState UNIX_REQUESTED = new UnixPermissionRequested();
public static readonly PermissionState UNIX_CLAIMED = new UnixPermissionClaimed();
public override string ToString()
{
return _name;
}
public virtual void ClaimedBy(SystemAdmin admin, SystemPermission permission)
{
}
public virtual void DeniedBy(SystemAdmin admin, SystemPermission permission)
{
}
public virtual void GrantedBy(SystemAdmin admin, SystemPermission permission)
{
}
}
public class PermissionClaimed : PermissionState
{
public PermissionClaimed() : base("CLAIMED")
{
}
public override void DeniedBy(SystemAdmin admin, SystemPermission permission)
{
if (permission.Admin != admin) return;
permission.IsGranted = false;
permission.IsUnixPermissionGranted = false;
permission.PermissionState = DENIED;
permission.NotifyUserOfPermissionRequestResult();
}
public override void GrantedBy(SystemAdmin admin, SystemPermission permission)
{
if (permission.Admin != admin) return;
if (permission.IsUnixPermissionDesiredButNotRequested())
{
permission.PermissionState = UNIX_REQUESTED;
permission.NotifyUserOfPermissionRequestResult();
return;
}
permission.PermissionState = GRANTED;
permission.IsGranted = true;
permission.NotifyUserOfPermissionRequestResult();
}
}
public class PermissionDenied : PermissionState
{
public PermissionDenied() : base("DENIED")
{
}
}
public class PermissionGranted : PermissionState
{
public PermissionGranted() : base("GRANTED")
{
}
}
public class PermissionRequested : PermissionState
{
public PermissionRequested() : base("REQUESTED")
{
}
public override void ClaimedBy(SystemAdmin admin, SystemPermission permission)
{
permission.WillBeHandledBy(admin);
permission.PermissionState = CLAIMED;
}
}
public class UnixPermissionClaimed: PermissionState
{
public UnixPermissionClaimed() : base("UNIX_CLAIMED")
{
}
public override void DeniedBy(SystemAdmin admin, SystemPermission permission)
{
if (permission.Admin != admin) return;
permission.IsGranted = false;
permission.IsUnixPermissionGranted = false;
permission.PermissionState = DENIED;
permission.NotifyUserOfPermissionRequestResult();
}
public override void GrantedBy(SystemAdmin admin, SystemPermission permission)
{
if (permission.Admin != admin) return;
if (permission.IsUnixPermissionRequestedAndClaimed())
{
permission.IsUnixPermissionGranted = true;
}
permission.PermissionState = GRANTED;
permission.IsGranted = true;
permission.NotifyUserOfPermissionRequestResult();
}
}
public class UnixPermissionRequested : PermissionState
{
public UnixPermissionRequested() : base("UNIX_REQUESTED")
{
}
public override void ClaimedBy(SystemAdmin admin, SystemPermission permission)
{
permission.WillBeHandledBy(admin);
permission.PermissionState = UNIX_CLAIMED;
}
}
PermissionState
實作, 實作的三大函數:ClaimedBy
, DeniedBy
與GrantedBy
SystemAdmin
與SystemPermission
兩種物件做判斷, 並在需要時更改這些物件的數值.而SystemPermission
重構成:
public class SystemPermission
{
private SystemAdmin _admin;
private SystemProfile _profile;
private SystemUser _requestor;
public SystemPermission(SystemAdmin admin, SystemUser requestor, SystemProfile profile)
{
_admin = admin;
_requestor = requestor;
_profile = profile;
IsGranted = false;
PermissionState = PermissionState.REQUESTED;
NotifyAdminOfPermissionRequest();
}
public PermissionState PermissionState { get; internal set; }
public bool IsGranted { get; internal set; }
public bool IsUnixPermissionGranted { get; internal set; }
public SystemAdmin Admin => _admin;
private void NotifyAdminOfPermissionRequest()
{
}
public void ClaimedBy(SystemAdmin admin)
{
PermissionState.ClaimedBy(admin, this);
}
internal void WillBeHandledBy(SystemAdmin admin)
{
_admin = admin;
}
public void DeniedBy(SystemAdmin admin)
{
PermissionState.DeniedBy(admin, this);
}
internal void NotifyUserOfPermissionRequestResult()
{
}
public void GrantedBy(SystemAdmin admin)
{
PermissionState.GrantedBy(admin, this);
}
internal bool IsUnixPermissionDesiredButNotRequested()
{
return _profile.IsUnixPermissionRequired && IsUnixPermissionGranted;
}
internal bool IsUnixPermissionRequestedAndClaimed()
{
return _profile.IsUnixPermissionRequired && PermissionState == PermissionState.UNIX_CLAIMED;
}
internal bool IsClaimedState()
{
return PermissionState == PermissionState.CLAIMED || PermissionState == PermissionState.UNIX_CLAIMED;
}
}
從上述看到, SystemPermission
擁有PermissionState
物件, 而任何的狀態處理都是委託PermissionState
完成.
這項重構手法是 Replace State-Altering Conditionals with State
是一種善用多型的方式重構.
其他還有
Replace Conditional Logic with Strategy
Move Embellishment to Decorator
Introduce Null Object
也都是處理這種smell的重構手法