Yes, there are drawbacks
Code that is easy to read is good, but also beware of what the code communicates as well. When an object's methods always return the object, it communicates a couple of things:
- I require advanced configuration that isn't necessarily obvious in which order things should be set or configured
- Each subsequent method call builds on the last
Valid Use Case: Ad Hoc Database Queries
Class libraries exist in most every language that allow you to query a database without resorting to hard coded SQL. Take the Entity Framework for .NET as an example:
DBContext db = new DBContext();
List<Post> posts = db.Posts
.Where(post => post.Title.Contains("Test"))
.OrderBy(post => post.DateCreated)
.ToList();
This is a fluent interface where each subsequent method call builds on the previous one. Reading these calls logically makes sense in the context of querying a database.
Invalid Use Case: Syntactical Sugar For Setting Properties
Now let's use the same pattern with the Post
class:
public class Post
{
public string Title { get; set; }
public DateTime DateCreated { get; set; }
public string Body { get; set; }
public Post SetTitle(string title)
{
Title = title;
return this;
}
public Post SetDateCreated(DateTime created)
{
DateCreated = created;
return this;
}
public Post SetBody(string body)
{
Body = body;
return this;
}
}
Now let's look at how you would use this class:
Post post = new Post()
.SetTitle("Test")
.SetDateCreated(DateTime.Now)
.SetBody("Just a test");
When I see this code, I immediately ask this question: "After calling SetBody
, does it query the database? Do I need to call another method to say 'I'm done?'"
What do the chained method calls communicate to the code using the Post
class?
- I have a complicated setup
- Each method call builds on the previous one
Is this actually true? No. The Post
class does not have a complicated setup. Setting the title, date created and body do not build on each other towards a more complicated end goal. You've mashed a square peg into a round hole.
The drawback to self-referential method chaining is that you communicate that multiple method calls are required to do something, and that each call builds off the last. If this is not true, then method chaining could be communicating the wrong thing to other programmers.
When your coworkers said:
Fluent interfaces should not be implemented just for convenience, but for semantics
They were absolutely correct. A fluent interface, or method chaining, communicates something in and of itself which might not be true.