[Haskell-cafe] The Good, the Bad and the GUI

ok at cs.otago.ac.nz ok at cs.otago.ac.nz
Wed Aug 13 01:27:03 UTC 2014


> On 12.08.2014 05:31, ok at cs.otago.ac.nz wrote:

>> Even in Visual Basic, if an object is "constructed" via a lengthy
>> sequence of steps, it is good design to distinguish between two
>> different things": a fully constructed Foo object and a FooBuilder
>> object.  Sometimes they need to be the same object, but there really
>> do need to be two *interfaces*.  Once past the construction phase,
>> you want to KNOW that the object is fully constructed, and there are
>> things the constructor might do that you DON'T want other objects to
>> do.
>
> Take a VAT Invoice as an example. You will have:
>
> Invoice, InvoiceBuilder,
> InvoiceLineItem, InvoiceLineItemBuilder,
> InvoiceCustomer, InvoiceCustomerBuilder,
> InvoiceSummary, (no Builder, as this is calculated)
> (many, many more classes in a realistic system)
>
> Now, where the rather complex validation belongs?

Follow the GRASP patterns.  Give the task to
the type/class that has enough information to do the job.
Remember, the goal is to not create invalid objects.
It makes no sense to have an Invoice validate itself,
because the whole aim is for invalid Invoices NEVER TO
EXIST.

Some validation is fairly shallow.  If you can check a
field as soon as it is typed, it might make sense to do
so.  (Or it might not.  That's a user interface design
question.)  Some validation is deeper, and can only be
done once you have full or nearly full information.
The obvious place to put deeper validation is
InvoiceBuilder; that, after all, is one of the principal
reasons to *have* an InvoiceBuilder.

By the way, I deny that you would have a delirious menagerie
of _Builder classes, as you seem to be implying.  There needs
to be something *separate* from an InvoiceLineItem that can
accumulate the information needed to build one, and can
validate that information before the thing is built, but an
InvoiceBuilder can take responsibility for building all the
components of an Invoice.

So which types/classes deserve their own _Builder and which
do not?
- First, the _Builder pattern applies for things that
  have to be built up *incrementally* and may be in
  incomplete or inconsistent states while they are being
  built.  If you can construct something all at once in a
  fully valid state, you should, and don't need a Builder.
  If you can build something in a complete valid state and
  then you want to revise it as part of normal operation,
  again you don't need a builder.
- Second, some things are "free-standing" and some only
  make sense as part of other things.  I'm not sure exactly
  what you have in mind by an InvoiceSummary, but let's
  assume that an InvoiceSummary belongs to one and only one
  Invoice and that it doesn't really make sense for an
  InvoiceSummary to exist on its own.  Then it can share
  its owner's Builder.

In OO terms, the pattern goes something like this:
  program creates an InvoiceBuilder x
  filling out and revising the form revises x.
  there is a "commit" button; when x processes that,
  it creates a new Invoice y.
  y fills itself in, calling back to x to get the
  information it needs.  y may create its dependents;
  in fact, since a dependent should be *born* knowing
  what it depends on, the dependents *can't* be created
  until y exists.  So it probably should be y that does
  the creation.
  Eventually y finishes initialising itself, and x is
  now able to make y visible elsewhere.

The term "Builder" may be a bit misleading: the job of a
Builder is to *accumulate the information needed for
complete construction*; the actual work of construction
may be done by the new object that is eventually created,
including the creation of its parts.

The key thing is that *incompletely or inconsistently objects
are never exposed*; the first time a thing is mentionable, it
is right.

> Optional / mandatory
> requirements, lengths, ranges, regexps, control sums, field
> interdependencies, autocompletes, server sent notifications? Where to
> put all of this? To regular classes, to builder classes, or to both?

There are two and only two honest answers to questions like that.
(A) This sounds like carping, not like a real question.
(B) "It depends."  Put it wherever it works best.

Invoices are actually a nice example.  I had a student once who
told me a bit about SAP.  (He made a lot more money as a SAP
expert than I make as a lecturer.)  As I understand it, one of the
key reasons to use SAP is that once a record has gone into the
data base, it cannot be changed or deleted.  It can be marked as
not to be used any more, but it can't go away.  It can be marked
as having been superseded by a corrected version, but it can't be
changed.  So if you want to create an invoice in SAP, you really
really want to use the Builder pattern (at least in spirit).
Once the various tuples that constitute the Invoice have been
inserted in the data base, changing them is very far from being
normal operation.  For SAP, at least, I can say "put all those
things anywhere you like EXCEPT in the (persistent) Invoice."

And of course in the real world, if you are old enough you have
had the experience of interacting with an InvoiceBuilder.
You the customer, in the act of buying carpet, are sitting
on one side of the desk.  On the desk is the Invoice.  On the
other side of the desk is the InvoiceBuilder, otherwise known as
"a salesman", who gathers information from you, and fills in the
invoice.  You don't interact with the Invoice.  It just sits there.
Once the Invoice leaves the desk, the information on it normally
should not change.  If it is to change, a corrected duplicate is
made, the old one crossed out, and the new one stapled to it.
Doing this might even require the customer's signature on the new one.
The rest of the business only deals with complete Invoices.

Of course, it _all_ depends.  If you are whipping up a prototype
and speed of development (so you can get to answer some high-risk
question soon) is a priority, cut all the corners you think
appropriate.






More information about the Haskell-Cafe mailing list