Parameter & Result Objects: More Than Grouping Values

Parameter and Result Objects

  • A parameter object replaces one or more parameters to a method with a single object instance.
  • A result object is a object created specifically for a return value from a method.

Parameter Objects

Changing a method to accept a parameter object is a common refactoring for grouping parameters that belong together.

Unfortunately that’s not the whole story. The real benefit of a parameter object is that it can protect its own invariants, like any other value object.

Take the common date range example. A start and end date belong together, but would it make sense for a start date to come after an end date? Not at all. A parameter object can make sure that never happens.

<?php

final class StartDateBeforeEndDate extends \InvalidArgumentException {}

class DateRange
{
    private $start;
    private $end;

    public function __construct(\DateTimeInterface $start, \DateTimeInterface $end)
    {
        $diff = $start->diff($end);
        if ($diff->invert) {
            throw new StartDateBeforeEndDate();
        }

        $this->start = $start;
        $this->end = $end;
    }

    // getters, etc
}

This sort of checking can shift the responsibility for argument validation out of the objects and methods themselves and into a common value object. For an interface with multiple implementations that lifting of validation to a common, logical place is a huge benefit.

The above has nothing to do with grouping multiple parameters. That’s still a valid reason to use a parameter object, but a parameter object that protects its invariants is useful with just a single property.

Result Objects

As mentioned above, a result object is just something that gets returned from a method. These come with the same benefits as other value objects: real types instead of dynamic mappings and protection of invariants.

A result object means thats its impossible for a class to return an invalid value — the object would prevent that from happening. This may not seem like a big deal, but with multiple implementations of an interface it helps. No question about adherence to the interface because the result object takes care of that.

For example, a method that calculates revenue of something. Revenue can probably not be negative (profit can be). Wrap that rule up in an object.

<?php

interface RevenueCalculator
{
    public function calculateRevenue() : Revenue;
}

final class RevenueLessThanZero extends \InvalidArgumentException {}

final class Revenue
{
    /**
     * int because monetary values as a floating point
     * is a bad idea. Could even use a `Money` value
     * object here.
     * @var int
     */
    private $value;

    public function __construct(int $revenue)
    {
        if ($revenue < 0) {
            throw new RevenueLessThanZero();
        }
        $this->value = $revenue;
    }

    // getters, etc.
}

Now anything that that implements RevenueCalculator cannot return an invalid value. Its clients need not worry or check things themselves.

Also note the use of non-generic exceptions.

Use More Value Objects

In general, value objects are a good thing. Parameter and result objects are just two flavors.

In PHP, any place that a free form array is being used a value object could be put in its place.