'. '

ChameleonBuilder

From APIDesign

Revision as of 08:39, 16 June 2016 by JaroslavTulach (Talk | contribs)
Jump to: navigation, search

Very nice attribute of a Builder pattern is the fact that it allows the users to associate the attributes one by one and doesn't force them to do so in a predefined order. Possibly it allows the users to skip definition of some of the attributes and use defaults. However what can one do if certain attribute is essential - e.g. needs to be specified?

Factory Method

Of course, one can use a factory method - e.g. let the builder be created only with necessary attributes. However this has the typical drawbacks to evolution of factory methods - the order of parameters is important, needs to be remembered and potentially number of overloaded methods grows. We used to have fromText method in Source class. One would use it as:

Source src = Source.fromText("my.js", "my.js");

Now, can you guess what of the parameters is name for the Source and which of them is the content? Hardly, I don't remember the order either. Now imagine we add also a MIME type:

Source src = Source.fromText("my.js", "my.js", "text/javascript");

Three subsequent String arguments are really hard to use properly. Let's try to use the builder pattern, but make it unfinished first.

Unspecified Return Type

Let's signal to the user of your builder that it is not yet properly configured by returning wrong type from the build() method. The class could look like:

public final class Builder<R> {
  public R build() {
    // construct Source
  }
 
  public static Builder<Void> newFromText(String text) {
    // new one
  }
 
  public Builder<R> name(String name) {
    // assign the name
    return this;
  }
 
  public Builder<Source> mimeType(String mime) {
    // assign the name
    return (Builder<Source>)this;
  }
}

We parametrize the Builder with R - the return type of the build() method. Initially, when it is created the parameter is set to be Void - e.g. non-existing. One can call various non-essential configuration methods that don't change the state (like the name one) - those still return the same builder with the same type. If you try to use it as in following example, you'll get a compile time error:

Source src = Builder.newFromText("function hello() { print 'Hello'; }").
  name("hello.js").
  build(); // returns Void not Source!
Once the essential attribute mimeType is set, the same this is returned, but the system re-casts it to Builder

Invalid language.

You need to specify a language like this: <source lang="html">...</source>

Supported languages for syntax highlighting:

abap, actionscript, actionscript3, ada, apache, applescript, asm, asp, autoit, bash, basic4gl, blitzbasic, bnf, c, c_mac, caddcl, cadlisp, cfdg, cfm, cpp, cpp-qt, csharp, css, d, delphi, diff, div, dos, dot, eiffel, fortran, freebasic, genero, gettext, glsl, gml, groovy, haskell, html4strict, idl, ini, inno, io, java, java5, javascript, kixtart, latex, lisp, lotusformulas, lotusscript, lua, m68k, matlab, mirc, mpasm, mxml, mysql, nsis, objc, ocaml, ocaml-brief, oobas, oracle8, pascal, per, perl, php, php-brief, plsql, python, qbasic, rails, reg, robots, ruby, sas, scala, scheme, sdlbasic, smalltalk, smarty, sql, tcl, text, thinbasic, tsql, vb, vbnet, verilog, vhdl, visualfoxpro, winbatch, xml, xpp, z80

The type information is really compile time thing - e.g. it gets erased during execution. Thus the performance of this APIDesignPattern is the same of plain builder - but rather on relying on runtime exceptions saying something is missing - it co-operates with Javac to give the users early edit time/compile time error indications.

Personal tools
buy