6

So, just as an example, let's say we have an abstract class called Question, that question contains a lot of strings, one for the question itself, one for the answer and two responses posted to the user, if he got the question right / wrong.

public abstract class Question {

    private final String question;
    private final String answer;
    private final String answerCorrect;
    private final String answerWrong;

}

My question basically is, what would be a common way to initialize all of the strings? So far I've made up 2 versions on how to do it, they have their up- and downsides and I wanted to know, if there was some kind of "best coding practice" for this.


Version A
Initialize everything in the constructor.

public abstract class Question {

    //...

    public Question(String question, String answer, String answerCorrect, String answerWrong) {

        this.question = question;
        this.answer = answer;
        this.answerCorrect = answerCorrect;
        this.answerWrong = answerWrong;

    }
}

This seems pretty convenient, the only problem I have with this is that users will not be sure, in which order the strings have to be.

public class ExampleClass extends Question {

    public ExampleClass() {
        super("I think, that's the answer", "and that's the question", "answer wrong?", "answer right?");
    }

}

Version B
Don't initialize instantly and wait for the user to do it.

public abstract class Question {

    //...

    public Question() {

        this.question = "";
        this.answer = "";
        this.answerCorrect = "";
        this.answerWrong = "";

    }

    public void setQuestion(String question) {
        this.question = question;
    }

    //...
}

This makes it easier to initialize variables, but the Strings can't be final anymore and it's not guaranteed that the user will initialize all of them.


I've also thought about letting the child-class implement abstract methods that are called in the constructor of Question to initialize all the strings and to keep them final, but that version seemed a little too strange to me.

Are there other / better ways to do it? What version should I prefer?
Thanks in advance for your support.

felix fritz
  • 462
  • 2
  • 5
  • 21
  • In this special case, I'd vote for Version A. Why? Because you want to create immutables. Their values won't change after creation. – Fildor Oct 15 '13 at 15:57
  • 2
    Your code won't compile if your attributes are `final` and you put a `setter` – nachokk Oct 15 '13 at 15:59
  • 3
    @nachokk I mentioned it: "(...) the strings can't be final anymore(...)" – felix fritz Oct 15 '13 at 16:00
  • @felixfritz "This makes it easier to initialize variables" why is that? – Fildor Oct 15 '13 at 16:01
  • 2
    `the only problem I have with this is that users will not be sure, in which order the strings have to be`. Users don't call the constructor, developers do. To ensure the right order you should write a comment. This should not hinder you to choose version A. – André Stannek Oct 15 '13 at 16:01
  • 1
    Considering that most IDE's have popups that give you the parameters of constructors, the disadvantage for the first is N/A (also note that this 'disadvantage' applies to just about every method and constructor ever written). – Bernhard Barker Oct 15 '13 at 16:02
  • I'd like to mention the factory pattern. But I would prefer Version A over a factory, here. – Fildor Oct 15 '13 at 16:03
  • @Fildor I just thought that reading methods like "setQuestion" makes it easier to figure out, how to initialize the question. – felix fritz Oct 15 '13 at 16:04
  • @Fildor So you would use the constructor-version for all final objects? E.g. if there's something like a "points"-integer that can be changed, make a seperate method for it? – felix fritz Oct 15 '13 at 16:05
  • 1
    If you have an additional **non-final** member "points", then use the CTOR to give it a reasonable "undefined" value. Then add a setter for it. In the docs of the getter state what that "undefined" value is. Like "-1 if there have no points been set, yet". – Fildor Oct 15 '13 at 16:12

3 Answers3

1

Version A is the way to go. You're right, though, if you do not tell your users (the other developers I'm assuming) which parameter is which, there is no way for them to know where to type what.

This is where Javadoc comes in handy.

Here's an example:

/**
 * Create a new instance of Question given the following parameters:
 * 
 * @param  question This is the question
 * @param  answer This is the answer
 * @param  answerCorrect Whenever someone guesses correct, print this
 * @param  answerWrong Whenever someone guesses wrong, print this
 */
