Fluent Interface and class complexity
Asked Answered
T

2

6

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.)

Tortuosity answered 14/3, 2014 at 16:29 Comment(4)
If a fluent interface adds that much complexity, how about not designing around a fluent interface?Midsummer
What exactly you have in mind?Tortuosity
Sorry, but I don't get how fluent interfaces add complexity to a class. The difference between a class with and without fluent interface is about returning a reference to the same object's instance ($this) in methods, that return no other values, which adds 1 LOC pro method at most (and no additional cyclomatic complexity). Or do you mean another definition of fluent interface and/or complexity? @TortuosityOnanism
You are right in theory. However in reality those methods will have more that 1 LOC/method (it has to do something before return $this). Complexity adds up very quickly.Tortuosity
S
2

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.

Stoddart answered 2/4, 2014 at 22:4 Comment(2)
Missed the point completely. Problem is not from the USAGE of fluent interface class, it's in the class that implements fluent interface itself. See first line of question "Problem: Implementing fluent interface..." not "Problem: Using ..."Tortuosity
Implementing simple fluent interface in the class itself is not the problem for complexity. If you will implement fluent interface for real phrases usage it can be a problem, so you can reach it by a proxy to taggle complexity. That i tried to say in the answer.Stoddart
D
0

One approach is to have a simple, reliable base class that provides the tooling methods for implementing the fluent interface class. Then the fluent interface class just calls into the base class for everything it does, reducing the complexity down to the single, fluent interface class.

Do you have a design for your fluent interface already? (I'm wondering just how many of those methods you actually will use.)

Dependence answered 2/4, 2014 at 21:27 Comment(1)
1) So idea is to have multiple helpers to which would be tightly coupled to main class? 2) Class purpose is generic functional programming library and it's up to user to choose what he wanna use via chaining.Tortuosity

© 2022 - 2024 — McMap. All rights reserved.