We were unable to load Disqus. If you are a moderator please see our troubleshooting guide.

Ben • 3 years ago

some ideas to the topic:

1. fluent apis do not need to return
the same class, nor do they even need to return the same interface. it's
perfectly fine to return a different interface, each providing a new
subset of features within the chain. that also resolves the problem of
adding new features aka. methods, since they will only be added to an
intermediate step and usually only a few classes have to be modified.
steps even can be implemented as being optional, by using inheritance
(e.g. A returns B which returns C, making B extends C lead to B being
optional).

2. instead of creating generic functions as(Class)
and type cast to the correct result via reflections, you may use a
method named for each desired type, e.g asJson()

3. use only
interfaces for the fluentApi and refactor your internal design as you
like. as said in other comments, you may want to use a decorator design
internally which is not exposed

4. it's true with every code, if
the interface needs to be changed, then users of that interface have to
modify their dependent classes as well. consider using some optional
steps with default behaviour. here is also a benefit of fluentApis,
since a method chain is not bound to the return type, but rather to the
following method signature. if you decide to change the return type, the
new type only has to support the same methods and client code may still
compile.

5. fluentApis may grow big and since they are more
complex to design, adding new features might seems complicated to
maintain. i recommend here creating a diagram to visualize the growing
design graph and refactor and restructure the design when necessary in
order to keep them maintainable.

i agree using fluentApis
everywhere is a bad practice, since they are difficult to design. but
there is nothing wrong with a good set of fluent interfaces to guide a
developer using a set of operations.

Kamil Bęben • 3 years ago

What about using using multiple "smart" classes and decorators to handle the logic, but expose them to the user through a service following the fluent api rules? There is nothing preventing you from exposing the classes handling the logic as well, which would allow anyone to override them and create custom implementations, or use the fluent api which is easier and more readable but more difficult to override.

Sure, you've got to mantain an extra service, but i belive that the benefits outweight the costs in that case.

Asjinga Akangka • 4 years ago

Uh, no. Unless I understood it wrong, Fluent Interface is simply done by replacing void returning function with "return this;" That's it. In contrast, your proposed solution disperses code into many class. So, the code for request class is now in BodyOfResponse, ResponseAssertStatus. RequestWithMethod, and JdkRequest, thus unnecessarily splitting a responsibility that is supposed to be just one: handling http request. Not to mention, what happen if your have a XBlueprint (Actually simply a builder pattern) with 8 parameter, all of them is optional. There is only two way out: complex logic, or combinatorial explosion.

Bernie Noel • 4 years ago

More classes mean better maintainability, in most cases. If they are designed right, of course, without inheritance :)

Asjinga Akangka • 4 years ago

You clearly didn't know the meaning of combinatorial explosion. It means that there will be exponentially many classes.

Also, normal design without fluent interfaces is similar, but with "return;" instead of "return this;" This means that the fluent interfaces is always better than the original design. The cost is negligible, those who dislike fluent interfaces can use the traditional syntax, etc. There's also no forcing the class to be large.

Yegor Bugayenko • 4 years ago

How about we replace the responsibility of "handling http request" with "helping customers talk to each other" and put the entire application into a single class? You may read more about the SRP, it's mostly a hoax: https://www.yegor256.com/20...

Asjinga Akangka • 4 years ago

1 responsibility != 1 class. The correct one is 1 responsibility = 1 item (this may be a class, but also a function, module, package, or even an entire project in this case). "helping customers talk to each other" is of course single responsibility, or else your app is ill-defined. Just like a big problem, a big responsibility like this is best solved by divide and conquer. You divide a problem until the problem becomes small enough. It's the failure of following this that makes the program non-SRP compliant.

Asjinga Akangka • 4 years ago

Addendum: also the attempt to combine unrelated responsibility.

First • 4 years ago

The real problem with fluent interfaces is that they are linear. They're virtually no different to joining a string. That's not a structure a lot of things conform to, most things have complex multi-dimensional structures. That's why we have things such as JSON and not nothing but CSV.

pt • 4 years ago

