Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fluent Interface and class complexity

Problem: Implementing fluent interface with many methods yields class complexity metric growing very fast.

How to keep low complexity for class which implements fluent interface?

Some information about specific class:

  • Class already has 25 methods and will get another 15-ish more.
  • All methods in class transform $this->wrapped object in one way or another.
  • Several (5-7) methods reuses already existing methods (those can be extracted to class and added via inheritance, not issue here).

Already considered options:

  1. Traits - I want to support PHP 5.3 and up.
  2. One class per method - Massive extend chain, not nice.
  3. 'Plugins' - helper classes somehow injected into "master class", called via magic methods and autocomplete support added via @method annotation.

Feedback (on design, performance, maintainability, etc.) for any options is highly anticipated.

Checked examples:

  1. Lodash: 170-ish methods, 1 class, 8400 lines

  2. Doctrine2 QueryBuilder: 40+40-ish methods, 2 classes, 1400+600 lines of code; separated via ExpressionBuilder class

  3. Symfony2 FormBuilder: 10-ish exposed methods, 1 class, 300 lines


Question can be considered language-agnostic - answers both from PHP implementation and design point of view are both equally welcome.

EDIT:

Aim is to have nice (easy to use and easy to maintain) tool for functional programming (map, reduce, etc.)

like image 756
Im0rtality Avatar asked Mar 14 '14 16:03

Im0rtality


1 Answers

Complexity is a software metric to indicate lacks of understandings for developer which have to work with. Thus, the higher complexity of a software entity (package/module, class, method) is, the worst maintainable it is. Or in other words, how much time and effort is required to modify and maintain a software entity.

The main types of complexities for classes are:

  • Cyclomatic-Complexity: Number of decision points in a method, like 'if', 'while', 'for', 'switch'
  • NPath-Complexity: Number of acyclic execution paths through that method
  • Too large class length: Indication for class is doing too much
  • Too large method body: Indication for method is doing too much
  • Too long method parameter list: Should be grouped by a ValueObject
  • Too much public methods provided: Indication for broken interchangeability
  • Too many class fields/properties: Indication for missing nested objects
  • Number of children by inheritance: Indication for an unbalanced class hierarchy
  • Depth of inheritance: Indication for an unbalanced/wrong class hierarchy
  • Coupling between objects: Indication for too many dependencies

All types can be measured by tools and have an angular point, where the complexity is too high. In this case it's a good suspect for refactoring, in order to reduce complexity by using Design Patterns. Another measurement for complexity is, how testable is the class. How much stubs/mocks i need, how much assertions i need, etc.

How to keep low complexity for class which implements fluent interface?

Fluent Interface is a design pattern for chaining method calls. Chaining at the best looks like real phrases. It aims to provide for more readable code. Thus it's reduce complexity. Implementing Fluent Interfaces is reached by only adding one new line of code, which only impacts large of method body and large of class length. Thus, fluent interface impacts complexity very low.

Chaining at the best with real phrases can be difficult with growing complexity, but to taggle this it's reacheable by proxy classes, which delegates operations to object without fluent interface.

In general best approach for taggling complexity is to keep single responsibility principle, interface segregation principle, information hiding principle and loose coupling as key aspects.

like image 189
Mamuz Avatar answered Oct 22 '22 04:10

Mamuz