Creating quality code should be the foremost thought on every developer’s mind. In this Agile world, code produced by a developer is not individually owned. So, we should not be afraid to provide useful code-review comments and should appreciate the code feedback we get from team members. One often-overlooked item in development is the Naming convention. One way developers can practice ‘kind programming’ is by leaving code in better shape than it was in when they got it. (Boy Scout rule?)
There are plenty of articles on coding, design and refactoring. So, this blog will take some sample code, analyze it piece by piece, modify it, and touch upon some theoretical concepts along the way. The end result will be better code. Important concepts to read about further are in bold and links to explore more are in [square brackets].
I chose Java for the code example in this post. However, this is applicable to any programming language.
The example code I chose reads a comma-separated string from a text file and creates a list of objects from it. Please take a moment to look over the three screenshots below.
Screenshot #1 Contents of products_data.txt file
Screenshot #2 Product POJO
Screenshot #3 ProductsService class for operations related to Products
At first glance, notice that apart from the class name ProductsService, the naming convention for the variables and methods is not proper.
The name of the method is generic, however the statements inside the method perform the specific function of reading a specific file from a specific location.
Below are better suggestions for naming the variables.
stream should be renamed to Stream streamOfProducts
list should be renamed to List csvProductsList — meaning it is a list of comma-separated values
- Paths.get(System.getProperty(“user.home”), “products”, “products_data.txt”) should be extracted out as a local variable or given a meaningful name like productsDataFilePath or getProductsDataFilePath
- readFile() should be getCsvProductsListFromFile()
STOP here before making the method name change. WHY? This is going to break the Test case. This is a classic case of mixing code rewriting and code refactoring.
See the definition of Refactoring below. When refactoring, we should be doing so iteratively to avoid test failures. As common sense suggests, it would be illogical to create better code at the risk of affecting the functionality.
Refactoring: Refactoring is the process of changing a software system in such a way that it does not affect the external behavior of the code yet improves its internal structure. [https://refactoring.com/]
Below are better suggestions for naming the variables.
list should be List csvProductsList (see how I copied the name of the returned variable of the readFile() method?)
Okay, let’s deviate a little on a design aspect. See the calling of the setter methods of the Product inside the lambda block? PAUSE here. What’s going on?
How do we ensure the Product object is not modified inadvertently in some other function or by some other thread? This is a perfect use case to make the Product object immutable.
Immutable: An object is considered immutable if its state cannot change after it is constructed. Maximum reliance on immutable objects is widely accepted as a sound strategy for creating simple, reliable code. [https://docs.oracle.com/javase/tutorial/essential/concurrency/immutable.html]
Many ways to make a Java object immutable may be found in the Java documentation. For this post, I am not going to overly reengineer using Reflections.
The simplest trick is to remove the setter methods and mark the instance variables as final and introduce a constructor with these three final variables as parameters. One important rule in Java’s immutability is that the methods inside a class should not leak object handles. getCreatedTime() method returns a LocalDateTime which is immutable. However, if it is a type of java.util.Date, the getter should return a clone or a copy.
One other design consideration on the constructor is: what if there are five more fields to initialize and/or some fields are optional? Builder pattern comes to the rescue.
Builder Pattern: The builder pattern is a design pattern designed to provide a flexible solution to various object creation problems in object-oriented programming.
The intent of the Builder design pattern is to separate the construction of a complex object from its representation. It’s one of the Gang of Four design patterns. [https://en.wikipedia.org/wiki/Builder_pattern]
One hot debate (not discussed here) is whether to use a static factory method to initialize or a public constructor. However, it should be the best decision to consider. An example: say you have a class called Customer, with 2 constructors, one with 2 String parameters and another with 3 String parameters. Wouldn’t it be better to use meaningful static factory methods instead of exposing the constructor? Check out the definition and learn more in the link provided.
Screenshot #4 Static Factory Method example
Static Factory Method Vs Constructor: One advantage of static factory methods is that, unlike constructors, they have names. If the parameters to a constructor do not, in and of themselves, describe the object being returned, a static factory with a well-chosen name is easier to use and the resulting client code is easier to read. [Effective Java by Joshua Bloch]
Below is how our Product and ProductsService class would look after making the naming and design changes.
Screenshot #5 Modified Product POJO
Screenshot #6 Modified ProductsService class
We hope you found this information helpful. Lastly, if you have any additional questions around creating quality code through meaningful naming and design practices, please don’t hesitate to reach out to us. We’d love to help you out.