Series Clean Code – Chapter 10: Classes

Tram Ho

1. Class Organization

By convention, the Class should start with a list of variables. Public static constants if present should be first. Then comes the private static variables. Rarely is there a good reason to set a public variable.

Public functions should be implemented in a list of variables. Place private utilities called by a public funcion immediately after that public function. This follows Stepdown’s rules and helps the program be read like an article.

  • Encapsulation

We want to keep the variables and utility functions private, but we shouldn’t be too crazy about it either. Sometimes we keep a utility variable or function in protected mode, so that it can be accessed by a test. For us, testing is the principle. If a test in the same package needs to call a function or access a variable, we leave it protected or package scope. The first thing we do, however, is find a way to maintain privacy. Loosening of the encapsulation is always the last resort.

=> List of variables: public static constants – private static variable – (public variable)

2. Classes Should Be Small

First rule, classes should be made small.

Second, classes should be made even smaller.

No, we are not going to repeat exactly what is in the Functions Chapter. But, like Functions , smaller is the first rule when it comes to designing classes . Like Functions , the direct question is always “How small?”

With Functions , we measure with line counts. With Classes, we measure differently, we count responsibility.

An example of a class , SuperDashboard, with about 70 public methods. Most developers assume it has a super size. Others claim it as a “God class”.

Next is also SuperDashboard, but contains only methods:

The five methods aren’t many, right? In this case it can be seen as such, but despite the small number of methods, SuperDashboard has many responsibilities.

The class name should describe what responsibility it fulfills. In fact, naming is probably the first way to help determine the size of a class. If we can’t get a concise name for the class, then it’s likely that the class is too large. With a vague name for the class, it was more likely that it took a lot of responsibility.

We can write a brief description of the class in about 25 words, without using words like “if”, “and”, “or” or “but”.

  • The Single Responsibility Principle

A class or module should have one and only one reason for the change . This principle gives us a definition of the responsibility and also the policy of the size of the class. Class should have a responsibility – a single reason for change.

The example above, the SuperDashboard class has two reasons to change. First, it keeps track of the profile and appears to need updating every time the software is shipped. Second, it manages the Java Swing component (a derivative of JFrame, Swing represents a top-level window interface). No doubt we will update the version number if we change anything in the Swing code, but the opposite is not necessarily true: we can change the version information based on the change. The code is elsewhere on the system.

Trying to identify responsibilities often helps us see and create better abstractions in code. We easily get 3 methods in SuperDashboard that distribute version information to separate a class named Version . This class has the potential for reuse in other applications!

SRP is one of the key concepts in object oriented design. It is also a simple concept to understand and adhere to. However, to the extent that the SRP is the most often humiliated principle ? We often come across classes that do too many things. Why?

Getting the software to run and making the software clean are two completely different things. Most of us are limited in our minds to prioritize making our code run rather than organizing and making it clean. This is completely appropriate. Staying separate from relationships is only important when our plan works.

The problem here is that too many of us think we’re just doing it for the program to work once. We failed to organize and clean them up. We move on to other problems than going back and breaking up classes to cram according to the principle that each class should bear a unique responsibility.

At the same time, many developers worry that a large number of small classes for single purposes will make it difficult to understand the big big picture. They worry about having to go from class to class to understand how a great piece of work is accomplished.

However, a system with many small classes does not move much more than a few large classes. It’s as much as reading a system with few large classes. So the question here is: Do you want your tool to have multiple small drawers, each with its own unique concept and label? Or do you want a drawer and everything in it

Each large system will contain a large amount of logic and complexity. The main goal in managing such complexity is to organize it so that the developer knows where to find things and just understands the complexity of the problem to learn immediately. In contrast, a system with large and purpose-rich classes always hinders us by trying to force us to wade through a lot of things that are not necessarily known right now.

=> We want our system to be made up of many small classes, not few classes, but big ones. Each class is summed up by a single responsibility, with only one reason to change, interact with a few other objects to achieve the state that the system wants.

  • Cohesion

Class should have a small number of variables. Each method of a class should manipulate one or more of those variables. Each variable in the class used by each method should have maximum cohesion.

The maximum cohesion here is that the methods and variables of the class are dependent and bound together as a whole.

Simply put, the class should limit the number of variables, and the methods in the class all use at least one of these variables to increase cohesion.

Maintaining Cohesion Results in Many Small Classes

If you divide a large function into many smaller functions, this also increases the class size. Assume that the function is large with many variables declared in it. You want to extract a smaller function then that function out. However, your code wants to use the 4 variables declared in the function. Do you need to pass all four variables to the new function as an argument?

Not at all! If we put those 4 variables in the common variable of the class, then we can extract the code without passing any variables there. It’s really easy to break the function up into small pieces.

Unfortunately, that stops the class from being tightly bound, because we are increasingly piling up variables into the class while only a few functions need to share them.

So, if some function wants to share a certain number of variables, why don’t we create a class for them? That is of course, if the class has lost its unity, split it!

3. Organizing for Change

For every system, the change is continuous. Each change leads to the risk of the rest of the system not working as intended. In a clean system, we organize classes to reduce the risk of change.

Example: Class Sql – SRP Violation

Fix:

=> We structure our system as garbage as possible when we update them with new features or change features. In an ideal system, we incorporate new functionality by extending the system rather than changing the existing code.

  • Isolating from Change (Loose Coupling – Dependency Invertion Principle)

Needs will change, so the code will always change. In a specific class, containing the implementation details (code), the abstract class along with that is representing the concept. A client class that is dependent on a particular detail is the risk that those details change. We can adopt interfaces and abstract classes to help prevent collisions with those details.

Dependence on a particular detail creates challenges for system testing.

For example: We build a Portfolio class and it depends on the external TokyoStockExchange API to get the value of the portfolio. The test case will be affected by the volatility of such a lookup. It is very difficult to write tests when our answers will be different after only 5 minutes.

Instead of designing the Portfolio so that it is directly dependent on TokyoStockExchange, we create an interface, StockExchange, and declare a unique method:

Now our test can be performed on the StockExchange interface emulator for TokyoStockExchange.

If a system is to be decoupled, it is more flexible and promotes reuse. Coupling restriction means that elements in the system are isolated from others and from changes. Isolation makes each system component easier to understand.

Share the news now

Source : Viblo