Best practice to create nested objects

321 views Asked by At

I always have the same question when I must to work with nested objects structure. An example:

class Criteria {
  filters: Filter[];
}
class Filter {
  field: string;
  operator: Operator;
  value: string;
}
class Operator {
  operator: '=' | '!=';
}

I try to apply Demeter's Law at return methods on that cases, but what's the best practice on creation step with that structures?

  • Create all objects from the client
class Client {
  constructor() {
    const criteria = new Criteria([new Filter('field1', new Operator('='), 'value1')]);
  }
}
  • Create all objects from the root object (Criteria) passing only primitives from the client to root class.
class Client {
  constructor() {
    const criteria = new Criteria([{field: 'field1', operator: '=', 'value': 'value1'}]);
  }
}
2

There are 2 answers

0
Matt Timmermans On

It depends on what the objects are for, so you may see both kinds of patterns in good code -- your example strips away everything that would indicate one vs the other.

I think in good code the first pattern -- the client creates the objects -- will be more popular. That is because those objects would be injected dependencies, and following the SOLID principles, the Criteria implementation should not have dependencies on all the different types of filters. If that were not the case, then why do you have a Filter class at all?

0
root On

The first question is: are the nested classes part of the API, or meant for internal use?

If they are part of the API, that means you want users to use them. Does that necessarily mean that you want uses to be able to create them, or just be able to use instances that you privately produced?

Think of it this way: you are dividing your API into concepts (Criteria, Filter, Operator), and your users already need to be familiar with the concepts and how they map to classes. Under these conditions, letting them create the nested classes themselves provides the maximum flexibility.
Requiring that they provide the primitive values and have you construct these nested objects for them limits that flexibility, and placing such a limit may be warranted - if you have a reason to do that.