Declarative Design
A
SSERTIONS
can lead to much better designs, even with our relatively informal way of testing
them. But there can be no real guarantees in handwritten software. To name just one way of
evading
ASSERTIONS
, code could have additional side effects that were not specifically excluded. No
matter how
MODEL-DRIVEN
our design is, we still end up writing procedures to produce the effect of
the conceptual interactions. And we spend so much of our time writing boilerplate code that
doesn't really add any meaning
or
behavior. This is tedious and fraught with error, and the bulk of
it obscures the meaning of our model. (Some languages are better than others, but all require us
to do a lot of grunt work.) I
NTENTION-REVEALING INTERFACES
and the other patterns in this chapter
help, but they can never give conventional object-oriented programs formal rigor.
These are some of the motivations behind
declarative design
. This term means many things to
many people, but usually it indicates a way to write a program, or some part of a program, as a
kind of executable specification. A very precise description of properties actually controls the
software. In its various forms, this could be done through a reflection mechanism or at compile
time through code generation (producing conventional code automatically, based on the
declaration). This approach allows another developer to take the declaration at face value. It is an
absolute guarantee.
Generating a running program from a declaration of model properties is a kind of Holy Grail of
MODEL-DRIVEN DESIGN
, but it does have its pitfalls in practice. For example, here are just two
particular problems I've encountered more than once.
A declaration language not expressive enough to do everything needed, but a framework
that makes it very difficult to extend the software beyond the automated portion
Code-generation techniques that cripple the iterative cycle by merging generated code into
handwritten code in a way that makes regeneration very destructive
The unintended consequence of many attempts at declarative design is the dumbing-down of the
model and application, as developers, trapped by the limitations of the framework, enact design
triage in order to get
something
delivered.
Rule-based programming with an inference engine and a rule base is another promising approach
to declarative design. Unfortunately, subtle issues can undermine this intention.
Although a rules-based program is declarative in principle, most systems have "control predicates"
that were added to allow performance tuning. This control code introduces side effects, so that the
behavior is no longer dictated completely by the declared rules. Adding, removing, or reordering
the rules can cause unexpected, incorrect results. Therefore, a logic programmer has to be careful
to keep the effect of code obvious, just as an object programmer does.
Many declarative approaches can be corrupted if the developers bypass them intentionally or
unintentionally. This is likely when the system is difficult to use or overly restrictive. Everyone has
to follow the rules of the framework in order to get the benefits of a declarative program.
The greatest value I've seen delivered has been when a narrowly scoped framework automates a
particularly tedious and error-prone aspect of the design, such as persistence and object-relational
mapping. The best of these unburden developers of drudge work while leaving them complete
freedom to design.
Do'stlaringiz bilan baham: |