While writing methods, please pay close attention to the method arguments, This is the one area where the method assimilates the foreign body into its core body. Foreign material is always dangerous, you do not have any control over it, but as an owner of a method, you can put a defensive mechanism aka Validation. Or the Anti-corruption layer of your method.
So, the first tip.
Always put validation on the input argument, you can use java annotations to do the validation.
Now, let's think about what we do in a method after receiving the inputs.
Most methods will do three things.
It will act on the inputs:: so the method will receive the inputs and apply some business logic on them and get a result.
Example::
public void buildReport(String firstName, String lastName, String address, int age, char sex, String dept) {
//build the report
}
If you pay attention to this method we can derive one important thing, the input parameter passed here has associativity, what I meant to say, all input parameters are passed into the method for which reason? Simple, to create the report we need those data. Think logically, when you build a report it will focus on a segment or a context. The Business rule always works to accomplish a feature inside a domain. So if you build a report which needs the above arguments that means you are dealing with the data which are used together frequently because they are in the same domain and same context.
In some cases, I saw some methods which take data from multiple domains and aggregate it and give a view of overall functionality but those cases are less. Mostly a method works on a narrowed feature under a domain.
Tip 2 : if your arguments list is long that means you are breaching encapsulations, easily you can tie them under an object as those arguments are often changed or used together, then pass that wrapped object.
I can modify the above method cleaner way
public void buildReport(EmployeeReportContext employeeReportContext) {
//build the report
}
I tied related arguments in the EmployeeReportContext object and make it a Monadic function(A method which takes one argument)
2. Methods will take decisions based on the arguments:: it will take the arguments and based on the arguments perform some algorithm or business rule to accomplish a feature.
public void serachEmployee(String firstName,String lastName, String latLong, ,Long deptId) {
//build the search query based on the inputs.
}
Again, we can see we are dealing with related input which can be encapsulated in an object.
Now on top of this, another observation is if we pass multiple arguments that means this method is responsible for multiple decision making but we know in SRP Pyramid on a particular level one method is responsible for one responsibility. So the method is breaking SRP, how we can solve this, just segregate out the decision/responsibility by extract method strategy.
I will break the methods into small chunks, in this way by extracting responsibility. Also, reduce the argument list and make each method a monadic Method.
public void searchEmployee(EmployeeSearchInput input) {
Address address = findAdressFromLatLong(input.getLatLong());
Department dept = finDeptbyID(input.getDeptId());
//TODO :: populateEmployeeSearchInput using address and dept
query = buildSearchquery(input);
result = executeSearch(query)
}
3. Holder methods:: These kinds of methods are edge methods that will take the arguments and pass those arguments to next-level methods.
Now first understand why we need this type of method? I like to call them Edge methods like edge service, while we are building an N-tier architecture often we need to pass a good amount of data to the next tier, now if you pass multiple arguments to the next level it will create coupling.
On the other hand, we also do not want to call next tier methods multiple times, then in a distributed system, it will create a Chatty communication where we need to pass multiple data over the network multiple times.
So it boils down to one principal question: how we can pass all data in one shot,on top of that we need to think about the future if I want to pass new data between tiers how to accommodate them because the holder method is a contract between two layers you can’t change method signature often !!!!
So passing multiple arguments in the holder methods is not a solution, as in the future you can change the signature.
So the best way to design this type of method is to create a coarse-grained object (DTO), and pass all data in one shot, or a Map like structure key is the property and value is the object, in both cases, you can easily extend them without breaching the contract.
Example:: The best example is the HTTPRequest object it carries data from UI to controller and servlet doPost and doGet is the holder method which takes the request, extracts parameters from it, and then passes to the next level.
Also, DTO (Data Transfer Object) is doing the same job.
Conclusion:: what we learned from the article?
We learned whatever the method type is, always encapsulate the inputs parameter, never pass multiple parameters in a method, it brings smell in different ways.
Try to restrict method argument to null-- no foreign body, or one, maximum 3 but over than three is risky, needs to wrap them or need to extract the responsibility from the calling method.