3

This well-known article criticises Java on the basis that it does not allow you to write functions that do not live in a class. This flaw forces you to write classes with names that look suspiciously like verbs, such as GridCellHander or GarbageDisposalDestinationLocator. In other words, it looks like you've built a class (i.e. a noun) to escort these methods (i.e. verbs). I believe that the article is entirely correct and I feel disturbed when I write a class with a name similar to these, particularly in a language that does not have Java's restrictions.

However, it is utterly standard advice that if you have a small amount of methods within a class that are all the only users of some of the class's state, then those methods and their shared state should all be placed in a different class. This maintains cohesion and keeps your classes small.

How can this advice be followed without writing an "escort" class that clearly only exists to hold these methods?

For example, I'm currently dealing with a grid. Some of its cells represent times. These times need to be sent to my server. I originally had two independent methods for handling them, one for deleting from the server and one for upserting. When I realised that they shared state, I put them and their shared state in a TimeCellHandler class. This let me significantly simplify the parameter list of the methods and let me write helper methods that they share. This follows the guidance described in my second paragraph, but violates the first. I can't just call this class TimeCell, because it plainly doesn't represent those cells; It only represents the small subset of their data needed to talk with the server about them.

Note that this question is language agnostic. The article linked gives the impression that functional languages (e.g. Scheme) can always replace TimeCellHandler with something better, but I'm just as happy to see an OOP or non-OOP answer. I don't care if you have to resort to CLOS or macros. I will be happy with any answer that demonstrates how to group two methods that share state in a sensible way that does not involve naming a class (or your language's most similar concept) in such a way that it makes it obvious that the class only exists to hold the methods.

20
  • 2
    @DocBrown Closures are just a poor man's classes.
    – J. Mini
    Jun 9, 2023 at 22:41
  • 1
    Been a while since I used them, but Java has inner classes, right? So the methods could go into TimeCell.Handler.
    – user949300
    Jun 10, 2023 at 6:04
  • 1
    @J.Mini: better call it "a lightweight alternative to classes" - exactly what you asked for, if I got you right.
    – Doc Brown
    Jun 10, 2023 at 6:21
  • 1
    How can this advice be followed without writing an "escort" class that clearly only exists to hold these methods? by not implementing the code in Java?
    – Laiv
    Jun 10, 2023 at 7:22
  • 1
    @J.Mini The post's owner doesn't give an alternative (another OO language that makes it better) so he's complaining by pleasure. If escort classes are code smell and you are against such a thing (which is legit too), the solution is "don't use Java". Or any OO language which functions are not first-level citizens. We can not complain about hammers not being good at screwing.
    – Laiv
    Jun 10, 2023 at 11:07

2 Answers 2

7

So you've followed people's advice and split some logic and state into a class, but you cannot give this new class a good name. Now is a great time to examine the design decisions that lead you to this conundrum.

This part of the question immediately caught my attention:

However, it is utterly standard advice that if you have a small amount of methods within a class that are all the only users of some of the class's state, then those methods and their shared state should all be placed in a different class. This maintains cohesion and keeps your classes small.

This advice is often given when dealing with a very large class, and splitting the class up makes the code easier to reason about. You should be able to identify an abstraction that can be pulled out of that large class, which lives just fine on its own.

As with all advice on software design, you must assess whether this is beneficial to your current code. I also don't see how relentlessly moving code into smaller and smaller classes maintains cohesion. The concept of cohesion applies equally to how well classes cooperate with each other as it does to the members within a single class.

Fields do not need to be used in 100% of the methods in a class for that class to be cohesive. These things need to work well together and compliment one another. If they do that, don't separate them. They belong together (hence they are "cohesive").

Each time you reorganize some code, it should be fairly easy to give it a good name. The methods and internal state combined with your knowledge of the business process you are modeling should guide you when naming this new abstraction.

When you can no longer find a good name, stop. Put that code back where it used to be. It was fine there. There is nothing wrong with a subset of methods in a class accessing a subset of fields.

Another part of the question jumped out at me:

I can't just call this [TimeCellHandler] class TimeCell, because it plainly doesn't represent those cells; It only represents the small subset of their data needed to talk with the server about them.

I can see a couple of missing abstractions which might lead you to better names:

  • What is this small subset of data? Is there a business concept associated with it? Consult with your end users, a subject matter expert, or whoever is gathering requirements for the application. They might already have a good name.

  • Talk to the server? Deletes? Upserts? This sounds like data access logic to me.

You might be missing a business class that represents that subset of data. You also might be missing an abstraction that allows you to access and manipulate data in permanent storage, be that a file system, web service, or database.

Names become easier to identify once you refocus on the business process you are modeling. Try to identify other common categories of logic, like data access. This can lead you to better names, and other opportunities to move code into appropriate abstractions that still maintain cohesiveness.

Then you don't need "escort" classes and this question becomes moot.

Oh, and a quick thought experiment. If you cram a bunch of free floating functions into their own file, what would you name the file? And would that name be any better than the class that only serves as a container for those functions? Why wouldn't you name the class the same as that file?

1
  • +1 I especially like "When you can no longer find a good name, stop"
    – user949300
    Jun 12, 2023 at 17:15
4

These times need to be sent to my server. I originally had two independent methods for handling them, one for deleting from the server and one for upserting. When I realised that they shared state, I put them and their shared state in a TimeCellHandler class.

This is fine? Now you just need a name that represents what the code is rather than what it does. TimeCellServerConnection? Something similarly nounlike.

And you might point out that simply changing a name has no impact at all on the code's actual design. There's no change in performance or behavior. I'll leave it as an exercise for the reader the consequences of this fact.

1
  • +1. And in case there are any doubts, for me "design" is not just about "performance of behaviour", it is clearly also about choosing expressive names. @J.Mini: though I mentioned the technical possibility of using anonymous function in another comment to avoid the necessity to come up with a name, I doubt it would make the code more readable for this specific case. Introducing a class TimeCellServerConnection is probably way more expressive.
    – Doc Brown
    Jun 11, 2023 at 15:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.