Is it good practice to write classes that usually have only one public method?

The more I write unit tests, the more often I find that I write smaller and smaller classes. Classes are so small now that many of them have only one public method associated with the interface. Then the tests go directly against this public method and are quite small (sometimes this public method will call internal private methods inside the class). Then I use the IOC container to control the creation of these easy classes, because there are so many.

Is this typical of trying to do something more in a TDD manner? I'm afraid that I have now reworked an inherited 3,000 class of strings in which one method was included in one that is also difficult to maintain on the other side of the spectrum, because now there are literally about 100 different class files.

Am I doing too much? I try to follow the principle of a single responsibility with this approach, but I may be in what is the structure of an anemic class, where I do not have very intelligent "business objects".

+4
source share
4 answers

These many small classes let me down. With this design style, it becomes very difficult to understand where the real work is done. I am not a fan of having tons of interfaces, each of which has a corresponding implementation class. The presence of a large number of pairs of "IWidget" and "WidgetImpl" is the smell of code in my book.

Breaking the 3000 line class into smaller pieces is wonderful and commendable. However, remember your goal: to make the code more readable and easier to work with. If you end up with 30 classes and interfaces, you most likely created a different type of monster. Now you have a really sophisticated class design. It takes a lot of mental effort to keep that many classes right in the head. And with a lot of small classes, you lose a very useful opportunity to open a couple of key files, choose the most important methods and get an idea of โ€‹โ€‹what is happening.

For what it's worth, I'm not really selling a test design though. Writing tests early is reasonable. But reorganizing and restructuring the design of your class to make it easier to test for fashion? No thanks. I will only create interfaces if they create architectural meaning, and not because I need to be able to combine some objects so that I can test my classes. This put the cart before the horse.

+2
source

You may have gone too far if you asked this question. Having only one public method in a class is not bad as such if this class has a clear responsibility / function and encapsulates all the logic regarding this function, even if most of them are in private methods.

When reorganizing such outdated code, I usually try to identify the components in the game at a high level, which can be assigned different roles / responsibilities and divide them into their own classes. I think about which functions should be responsible for the components and move the methods to this class.

+1
source

You are writing a class so that instances of the class keep state. You put this state in the class because all the state in the class is connected. You have a function to manage this state so that you cannot set invalid state permutations (a notorious square that has the width and height of an element, but if the width doesn't equal the height, it's not a square.)

If you have no state, you do not need a class, you can just use free functions (or in Java, static functions).

So the question is not, should I have one function? but rather, "what state object does my class encapsulate?"

Perhaps you have one function that sets the whole state - and you should make it more granular, so that, for example, instead of void Rectangle::setWidthAndHeight( int x, int y) you should have setWidth and a separate setHeight .

Perhaps you have a ctor that sets things up, and one function that doesIt , no matter what it is. Then you have a functor, and one doIt may make sense. For example, class Add implements Operation { Add( int howmuch); Operand doIt(Operand rhs);} class Add implements Operation { Add( int howmuch); Operand doIt(Operand rhs);}

(But then you may find that you really want something like a visitor pattern - a cleaner functor is more likely if you have objects with a pure value, a visitor if they are located in a tree and connected to each other.)

Even if these are many small objects, a single function is the right level of detail, you might want something like a facade pattern to make up of primitive operations, often used complex operations.

No answer. If you really have a bunch of functors, that's cool. If you really make every free function a class, this is stupid.

The real answer lies in the answer to the question: "What state do I control, and how well do my classes model my problem area?"

0
source

I would reflect if I would give a specific answer without looking at the code.

However, this sounds like you're worried, and this is a specific flag for viewing code. The short answer to your question goes back to the definition of Simple Design . The minimum number of classes and methods is one of them. If you feel that you can select some elements without losing other desired attributes, continue and collapse / insert them.

Some pointers to help you decide:

  • Do you have a good test for "One Responsibility"? It is deceptively difficult to understand, but this is a key skill (I still do not see it as a master). This does not necessarily mean one class of methods. A good criterion is 5-7 public methods in the class. Each class can have 0-5 employees. Also, to check SRP compliance, ask a question, what could lead to a change in this class? If there are several unrelated answers (for example, changing the structure of a package (parsing) + changing the contents of a package to an action map (command manager)), you may need to separate the class. On the other hand, if you feel that changing the structure of the package can affect 4 different classes - you fled from another cliff; perhaps you need to combine them into a cohesive class.
  • If you are having problems with the names of specific implementations, you may not need an interface. for example, the XXXImpl classes you need to pay attention to XXX need to look at. I recently learned about a naming convention where the interface describes the role and the implementation is called the technology used to implement the role (or back to what it does). e.g. XmppAuction implements an auction (or SniperNotifier implements AuctionEventListener)
  • In the end, is it difficult for you to add / modify / test existing code (for example, installing a test is long or painful)? These may be signs that you need to go for refactoring.
0
source

Source: https://habr.com/ru/post/1315876/


All Articles