public Question(String question, String answer, String answerCorrect, String answerWrong) {

    this.question = question;
    this.answer = answer;
    this.answerCorrect = answerCorrect;
    this.answerWrong = answerWrong;

}
Birb
  • 866
  • 10
  • 23
  • This is where "naming your parameters properly" comes in handy ;) – Fildor Oct 15 '13 at 16:04
  • 2
    Just saying form own experience: State what happens if one of the params is null - or generally speaking, what you expect them to be. And always add units if a param can have one. (Like "int time" - what is it? Seconds? Days? Can it be negative? ) – Fildor Oct 15 '13 at 16:09
  • @Fildor: I wanted to use the same names as `felix fritz` did in his question. Is the comment intended to be an addition to my answer? If so: I agree. I personally prefer names like `listCarBrands` or `btnCreateNewEntry` to describe a list of car brands and a button which purpose is to create a new entry respectively. This way I can quickly get an overview of all my buttons, for example, if I type `btn` and hit Ctrl+Space (auto-complete suggestions) in my preferred IDE. – Birb Oct 15 '13 at 16:14
  • Yes, it was meant to be an addition :) – Fildor Oct 16 '13 at 05:54
1

This might be overkill, but I believe you could use a builder here...

public class Question
{
    private final String question;
    private final String answer;
    private final String answerCorrect;
    private final String answerWrong;

    Question(QuestionBuilder builder) {
        this.question = builder.question;
        this.answer = builder.answer;
        this.answerCorrect = builder.answerCorrect;
        this.answerWrong = builder.answerWrong;
    }

    // public getters omitted to shorten answer

    @Override
    public String toString(){
        return String.format("question: '%s', answer: '%s', answerCorrect: '%s', answerWrong: '%s'", question, answer, answerCorrect, answerWrong);
    }

    public static void main(String[] args) {
        QuestionBuilder qb = new QuestionBuilder();
        qb = qb.question("This is the question").answer("This is the answer").answerCorrect("Correct answer").answerWrong("Wrong Answer");
        Question question = new Question(qb);
        System.out.println(question);
    }


    public static class QuestionBuilder{
        private String question;
        private String answer;
        private String answerCorrect;
        private String answerWrong;

        public QuestionBuilder question(String question) {
            this.question = question;
            return this;
        }

        public QuestionBuilder answer(String answer) {
            this.answer = answer;
            return this;
        }

        public QuestionBuilder answerCorrect(String answerCorrect) {
            this.answerCorrect = answerCorrect;
            return this;
        }

        public QuestionBuilder answerWrong(String answerWrong) {
            this.answerWrong = answerWrong;
            return this;
        }
    }
}

Gives the output

question: 'This is the question', answer: 'This is the answer', answerCorrect: 'Correct answer', answerWrong: 'Wrong Answer'

Note: I realize the original question was in reference to an abstract class. I used a concrete class so I could give a working example, although the solution can be adapted for use with an abstract class.

Jason Braucht
  • 2,358
  • 19
  • 31
  • @Dukeling Agreed, the Builder does not force the user to set each of the values, however this is also true of OP's 'version B' solution. Unlike the 'version B' solution, I believe the Builder gives the immutability that OP desired. – Jason Braucht Oct 15 '13 at 16:32
  • I haven't seen something like this before, this is cool! And I think, if the developer decides not to initialize everything, that would be the same as setting the string to null in version A, right? I like the idea. – felix fritz Oct 15 '13 at 18:38
  • @felixfritz That's correct. If, for example, you only called the `.answer(...)` method, the remaining elements would be `null` and the resulting output would be `question: 'This is the question', answer: 'null', answerCorrect: 'null', answerWrong: 'null'` The example I provided should compile and run so you can easily play with it in a debugger to see how it would work in various different situations. – Jason Braucht Oct 15 '13 at 18:47
1

Instead of thinking of the attributes (such as question) as just variables, think of the restrictions on their values that must be obeyed for the classes to behave correctly. Can they be null? Can they be empty? Now design your methods and the constructor so it is impossible for those restrictions to be broken. You might find that the only way you can do this is to set initial values in the constructor (your version A). You might have to add pre-condition checks to your constructor and setter methods, which check the values given a throw a suitable exception (NullPointerException or IllegalArgumentException) if the values passed to them would result in the restrictions being broken.

Also, consider whether it really makes sense to change the value of an attribute after the object is constructed. If not, then the attribute should not a setter, making your version B impossible.

Raedwald
  • 46,613
  • 43
  • 151
  • 237
  • If a variable cannot be null (and is final), should I throw a NullPointerException right away? – felix fritz Oct 16 '13 at 08:44
  • @felixfritz Yes; I've amended my answer to clarify this. – Raedwald Oct 16 '13 at 12:00
  • This approach either makes it *impossible* for the attributes to have invalid values, or causes code that tries to have invalid values fail fast: http://stackoverflow.com/questions/2807241/what-does-the-expression-fail-early-mean-and-when-would-you-want-to-do-so – Raedwald Oct 16 '13 at 12:06