On Wed, Jul 9, 2008 at 9:40 PM, Henry Ware <
henryware@...> wrote:
> On Wed, Jul 9, 2008 at 6:26 PM, Rafael de F. Ferreira
> <
rafael@...> wrote:
>> Also, I was wondering if there is any way to remove the boilerplate
>> from the code. It becomes apparent when looking at the version with
>> abstract members (
http://snippets.dzone.com/posts/show/5741).
>>
>> Leaving aside the phantom types, it is easy to define a BuilderPart
>> class to encapsulate each field (a thin wrapper over Option[] that
>> returns the builder for method chaining); the price is a little
>> mutable state. But when the types are added to the picture, I can't
>> think of a way to avoid threading them all in each builder method.
>> Moreover, when threading the types, I couldn't find a way to avoid
>> threading the values as well.
>
> The boilerplate for the values in the anonymous ScotchBuilders can be
> abstracted into a common parent. I also don't see anyway to avoid repeating
> the types.
>
> class DefaultBuilder[a,b,c](default:ScotchBuilder) extends ScotchBuilder {
> protected[BuilderPattern] val theBrand:Option[String] = default.theBrand;
> protected[BuilderPattern] val theMode:Option[Preparation] = default.theMode;
> protected[BuilderPattern] val theDoubleStatus:Option[Boolean] =
> default.theDoubleStatus;
> protected[BuilderPattern] val theGlass:Option[Glass] = default.theGlass;
> type HAS_BRAND = a;
> type HAS_MODE = b;
> type HAS_DOUBLE_STATUS = c;
> }
>
> def withBrand(b:String) = new
> DefaultBuilder[TRUE,self.HAS_MODE,self.HAS_DOUBLE_STATUS](self) {
> override protected[BuilderPattern] val theBrand:Option[String] = Some(b);
> }
>
> def withMode(m:Mode) = new
> DefaultBuilder[self.HAS_BRAND,TRUE,self.HAS_DOUBLE_STATUS](self) {
> override protected[BuilderPattern] val theMode:Option[Preparation] = Some(m);
> }
>
That's cool. I hadn't thought of that.
> On the other hand, while the Phantom Types are very cool, going further down
> the abstract member path might be more practical--- at least for the
> required fields:
>
> case class OrderOfScotch (val brand:String, val mode:Preparation,
> val glass:Option[Glass], val double:Boolean);
> abstract case class ScotchBuilder {
> self :ScotchBuilder =>
> val theBrand:String;
> val theMode:Preparation;
> val theGlass:Option[Glass]=None;
> val double:Boolean;
>
> // optional fields can be like before
> def withGlass(g:Glass)=new ScotchBuilder{
> val theBrand=self.theBrand;
> val theMode=self.theMode;
> val double=self.double;
> override val theGlass=Some(g);
> }
>
> def build=new OrderOfScotch(theBrand,theMode,theGlass,double);
> }
>
> def main(argv:Array[String]){
> val b=new ScotchBuilder { val theBrand="Glen Ordinal"; val
> theMode=Neat; val double=false; }
> println(b);
> println(b.build);
> }
In this case we probably should do away with the builder entirely and
define the abstract members directly on the class being created
(OrderOfScotch in the example).
>
> -Henry Ware
>
--
Rafael de F. Ferreira.
http://www.rafaelferreira.net/