Insurance premium calculator exercise
A good OOP design is open for extension and closed for modification.
The single-method implementation is very far from that.

I think the purpose of the exercise is to be able to build a premium calculator from parts,
in a way that you can easily modify the parts that are likely to change.
What are the parts?


The base premium
Calculate percentage modifier based on conditions


There is a list of conditions, with associated percentage increase or decrease



A percentage modifier part is essentially a function that takes a Person and returns a percentage.
You could provide such parts to make it easy to build a calculator, for example:

interface PremiumPercentCalculator extends Function&lt;Person, Integer&gt; {}

PremiumPercentCalculator male(int percent) {
    return person -&gt; person.male ? percent : 0;
}

PremiumPercentCalculator preConditions(int percent) {
    return person -&gt; person.preConditions().size() * percent;
}

PremiumPercentCalculator goodHabits(int percent) {
    return person -&gt; person.habits().stream().filter(Habit::isGood).mapToInt(h -&gt; percent).sum();
}

PremiumPercentCalculator badHabits(int percent) {
    return person -&gt; person.habits().stream().filter(h -&gt; !h.isGood()).mapToInt(h -&gt; percent).sum();
}


Then, using the builder pattern, you could build a calculator from parts like this:

float premium = new Calculator(person, 5000)
    .add(p -&gt; getPremiumPercentByAge(p))
    .add(male(2))
    .add(preConditions(1))
    .add(badHabits(3))
    .add(goodHabits(-3))
    .calculate();


Imagine that there is a UI for an insurance agent,
where he can drag and drop widgets that correspond to condition types like "is male", "pre-conditions", and so on, each with an option to modify the percentage to apply.
Such a UI could be easily built from the parts we provided,
and new parts can be easily added by adding more PremiumPercentCalculator implementations.

Notice that person and the base premium 5000 are constructor parameters,
as they are absolutely essential for the calculator,
so it made sense to make them required up front,
so that the rest of the implementation can count on them to exist without further validation.

You might notice that badHabits and goodHabits iterate over the collection of habits twice.
This might very well be negligible.
But if not, it's easy enough to fix,
by adding another part:

PremiumPercentCalculator habits(int goodPercent, int badPercent) {
    return person -&gt; person.habits().stream().mapToInt(h -&gt; h.isGood() ? goodPercent : badPercent).sum();
}


Admittedly getPremiumPercentByAge is not so easy to make extensible,
this part can certainly be improved,
but I think that was not the most important part of the interview.

Note that although I used lambdas for compact expressions,
the concept doesn't require it,
and although the solution will be more verbose with classes fully written out,
that won't diminish its benefits.