1

There is a service:

createUser(User user)

where you can create a new user, with so many attributes (name, address lines...). Now, some user attributes are mandatory. Other attributes are not mandatory (like nickname or image).

The problem is that, from the outside, you can't tell the difference (in compile time). You can't force yourself to set only mandatory fields; moreover, the list of mandatory fields can change in the future and your code will stop working (while still compiling).

So we can

  • createUser(int userId, String username...) - explode the bean properties and don't use bean as an argument. Now every change will be reflected in the API, but method signatures may become huge. Also, when overloading methods for various combinations of mandatory fields with non-mandatory - that can be a mess, too.
  • use multiple beans, and have methods like (given names are just illustrative) createUser(UserCoreData user), createUser(UserFull user) but that still would not help much.
  • or have service for storing mandatory User data and then having additional services for storing additional, non-mandatory data.
  • (EDITED) or to split User into logical components (ie smaller beans) Address, DateFrom... and use those in service methods.

Is there a pragmatical way to describe service method and its arguments in case like this? (in Java).

2

Don't use a Java Bean if you can

While Java Beans are extremely useful on many occasions, I would argue that it would be better for User not to be a bean in the context you describe. You can design this class so that its instances are always in a valid state.

In the following example, name is mandatory and address is optional.

class User {
    private String name;
    private String address;

    User(String name) {
        setName(name);
    }

    void setName(String name) {
        if (name == null)
            throw new IllegalArgumentException("[name] cannot be null");
        this.name = name;
    }
    String getName() {
        return name;
    }

    void setAddress(String address) {
        this.address = address;
    }
    String getAddress() {
        return address;
    }
}

As you can see, with that design, it is not possible at any point to have a User instance without a name. However, you can have instances with an address, as well as instances without one.

Also notice that in the code example above I am performing some basic validation. If needed, you can perform more complex validation than that. For example by making sure the name does not contain characters not likely to appear in your context.

Things to consider

There are a few things to consider to make sure such a class meets its goal. Only its immutable content can be shown to the outside world, for example. Consider another similar class that contains a list that must always contain at least one item. If that list is mutable and external code can access it (through a getter, for example), nothing stops it from removing objects from it until it is empty, putting your initial instance in a state that shouldn't be allowed. Of course, if you need to expose that data, consider only giving a copy of the list or have it immutable.

What if there are a lot of mandatory values ?

If it makes sense to group some of these values in classes of their own, and that in doing so the constructor becomes wieldable, go for it. Such classes should be designed with the aforementioned constraints in mind.

Alternatively, you can define a Builder to help in constructing the objects.

What if a Java Bean is needed in a specific part of my code?

That design may work well in most places these classes are used except at specific places where, because for example a framework needs it, you must use Java beans. When that happens, instead of using beans everywhere, you can define a bean that contains the same data with added constructor and method to take care of data conversion. For example...

class UserBean {
    private String name;
    private String address;

    UserBean() {
    }
    UserBean(User user) {
        this.name = user.getName();
        this.address = user.getAddress();
    }

    void setName(String name) {
        this.name = name;
    }
    String getName() {
        return name;
    }

    void setAddress(String address) {
        this.address = address;
    }
    String getAddress() {
        return address;
    }

    User toUser() {
        User ret = new User(name);
        ret.setAddress(address);
        return ret;
    }
}

That way it is easy to have a bean from the non-bean instance and vice-versa.

Conclusion

As you can see the problem you have can be solved while keeping its initial signature. It is possible that going this way results in more code to be written for a similar result (though I wouldn't vouch for it, since passing a necessarily valid object around means less validation needed by the code using it).

I have tried to keep it short (ahem...) and to use here a design close to your initial one (beans). However, it is more for the sake of the explanation rather than because I consider that the best design. With all the hassle of maybe needing builders and additional bean classes, I would tend to go the extra mile and make the class being passed around immutable. That is how I have designed a recent, short project and it turned out to work particularly well.

  • After some thinking - this would give a lot of similar classes (bean + builder), and the signature of the service remains the same (bean), right? I love builders but not sure if I would use it here. – igor Dec 5 '13 at 9:02
  • The signature of the service does not remain the same in that the User object passed to it is not a bean anymore (since the bean can be in what the service considers to be an illegal state). It is true though that it makes more code to write. Plus that design does not address all the problems that can happen in non-trivial code (say some states are valid in a given context but invalid in another; to have to write what is basically the same class can be cumbersome). But with few simple classes, and careful copy/paste operations, I have found that it works decently. – KevinLH Dec 5 '13 at 9:14
0

It sounds like you're creating a library for others to use. If it were me, I would pass in a user object partially populated with the known values, and my first step in creating a user would be to validate the input to the method. If required fields are missing, I would throw a MissingParameter exception (that you invent) at run time.

Even if you find a way to compile-time verify the user of your library is passing in values, you still don't know that they are non-empty values. You'll still have to throw an exception for invalid required parameters. Hit two birds with one stone.

  • Its not a library, its a service layer of an webapp. Validation make sense. – igor Dec 4 '13 at 12:26
  • Even trickier - GWT? That's the only case where a thrown exception would be helpful. Maybe add more detail on who is using your webapp service layer? – Kieveli Dec 4 '13 at 12:29
  • It used by front end (mainly from JSP) and from 3rd party (service api is exposed as remote services) – igor Dec 4 '13 at 21:17
0

Use compile time annotations and then use the annotation processing tools to check the mandatory parameters at compile time itself. You need 2 annotations, Mandatory and Optional.

Annotate the bean fields properly and you should be able to achieve it.

Ref Link: http://www.javalobby.org/java/forums/t17876.html

  • This might be tricky. Mandatory flag depends on method it is using. For example, on some place having nickname set is mandatory. – igor Dec 4 '13 at 21:03
  • This looks like @Null and @Nullable annotations in IntelliJ, but this idea can be extended to eg @Contract annotation on service method (like in new idea). – igor Dec 5 '13 at 9:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.