Everything is a parameter

Problem: Methods are difficult or impossible to test because they require too much state and change too many properties

Pattern: During development a rule is adopted that anything which alters the state of an object, or performs manipulation of data, is written as a static method that operates only on data passed as a parameter. Instance methods are then written that pass all required values as parameters to these static methods, then set instance members based only on what is returned.

Example: In the old code, a method that's difficult to test might look like this:

private bool ProcessData()
{
    SqlConnection LookupDB = new SqlConnection(this.LookupDBConnectionString);

    for (int i = 0; i < this.InternalData.Length; i++)
    {
        var thing = this.InternalData[i];
        decimal CompensationFactor = SqlConnection.Command("SELECT compensationFactor FROM compensations WHERE blortValue = " + thing.blortValue).ExecuteScalar();
        thing.StrawberryValue.Value = (this.MagicValueQ / this.MajorConstant) * (thing.Whatsit * CompensationFactor);
        this.ProcessedData[i] = thing;
    }

    return this.ProcessedData.Length == this.InternalData.Length;
}

By following "Everything is a Parameter", refactored code might look like this:

public static IEnumerable<StrawberryValue> CompensateByFactor(IndustryDatum datum, 
                                                              IDictionary<int, decimal> CompensationFactors, 
                                                              int QValue, 
                                                              int MajorConstant)
{
    return new StrawberryValue(
        (QValue / MajorConstant)  * (datum.Whatsit * CompensationFactors[datum.blortValue])
    );
}

private void ProcessData()
{
    var CompensationFactors = (from factor in this.LookupDB.compensations
                               select new {
                                 blortValue = factor.blortValue,
                                 compensation = factor.compensationFactor
                               }).ToDictionary(x => x.blortValue, y => y.compensation);

    List<StrawberryValue> results = new List<StrawberryValue>();
    foreach (IndustryDatum datum in this.InternalData)
    {
        StrawberryData compensated = CompensateByFactor(datum, CompensationFactors, this.MagicValueQ, this.MajorConstant);
        if (compensated.Value > 0)
            results.Add(compensated);
    }

    if (results.Count() != this.InternalData.Length)
        throw new Exception("Could not compensate for all values");
    else
        this.ProcessedData = results;
}

Discussion: In the example, the new ProcessData() is practically a blueprint for the unit test of CompensateByFactor, which has not only inherited all of the business logic, but also takes everything it operates on as a parameter.
Comments