當異動一項功能, 導致系統其他的程式碼也得異動.
稱為Shotgun surgery是一種比喻, 霰彈槍發射(功能異動), 打到的目標(其他模組)都會變化
假設有個聊天室系統, ChatService
處理當前使用者能傳訊息、傳送檔案與變更狀態(上線、忙碌等)
public class ChatService
{
private User _user;
public void SendMessage(User recipient, string message)
{
if(!_user.IsLogged())
{
throw new AuthenticationException("User is not logged");
}
// ...
}
public void UploadFile(User recipient, File file)
{
if(!_user.IsLogged())
{
throw new AuthenticationException("User is not logged");
}
// ...
}
public void ChangeStatus(Status status)
{
if(!_user.IsLogged())
{
throw new AuthenticationException("User is not logged");
}
// ...
}
}
試想如果if(!_user.IsLogged())
這個if block要異動時, 這3個甚至多個函數, 都得全部一筆一筆修改.
於是Extract function
,將這段邏輯都只移到一函數處理:
public class ChatService
{
private User _user;
public void SendMessage(User recipient, string message)
{
AssertUserIsLogged();
// ...
}
public void UploadFile(User recipient, File file)
{
AssertUserIsLogged();
// ...
}
public void ChangeStatus(Status status)
{
AssertUserIsLogged();
// ...
}
private void AssertUserIsLogged()
{
if(!_user.IsLogged())
{
throw new AuthenticationException("User is not logged");
}
}
}
經過這種重構, 未來要變更是否登入的流程, 範圍已限縮於AssertUserIsLogged
函數.
Shotgun surgery很常發生, 只要你完成一段邏輯, 而別的模組又得用到而你要懶得設計, 直接複製貼上,
此時這code smell慢慢出現, 久而久之維護程式碼就變得很耗時.
最好的做法是時時要有"Don't repeat yourself"的原則, 別讓程式碼大量重複.