As an amateur functional programmer coming to OOP in a more professional setting (enjoyed F# & Haskell as a data analyst, but am now required to use C# for larger systems applications), I am having some issues translating some of the commonly used types from FP to OOP.

Edited for brevity and clarity:

First off, many common (and very useful) abstractions in functional programming, such as Monoids, Functors, and Monads, are by their nature "fluent interfaces", in that their methods return the same type as the original object (with the exception of generic type changing, a Functor's map function will always return a Functor"). I agree that making single classes with every conceivable interface/method added is bad design. Even something simple like the Maybe type (Optional in Java, Nullable in C# - basically), CAN implement well over a dozen interfaces, each of which in turn can have multiple extension methods. There's also the problem of casting like the article mentions. If I use a Functor-returning Fmap method on an Optional, it would have to be recast as an Optional to regain the non-Functor methods. So as someone new to OOP, what is the right approach? What I'm leaning to is a combination of decorators, interfaces, and extension methods. I make a base class for each type, and then for each interface it can implement I make a decorator for it. To get around the casting issue, the decorator also has a "duplicate" definition of the interfaces methods. For example, Optional would have both "map" and "mapOptional". It does increase code size and complexity, but I'm not sure how else to avoid having to perform a bunch of type-casting.Extensions methods, in languages that support them, can be made in separate libraries/namespaces to be imported as needed. Here is an example of the simple "Identity" type. In addition to wondering if this approach can be improved or if there are better alternatives, as someone new to OOP I wonder are these abstractions used? They aren't real "objects" as described in this blog - they are data structures meant primarily to perform simple operations. All comments appreciated!


using System;

namespace IdExample
{
public abstract class Id<T>
{
protected readonly T value;
public Id(T value) => this.value = value;
public Id(Id<T> id) => this.value = id.value;
}

public interface Functor<T>
{
Functor<R> Map<R>(Func<T, R> mapping);
}

public interface Foldable<T>
{
A FoldLeft<A>(A acc, Func<A, T, A> folder);
A FoldRight<A>(A acc, Func<T, A, A> folder);
}

public sealed class IdFunctor<T> : Id<T>, Functor<T>
{
public IdFunctor(T value) : base(value) { }
public IdFunctor(Id<T> id) : base(id) { }
public IdFunctor<R> MapId<R>(Func<T, R> mapping) => new IdFunctor<R>(mapping(this.value));
public Functor<R> Map<R>(Func<T, R> mapping) => this.MapId(mapping);
}

public sealed class IdFoldable<T> : Id<T>, Foldable<T>
{
public IdFoldable(T value) : base(value) { }
public IdFoldable(Id<T> id) : base(id) { }
public A IdFoldLeft<A>(A acc, Func<A, T, A> folder) => folder(acc, this.value);
public A IdFoldRight<A>(A acc, Func<T, A, A> folder) => folder(this.value, acc);
public A FoldLeft<A>(A acc, Func<A, T, A> folder) => this.IdFoldLeft(acc, folder);
public A FoldRight<A>(A acc, Func<T, A, A> folder) => this.IdFoldRight(acc, folder);
}

namespace FunctorExtensions
{
public static class FunctorExtensionExampleClass
{
public static Functor<R> Replace<T, R>(this Functor<T> functor, R value) => functor.Map(_ => value);
}
}

namespace FoldableExtensions
{
public static class FoldableExtensionExampleClass
{
public static int Count<T>(this Foldable<T> foldable) => foldable.FoldLeft(0, (s, _) => s + 1);
public static int Sum(this Foldable<int> foldable) => foldable.FoldLeft(0, (s, x) => s + x);
}
}
}


Paymon Khamooshi • 6 years ago

I use the following pattern in C# which solves these problems:
- Instead of public methods, I define the methods as “virtual protected internal” and prefix then with “Do”, for example DoXyz().
- Define the public method as an extension method named just Xyz() which invokes the DoXyz method and return the object with whatever type it is given.

public class SomeFluent
{
protected internal virtual void DoXYZ()
{
// default implementation
}
}

public static class SomeFluentExtensions
{
public static T Xyz<t>(this T item)
where T : SomeFluent
{
item.DoXyz();
return item;
}
}

Now I can have a subclass for SomeFluentExtensions that can add additional methods the same way or override existing ones.

Yegor Bugayenko • 6 years ago

Can you please format it? It's unreadable now.

uki-uki • 6 years ago

Your blog convinced me that decorator is anti-OOP pattern.
Basically a decorator is a manager who managed a single entity. Decorator treats this entity as "dumb object". It's against your own principles.
Instead of decorators we should use something different. For streams, for example, that can be a pipelining.
You need to look at node.js streams. It is much more flexible and object oriented way than using decorators.

Elwin Slokker • 6 years ago

This is an amazing explanation on why Fluent Interfaces usually have to many drawbacks.

Michael • 6 years ago

Thank you for this!

I'm wondering about your choices in your object model. I pretty much agree with you on many small classes and decorators, but I wonder why you break down the problem into the classes you do. When I consider the problem of modelling HTTP interactions in an object-oriented fashion, I start with 3 classes: Server, Request, and Response. The Server is instantiated with a protocol, a hostname, and a port and has an accept method that takes a Request and produces a Response. In this way, it is responsible exclusively for communicating with the server (establishing a connection, adhering to the protocol for instance with the handshake for HTTPS, and sending the request over the wire). The Request and Response objects, in turn, model the well-formatted text that gets sent over the network by the Server object, so they would be based on the HTTP specification. For example, they would both be instantiated with a collection of Headers and an optional body, among other things. Each of these elements would also be properly encapsulated within objects. To me, things like the media type of the body would belong to the Body object which would be used in Request and Response objects rather than directly in the Request and Response objects. In some sense, it feels to me like the example choices from this article are exchanging decorators for properly encapsulated objects thereby violating the single-responsibility principle. Does that sound fair to you?

Yegor Bugayenko • 6 years ago

Sounds right. You may like this project, which uses exactly the same concept of Server+Request+Response: https://github.com/yegor256...

Michael • 6 years ago

Thank you! I was hoping you would do this. I've actually got a lot of code that I was writing for parsing and escaping URLs that I'd love to contribute. I'm sure it needs extensive review and refactoring, but I'd love to contribute it. At the very least, I'd like to contribute the knowledge I've gained from the attempt.

Mohit Nayak • 6 years ago

If you need builders, you can use Lombok, which autocreates builder classes for you based on annotations. Reduces the maintainability problem.

Vedran Grgo Vatavuk • 6 years ago

How about using static nested classes to get code auto-complete?
For example:


String html = new Response.Body(
new Response.AssertStatus(
new Request.WithMethod(
new JdkRequest("https://www.google.com"),
"GET"
),
200
)
).toString();
Yegor Bugayenko • 6 years ago

Not a bad idea!

Alex Williams • 6 years ago

You can make beautiful fluent interfaces with extension methods. The extension methods can even be in different namespaces, which can make them very easy to maintain. The real problem with object oriented programming are instance methods. They are unnecessary and lead to poor design decisions.

box_500 • 6 years ago

ASP.NET Core Web and the Mongo Drivers LOVE fluent. I agree with you, I don't like it.

Simone • 6 years ago

So, no more "Media" for "Printers"?

Darsstar • 6 years ago

Some what unrelated, I wonder what you think of the Rust language.

There is no inheritance, no classes, just traits. It has Universal Function Call Syntax, meaning x.foo(y, z) is syntactic sugar for Foo::foo(x, y, z) which the compiler allows when foo is unambiguous in a given context.
Not just the one who declares a struct/enum can implement a trait for said struct/enum, but also the one who declares the trait.

"I don't know enough about it and I don't feel like learning more about it" is a perfectly good answer.

Beat Kappert • 6 years ago

I would also like to see Yegor Bugayenko apply his unconventional thinking to functional programming.
Maybe some of the "bad OO" problems he has solved would not have come into existence in the first place with the functional paradigm?
I remember a statement along these lines: "Object oriented programming was INVENTED (it is a human idea, no natural laws behind it, just opinion), functional programming was DISCOVERED (it has a solid foundation in mathematics, see the "Category Theory for the Working Hacker" lecture by Philip Wadler, for example)."
My favorite languages for the future are Rust and Kotlin (where interoperation with the Java universe is needed). I do not consider myself a functional programming expert by any means, but I think this is where the future lies when it comes to more robust code.

Yegor Bugayenko • 6 years ago

Some other languages also have "traits", including PHP, for example. I wrote about this technique some time ago, check this out: http://www.yegor256.com/201...

Darsstar • 6 years ago

Which happens to have a comment of almost a year old with an ok explanation of why the breaks-encapsulation argument doen't apply for rust. (When defining a trait implementation for a value, you are either still within the encapsulation boundary, a module, or you are only allowed to use the public API.)

So I will conclude you haven't looked into Rust. (Not very much, anyway.)

PS. In case you want to learn a tiny bit about rust this might be one page from the book you will find interesting https://doc.rust-lang.org/b...

carfield • 6 years ago

so... are you suggesting decorators are sliver bullet?

Yegor Bugayenko • 6 years ago

If by "silver bullet" you mean a solution that kills any vampire, than no. You can't kill a vampire with a decorator. But you can solve most of the problems in OOP with a decorator. Not all, of course. But many of them. I believe that Decorator is a cornerstone design pattern in OOP.

Kapralov Sergey • 6 years ago
you can solve most of the problems in OOP with a decorator.

I'd replace "decorator" to just object composition in this statement. Decorators are good, but what makes them better than, let's say, adapter or composite?

Rafael Amizes • 6 years ago

Totally agree.
I would say that object composition is the solution for all problems in computer programming. (There are no problems in OOP; OOP -- via composition -- is the solution).
We should stop thinking that there are specific areas where O.O is not suitable. For instance, if performance is crucial, just use a compiled language like C++ or D. These languages implement O.O features in a very optimized way.

Asjinga Akangka • 4 years ago

That's what it's meant by the phrase "silver bullet"

NQA • 6 years ago

That's a much kinder answer. :)

For a bit of context (in case it was lacking): "...there is no single development, in either technology or management technique, which by itself promises even one order of magnitude improvement within a decade in productivity, in reliability, in simplicity." http://worrydream.com/refs/...

Decorators are clearly not a silver bullet. Even the whole of (some "proper" form of) OO hasn't achieved that yet. I'd be interested to know, though, if anyone is claiming that some form of OO even promises that.

NQA • 6 years ago

Almost certainly that's what he's suggesting. I'm just hoping that at some point someone wise will publish a paper about how there simply is no silver bullet and then this kind of madness (which only a few brave and brilliant people like yourself are beginning to stand against) will come to an end.

sonia zaytseva • 6 years ago

I personally feel the hype about fluent interfaces is getting out of line. Stream API is used up to a point that the code becomes less readable. Complex map lambdas, custom collector implementations, or just complex groupingBy statements, etc. Fluent API allows developers to easily overuse it.

Robert • 6 years ago

How do you unit test this:
String html = new BodyOfResponse(
new ResponseAssertStatus(
new RequestWithMethod(
new JdkRequest("https://www.google.com"),
"GET"
),
200
)
).toString();
? Asking because the method creates objects.

Yegor Bugayenko • 6 years ago

You test them individually, class by class -- this is called unit testing. Then you combine them all together in a composite object and test it -- this is called integration testing.

Skyler • 6 years ago

I think what Robert is saying is that the code above would call google.com unless that functionality could be mocked with a modified hosts file or something. Calling google.com violates the spirit of unit testing. With the Fluent API version, you can mock the method fetch() to return a canned body. In the OO example above, I am less sure I could run that code in a test scenario and not hit google.com.

Robert • 6 years ago

Sorry for the format:


String html = new BodyOfResponse(
new ResponseAssertStatus(
new RequestWithMethod(
new JdkRequest("https://www.google.com"),
"GET"
),
200
)
).toString();
Arsen Simonean • 6 years ago

I personally feel the hype about fluent interfaces is getting out of line. Stream API is used up to a point that the code becomes less readable. Complex map lambdas, custom collector implementations, or just complex groupingBy statements, etc. Fluent API allows developers to easily overuse it.

omid pourhadi • 6 years ago

Have you ever thought about reusing any objects or you just new everything.

Yegor Bugayenko • 6 years ago

Reusing through mutability? Or what do you mean?

Asjinga Akangka • 4 years ago

On the other words: (in pseudocode)

val a = new ComplexClass("a")
val b = new ComplexClass("a")

is replaced by this code

val a = new ComplexClass("a")
val b = a

NQA • 6 years ago

I guess the meaning is, holding a reference to an object you've already created and "reusing" it, rather than always new-ing everything up as you need it. My assumed answer to that would be that the only reason to hold onto a reference would be that the object isn't immutable (so you need it to keep track of state), or perhaps that performance is supercritical (so you don't want the overhead of creating another instance). I think these should both probably be edge-cases, and avoided as much as possible

Tomáš Votruba • 6 years ago
Yegor Bugayenko • 6 years ago

Me too, I quoted it above.

Marten Gajda • 6 years ago

At some point we've experimented with fluent adapter types. That is, a type which extends the non-fluent base type and adds the fluent methods for convenience. Normally there is only a single implementation of this fluent type which delegates to the non-fluent base type. All the fluency methods just apply the regular decorators, so they don't contain any additional logic.

FluentIterable is a very minimal example of this approach. The sole implementation is Fluent which looks like this:


public final class Fluent<E> implements FluentIterable<E> {
private final Iterable<E> mDelegate;

public Fluent(Iterable<E> delegate) {
mDelegate = delegate;
}

@Override
public FluentIterable<E> filtered(Filter<E> filter) {
return new Fluent<>(new Filtered<>(mDelegate, filter));
}

@Override
public <T> FluentIterable<T> mapped(Function<E, T> function) {
return new Fluent<>(new Mapped<>(mDelegate, function));
}

@Override
public Iterator<E> iterator() {
return mDelegate.iterator();
}
}


Usage is like you'd expect from a fluent type, except that you apply the fluent adapter first:


Iterable<String> it = new Fluent<>(iterableOfStrings).mapped(s -> "hello "+s).filtered(s -> s.lenght()<10);


This way we can make any type fluent without having to implement the methods over and over again in every implementation and the base type stays lean and clean.

In the end, we didn't really pursue this approach because I prefer the decorator notation. Though we can easily add new methods without breaking existing implementations if we decide to return to this pattern.

amihaiemil • 6 years ago

Well, it's not only about fluency, it's also about the level of abstraction. For instance, jcabi-http's interface to the user is nice, but it is too abstract. I implemented that ``multipartBody()`` method so I had to study the library a lot -- I came to the conclusion that it is currently impossible, in jcabi-http, to implement file upload without changing user-facing interfaces, creating a release that won't be backwards compatible. So, maybe if the abstractions weren't so high, then also fluent methods wouldn't be a problem.

Nevertheless, I guess it comes down to what you want to implement: an SDK or an API. Fluent interfaces means it's an API, a system fully encapsulated behind properly linked interfaces. If it's an SDK, then it's just a set of tools that the user puts together to build something. I illustrated this idea here: http://www.amihaiemil.com/2...

jcbai-http offers an API for making HTTP calls, while the alternative, with small objects, decoration etc is actually an SDK for making HTTP calls.

IR Bosman • 6 years ago

I think C# allows for a way to have clean fluent interfaces, while keeping classes short, using extension methods. It's not entirely as clean as I'd like it, since you do need to create a static method to contain the extension method, but it allows for clean, small classes with a single function, while still being extendible.

Do you see any reason when/why something like the following would be wrong/insufficient?


_____ FILE 1 _____
public class Action : IAction
{
public Action( /* stuff */){

}
}

_____FILE2____
public class ActionWithLogging : IAction
{
public ActionWithLogging(IAction action) {
/* stuff */
}
}

public static class ActionWithLoggingExtender
{
public static ActionWithLogging WithLogging(this IAction action){
return new ActionWithLogging(action);
}
}

EDIT: Actually, what do you think about extension methods in general? While they are somewhat like helper classes in that they're static and are often used wrongly, I do think they're a very powerful tool to keep your code legible. Do you think they're a good thing often used wrongly, or, because you can use them wrongly, they probably should not exist?

Guest • 6 years ago