Have you seen code like this?
void transform(String fileIn, String fileOut, String separatorIn, String separatorOut); |
This seems simple enough, but it can be difficult to remember the parameter ordering. It gets worse if you add more parameters (e.g., to specify the encoding, or to email the resulting file):
void transform(String fileIn, String fileOut, String separatorIn, String separatorOut, String encoding, String mailTo, String mailSubject, String mailTemplate); |
To make the change, will you add another (overloaded) transform method? Or add more parameters to the existing method, and update every single call to transform? Neither seems satisfactory.
One solution is to encapsulate groups of the parameters into meaningful objects. The CsvFile class used here is a “value object” — simply a holder for the data.
class CsvFile { CsvFile(String filename, String separator, String encoding) { ... } String filename() { return filename; } String separator() { return separator; } String encoding() { return encoding; } } // ... and do the same for the EmailMessage class void transform(CsvFile src, CsvFile target, EmailMessage resultMsg) { ... } |
How to define a value object varies by language. For example, in Java, you can use a record class, which is available in Java 16+ (for older versions of Java, you can use AutoValue to generate code for the value object); in Kotlin, you can use a data class; in C++, you can use an option struct.
Using a value object this way may still result in a long parameter list when instantiating it. Solutions for this vary by language. For example, in Python, you can use keyword arguments and default parameter values to shorten the parameter list; in Java, one option is to use the Builder pattern, which lets you call a separate function to set each field, and allows you to skip setting fields that have default values.
CsvFile src = CsvFile.builder().setFilename("a.txt").setSeparator(":").build(); CsvFile target = CsvFile.builder().setFilename("b.txt").setEncoding(UTF_8).build(); EmailMessage msg = EmailMessage.builder().setMailTo(rcpt).setMailTemplate("template").build(); transform(src, target, msg); |
Always try to group data that belongs together and break up long, complicated parameter lists. The result will be code that is easier to read and maintain, and harder to make mistakes with.