
On 12.08.2014 05:31, ok@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.