5

Dependency Inversion is Good

Inversion of dependency is good, it:

  • Simplifies unit-testing
  • Reduces coupling, allowing software components to be used interchangeably
  • Keeps instantiation logic for a given object in fewer places, preventing repetitive code.

For these reasons, I avoid instantiating objects within classes; preferring instead to pass in factory classes as a dependency. However, I am wondering if this is excessive for some types of objects.

Are Value Objects Exceptions?

Value objects are, by definition, simple objects with simple construction.

In my experience, there is rarely any complication in the instantiation logic, unit-testing is not compromised as value objects can be regarded as any other data type, and coupling is not a concern because all classes must rely on data-types of some description (caveat, see My Concern).

Collection Objects too?

A special associated case is that of collection objects.

My opinion is that collection objects, like value objects, require no real instantiation. They are, in effect, a specialised form of array; a value object.

Example:

class PencilCase
{

  private $pencils=[];

  public function construct() 
  {
   // blah blah
  }

  public function emptyPencils()
  {
    $pencils = $this->pencils;
    $this->pencils = [];

    return new PencilsCollection($pencils);
  }
}


class PencilsCollection implements \Traversable
{
  public function __construct($pencils=[])
  {
    // blah blah
  }
}

My Concern

In general I have avoided this type of coupling because I may well want to change the package that is providing some of the data types being used; in the example above I may be using a value object provided by Dixon and then change to those provided by Staedtler.

This type of refactoring could be expensive.

Question

Should I instantiate value & collection objects with factories and injected dependencies or should I instantiate in-class?

3
  • 1
    The answer to your question in the last paragraph depends entirely on your application's requirements. As a general rule, things like factories and dependency injection are reserved for large programs. By the time your program gets large enough to justify factories and DI, the refactoring will be worth the expense (don't wait too long). Mar 11, 2016 at 16:27
  • 1
    Why would you ever inject a value object? There aren't different representations or implementations of single value objects; there's only one (i.e. an int is an int). Not sure about collections; my guess is they should be treated like any other object. Mar 11, 2016 at 16:31
  • The problem comes in when you want your value objects to be very OO, and since they're generic, there are many things they can be asked to do (->clone, ->equals, etc.). They end up with too many methods. I don't know the solution to this.
    – Greg Bell
    Apr 18, 2018 at 23:50

2 Answers 2

1

Should I instantiate value & collection objects with factories [..].

No, usually creation of value objects is very simple and there is no need for factories.

At most you will use some kind of static constructor, like Money::fromString() which will instantiate value object and needed dependencies.

[..] and injected dependencies or should I instantiate in-class

Depends on a use case. Lets stay with aforementioned Money - you may have a use case where you have just amount of money, and Currency is taken from different source. In such case - you inject Currency into Money constructor with integer.

There may be also a case that you have string containing amount with currency code, and then you would use Money::fromString which will internally create Currency instance for you.

Same goes with collections - you will have situations, where some collection will be instantiate in-class and then injected, just because it's easier to expect specific interface than array of value objects that need to be validated.

0

@potfur already answered you, but I wanted to add…

I guess also the first case he describes could use a static constructor and inject Currency into it.

Like Money::withCurrency($euroCurrency, $amount).

About VOs and collections…

Using factories keeps you resilient to change, so if you expect types to vary then it might make sense to also use factories over them.

Take the example from @RobertHarvey's comment: it's true that int is int, but it might be necessary to update the requirements in the future and some parts of the code might need to handle a different implementation (e.g. int32 -> int64).

But as they both mentioned, it depends from your use case or requirements. In general, if you don't want to go for a factory, I think it is not a bad idea to already encapsulate that instancing code into a static constructor, and avoid having "bad news" spread in uncomfortable places.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy