Tuesday, October 9, 2012

Refactoring out parameters code smell to Tuple


Using out parameters in methods are usually a code smell with indicates that you want to effectively return two results from a method. Out parameters are mostly introduced when developers realize the need of an additional return value from an already existing method. The problem with out parameters is that they introduce the risk of side effects and are hard to debug.
You can use the Transform out parameters refactoring to transform out parameters to a tuple in C#.
Later you can convert the tuple with a class or struct with a static create method to enhance readability and maintainability.

For e.g., we can refactor the below given code
[TestMethod]
public void AddEmployeeReturnsTrueIfEmployeeWasSaved()
{
    var employee = new Employee {Name = "Prajeesh Prathap"};
    var repository = new EmployeeRepository();
    int employeeId = 0;
    var success = repository.Add(employee, out employeeId);
    Assert.IsTrue(success);
    repository.RemoveAll();
}
public bool Add(Employee employee, out int employeeId)
{
    employeeId = 0;
    if (_employees.Any(x => x.Name == employee.Name)) return false;
    employee.Id = _employees.Any() ? _employees.Max(x => x.Id) + 1 : 1;
    employeeId = employee.Id;
    _employees.Add(employee);
    return true;
}

Using the transform out parameters refactoring pattern to...




[TestMethod]
public void AddEmployeeReturnsTrueIfEmployeeWasSaved()
{
    var employee = new Employee {Name = "Prajeesh Prathap"};
    var repository = new EmployeeRepository();
    var add = repository.Add(employee);
    var success = add.Item1;
    Assert.IsTrue(success);
    repository.RemoveAll();
}

public Tuple<bool, int> Add(Employee employee)
{
    int employeeId = 0;
    if (_employees.Any(x => x.Name == employee.Name)) return Tuple.Create(false, employeeId);
    employee.Id = _employees.Any() ? _employees.Max(x => x.Id) + 1 : 1;
    employeeId = employee.Id;
    _employees.Add(employee);
    return Tuple.Create(true, employeeId);
}

Further improvement can be achieved by encapsulating the parameters in a class as given below

public class AddOutput
{
    public bool Result { get; set; }
    public int Id { get; set; }

    public static AddOutput Create(bool success, int id)
    {
        return new AddOutput {Result = success, Id = id};
    }
}
public AddOutput Add(Employee employee)
{
    int employeeId = 0;
    if (_employees.Any(x => x.Name == employee.Name)) return AddOutput.Create(false, employeeId);
    employee.Id = _employees.Any() ? _employees.Max(x => x.Id) + 1 : 1;
    employeeId = employee.Id;
    _employees.Add(employee);
    return AddOutput.Create(true, employeeId);
}
[TestMethod]
public void AddEmployeeReturnsTrueIfEmployeeWasSaved()
{
    var employee = new Employee {Name = "Prajeesh Prathap"};
    var repository = new EmployeeRepository();
    var add = repository.Add(employee);
    var success = add.Result;
    Assert.IsTrue(success);
    repository.RemoveAll();
}

No comments: