How to protect yourself from incorrect settings

Let's say I have this important method:

int generateId(int clientCode, int dataVersion) {
    return clientCode * 2 + dataVersion % 2;
}

      

Both parameters int

, so it's pretty easy to call this method with inappropriate parameters, eg generateId(dataVersion, clientCode)

. It will compile and execute successfully. But the generated ID will be completely wrong, which can cause serious problems.

So my question is, is there a way to protect myself from such parameter bias?

Now I could only think of changing it to int

wrapper classes:

int generateId(@Nonnull ClientCode clientCode, @Nonnull Version version) {
    return clientCode.getValue() * 2 + version.getValue() % 2;
}

static class IntWrapper<T> {
    private final int value;

    IntWrapper(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }
}

static class ClientCode extends IntWrapper<ClientCode> {
    ClientCode(int value) {
        super(value);
    }
}

static class Version extends IntWrapper<Version> {
    Version(int value) {
        super(value);
    }
}

      

and calling it with generateId(new ClientCode(clientCode), new Version(version))

. Of course, this does not guarantee complete protection, but at least it is more transparent.

Is there a better / different way?

+3


source to share


4 answers


Think about how to set up your object using chaining methods like this

configure().withDataVersion(dataVersion).withClientCode(clientCode).generateId(); 

      



While it makes the configuration much more detailed, it is also self-explanatory.

DataVersion and ClientCode-Information can be moved into an inner class. configure()

initiates an inner class, withDataVersion(int)

and withClientCode(int)

are basically setters. generateId()

will build and return Id like today.

+6


source


Writing wrapper classes for each individual parameter, just to make sure someone is not confused with the order of the parameters, sounds pretty extreme and is cumbersome to use.

If clientCode

or dataVersion

can fit into smaller data types like byte

or short

, you can use that to differentiate. If some of these values ​​have a specified range of values ​​(for example, 1 to 100,000), use a compile-time check inside the method and throw an exception if the specified value does not match (which can happen if the caller parameters are invalid).

If you add additional parameters int

, your concern will be more justified. In this case, write only one wrapper class that will contain all the parameters:



public class Input {
    private int clientCode;
    private int version;
    //other parameters
    ..
    // getters and setters
}

      

By using explicit getters and setters, you make the caller care about the supplied values. Bonus - you can add default values ​​to some of the parameters if needed. Method signature now int generateId(Input input)

.

This is a good practice, as documented in Joshua Bloch Effective Java .

+1


source


You are on the right track, refactoring the code further, making high quality classes ClientCode, Version

and Id

. Rebuild the int wrappers. Make Id

knows all about how to generate a version, in other words, encapsulate code into a class Id

.

Id generateId(ClientCode code, Version version) {
    return new Id(code, version);
}

      

0


source


As pointed out by Tom in the comments, java does not provide any means to prevent such an issue. Other languages ​​such as python and scala support named arguments to solve this problem.
Although it's good, you don't need to have one.

Java relies on programmers to properly understand and use all libraries. For this reason, proper document comments and documentation should be used.

Another alternative to using the wrapper classes you suggested would make the code cumbersome and difficult to understand. Not to mention adding extra lines of code that don't have this purpose.

conclusion . Relying on the knowledge of programmers and create documentation. Don't use wrapper classes. Since this is a widely used approach to this in the java community.

0


source







All Articles