Hi, thanks for the interest. Here is the example builder I used in its
entirety:
// The builder DSL
public interface SnpIdChromBothAllelesOnly {
public SnpDetail withOddsRatios(Map<Genotype, Double>
oddsRatios);
}
public interface SnpIdChromRiskOnly {
public SnpIdChromBothAllelesOnly withNonRiskAllele(char
nonRisk);
}
public interface SnpIdChromOnly {
public SnpIdChromRiskOnly withRiskAllele(char risk);
}
public interface SnpIdOnly {
public SnpIdChromOnly withChromosome(String chrom);
}
public static SnpIdOnly buildForId(String rsId) {
return (new Builder()).newFor(rsId);
}
public static class Builder implements SnpIdOnly, SnpIdChromOnly,
SnpIdChromRiskOnly, SnpIdChromBothAllelesOnly {
private String rsId;
private String chrom;
private char riskAllele;
private char nonRiskAllele;
public SnpIdOnly newFor(String rsId) {
this.rsId = rsId;
return this;
}
public SnpIdChromOnly withChromosome(String chrom) {
this.chrom = chrom;
return this;
}
public SnpIdChromRiskOnly withRiskAllele(char risk) {
this.riskAllele = risk;
return this;
}
public SnpIdChromBothAllelesOnly withNonRiskAllele(char
nonRisk) {
this.nonRiskAllele = nonRisk;
return this;
}
public SnpDetail withOddsRatios(Map<Genotype, Double>
oddsRatios) {
return new SnpDetail(rsId, chrom, riskAllele,
nonRiskAllele, oddsRatios);
}
}
It is, as mentioned previously, a wall of boilerplate (so much of Java
is), but this is still a neat pattern worth mentioning. In particular,
it uses different interfaces, each with a single method, returned from
each builder method, to lead you through the creation of the object.
This idea could be extended, for example if you needed either one
thing or another but not both, at some point you would return the
builder cast as an interface that offered only two methods, each of
which returning a different interface view of the builder, etc.
The technique is overkill in this case, but if you have a DSL that is
intended to be used in a very controlled way and also is heavily used,
it starts to make more sense, since you can enforce object creation in
a way that you can only get the real object back once you have
satisfied all of the dependencies.
The point of the talk was to mention that default and named parameters
largely eliminate the need for this kind of builder (although if you
really want to control everything about it, this same technique can be
used in Scala and other languages too).
Cheers
Dick