It seems to me that part of the difficulty here lies in choosing a good descriptive name that covers everything that the method does. Naturally, the problem is that sometimes you have a lot of complex logic that you cannot easily describe with a simple name.
In the case presented in your code example, I will be tempted to simplify the name of the method itself to something more generalized:
Receipt Transaction(long amount, long cardNumber) { buy(amount); accumulatePoints(cardNumber); return generateReceipt(); }
What about this logical problem that I talked about? This in itself boils down to whether your method is really fixed in what it does. If a transaction can only be performed using the Buy-> Points-> Receipt sequence, then a simpler name will be used, but also a more reasonable name and convenient interface.
How about when the client does not have a reward card or does not want to receive a receipt? How about situations where several items can be purchased in one transaction - unless, of course, the purchase method can be a purchase, and not just the total amount that was calculated elsewhere? Once you start introducing questions / options into the sequence, the design becomes a little less obvious and the naming is much more complicated. Of course, you would not want to use a crazy long name, for example:
BuyAndAddPointsIfTheCustomerHasACardAndReturnAReceiptIfTheCustomerAsksForIt(...)
Of course, he definitely talks about what he is doing, but also points out a potential problem in that the method may be responsible for too many things or that it may hide the more complex smell of the code behind one of the methods that it calls. Similarly, a simple method name, such as โTransaction,โ can simplify a complex problem that needs to be better understood.
A free interface can be very beneficial if it helps the developer make smart decisions on how to apply the quick methods called. If the call sequence is important, you need to limit the return data types to the next selection in the sequence. If the calling sequence is less important, you can use the return type with a more generalized interface that allows you to select methods in any sequence.
As for whether to really use a free interface, I do not think that this should be solved only as a means for decomposing hard-to-determine methods. You make the choice of design with which you will have to live throughout the life of the product, and from the point of view of service, I found that smooth interfaces can make design more difficult to visualize and organize and maintain in your code. Ultimately, you need to decide if this could be something you can live with as a compromise with the benefits it gives you. For me, I usually start by asking whether the use case combinations are random and simple, or if they are relatively infinite. If the latter, a free interface can help keep the code cleaner and easier to use in multiple scenarios. I would also like to consider whether the code belongs to a more generalized layer, for example an API, for example, when a free interface can work well or something more specialized.