重構目的:消除重複、清晰結構、去除壞味道。有測試保護,重構無懼。
public int Add(string loginName) {
int count = 0;
using (var conn = new SqlConnection(connStr)) {
conn.Open();
var cmd = new SqlCommand("insert(loginName) value('" + loginName + "')", conn);
count = cmd.ExecuteNonQuery();
cmd.Dispose(); conn.Close();
}
return count;
}
public int Delete(string loginName) {
// 除 SQL 字串外完全相同...
}
private int ExecuteSql(string sql) {
int count = 0;
using (var conn = new SqlConnection(connStr)) {
conn.Open();
var cmd = new SqlCommand(sql, conn);
count = cmd.ExecuteNonQuery();
cmd.Dispose(); conn.Close();
}
return count;
}
public int Add(string loginName) => ExecuteSql("insert(loginName) value('" + loginName + "')");
public int Delete(string loginName) => ExecuteSql("delete from LoginUsers where loginName='" + loginName + "'");
代碼清晰度大幅提升。
代碼重複度高、層次混亂、出現「壞味道」(長方法、大類別、散彈式修改等)時皆可重構。測試是安全網。
違反單一職責原則(SRP)。拆分服務層與資料層。
public interface IEmployeeService {
bool Login(string loginName, string password);
bool ValidateLoginName(string loginName); // 驗證單獨抽離
}
public class EmployeeService : IEmployeeService {
private readonly IEmployeeDataAccess _empDAO;
public EmployeeService(IEmployeeDataAccess empDAO) => _empDAO = empDAO;
public bool ValidateLoginName(string loginName) => !string.IsNullOrEmpty(loginName);
public bool Login(string loginName, string password) {
if (ValidateLoginName(loginName))
return _empDAO.GetCount(loginName, password) > 0;
return false;
}
}
關鍵:依賴介面 IEmployeeDataAccess 而非具體實現,解耦服務層與資料層。
public interface IEmployeeDataAccess {
int GetCount(string loginName, string password);
}
public class EmployeeAccess : IEmployeeDataAccess {
public int GetCount(string loginName, string password) => throw new NotImplementedException();
}
[TestFixture]
public class EmployeeServiceTest {
private IEmployeeService _empService;
[TestFixtureSetUp]
public void Init() {
var mockDao = new Mock<IEmployeeDataAccess>();
mockDao.Setup(d => d.GetCount("admin", "pwd")).Returns(1);
_empService = new EmployeeService(mockDao.Object);
}
[Test] public void LoginTest() => Assert.IsTrue(_empService.Login("admin", "pwd"));
[Test] public void ValidateNullLoginName() => Assert.IsFalse(_empService.ValidateLoginName(null));
[Test] public void ValidateEmptyLoginName() => Assert.IsFalse(_empService.ValidateLoginName(""));
}
Moq 優勢:資料層未完成時,即可模擬資料對服務層獨立測試,實現真正的分層並行開發。
重構不僅是代碼整理,更是架構演進:模式為變化而生。若專案交付後無變化,模式確屬過度設計。但在真實世界,變化是常態——TDD + 重構 + 設計原則,讓我們從容應對。