> I briefly ran through the two articles you pointed out. While long,
> I do not think they have anything new to contribute to software
> development. It is well known that good software is developed in
> layers, and that layers on the bottom should be completely unaware of
> layers above them that use them.
We think alike here.
>Whether it is 2 layers as in
> Client-Server or n-layers as in the buzzword "n-layered architecture"
> does not really change the fundamental concept.
Well - thanks for your kind words Tim :)! Actually I disagree with
you, and also think you may be confusing some different concepts - not
that that is any great sin. Layering is still the subject of a of
confusion out there - viz your own comments.
Some examples:
- Layers vs. Tiers. this is a common confusion. I take the view the
layers are (concptual) placeholders for packages (or whatever unit of
modularity your programming language supports). Tiers are about
software deployment. IMHO Client/Server is a two-"tier" architecture
rather than a two-layer architecture. Note the term is "three-tier
architecture" not "three layer architecture!"
Putting it another way - you deploy executables on tiers, and packages
(or libraries - their binary equivalent) in layers. Layering is about
compile-time relationships between packages, tiers are about run-time
deployment of executable code. Different forces, different objectives,
pertty much different full stop.
Of course, if you subscribe to the view (as some do) that a layer is a
(defined but arbitrary) peer grouping of arbitrary software "units"
(it gets difficult - you have to be quite vague in your description to
make this work) - then you could call client server a two layer
architecture. I just don't think this helps much - as one is
overloading the term layers and causing the confusion.
- Compile time versus run-time view.
Some people (who I present to) say - but layering shows a run-time
(method call) view. This is simply not the case, and it is not
possible to make sense of layering diagrams as a run-time point of
view - for the pure and simple reason that run-time control goes up
and down the layers (at least as presented in the model I promote).
There's one particular book I can think of, which shows -
--------------------------
applications
--------------------------
operating system
--------------------------
device drivers
--------------------------
hardware
----------------------------
- in the layering diagram. The point here is that the author has put
device drivers below operating system - presumably on the basis that
the operating system calls the device driver. In compile time terms,
device drivers are **above** the operating system - a device driver
class (for example) inherits the operating system's device driver
interface - e.g.
interface devicedriver {
open()
close()
read()
write()
}
so the device driver is above the operating system from a layering perspective.
- Layers versus "components" / "packages"
Another common confusion, imho, is to confuse packages (or components,
a term for a binary version of a package or module) with layers. You
see this in discussion of data-access layers. When people talk about
the data access "layer" they are often talking about a single
object/relational persistence mapping component (e.g. hibernate).
When I discuss layers, I view them as a placeholder for zero-to-many
packages/components - not just one. I think a layer like
"infrastructure" (which in my terms contains packages of general
purpose technical utility with *no* domain specific dependencies) is
an infinately more useful concept than "data-access layer" - there's a
whole mass of stuff which can have domain factored out and be
re-usable across multiple projects.
Coming to your point about adding value to the software development
community - I discuss the above points in the two papers - but also
identify the following points which I haven't seen documented
elsewhere (though feel free to point them out if you know of them).
- Interface includes not only application specific user-interface, but
also application specific external system interface (asynchronous) and
also application specific timer related code.
The reason this is important is that the layer below (Application)
exposes a set of services that can be re-used by all three types of
interface package. So less repeating yourself.
- Interface contains "application specific" user interface code -
general purpose stuff - like Swing libraries of MFC or .NET Gui
classes are Platform. This is often a cause of confusion.
- Strict layering (you can only use the layer below you) doesn't work
without having to write loads of redundant code.
- the Domain layer should hide the details of the persistence. In
other words, the layers above the domain layer (application and
interface) are presented with a conceptual "in-memory" model of domain
objects. The domain layer has to sort out getting them really in
memory - but transparently.
Finally, the ARM presents a model of five layers (and importantly a
set of rules) that can be used on any projects (completely generic) -
layering in the abstract - as you describe it above - doesn't actually
help you decide how to horizontally chop up your application. I've
found it very useful to get common vocabulary and understanding of
what code to put where on large projects. It's fundamentally a
teaching aid - and works well to help less experienced software
developers.
Btw: W9.html was written and published in the mid-to-late 90s.
Hope I haven't bored you too much! :)!
> Also, your use of Keys in one of the examples actually shows that
> high-level OO design is moving closer and closer to the conceptual
> model that relational database development produces.
See my discussion of spec. models in another email. We probably think
alike here - but perhaps you could expand on your point a little?
> Of the purported benefits of OO - encapsulation, abstraction,
> polymprphism and inheritance - good procedural languages embody three
> of them (encapsulation, abstraction and polymorphism.)
Do procedural languages support encapsulation and polymorphism? You
can do pseudo-encapsulation in say C (e.g. as in the stdio libraries
and their use of FILE *) - but it isn't enforced by the compiler.
Regarding polymorphism, I had the unfortunate experience of having to
work with some Unix yellow-pages code some years ago. This used a form
of pseudo-polymorphism (explicityly storing pointers to functions) -
but hell it was a pain to debug!
> As far as
> inheritance is concerned, it has very limited uses and it is generally
> accepted that composition is preferable to inheritance. Given that, it
> seems that OO does not offer as significant benefits as claimed by its
> vociferous boosters.
Inhertiance can be useful in not-repeating-yourself. But your point
here isn't too far off the mark. Hey Tim, in the early days we all
went on about OO as the silver bullet to end all of man's problems.
But we exagerated a little - or perhaps didn't see all the problems.
Havingt said that I'd stilll rather user C++ than C, or Java than
Pascal, or Ruby than any of them :)!
> PS: If I do not see any improvement in the speed with which articles
> are posted, I'll stop posting here, And then the contributors to
> ObjectiveView Magazine can talk to each other happily, with nary a
> dissenting opinion.
That's up to you.
Mark Collins-Cope
Ratio - Training, Consultancy and Development
--------------------------------------------------------------------------
+44 (0)20 8579 7900 or :+44 (0)777 163 6882
Author: "Agile Development with Iconix Process"
Editor: www.ratio.co.uk/objectiveview.html
---------------------------------------------------------------------------
recursion is - of course - a form of magic
I define a layer of software in very general terms. A tier itself is
a layer of some sort. The fact that tiers are deployable does not
matter much. The key concept, no matter what terminology you use, is
that layers are structured such that the "lower" level does not know
about the layer that uses it.
For example, if we write a generic error handler, then it must never
know what program/module/class calls/uses it. The same applies for
message handlers and utility classes. These three could form the nase
layer for many applications.
As for procedural languages not having encapsulation, I can cite
PL/SQL, Oracle's procedural lnaguage for SQL. (The following argument
also apply to any other language that has the concept of packages and
public and private variables and methods.)
PL/SQL modules are typically packages, which can have a mixture of
procedures and functions. For simplicity, let's say procedures when we
mean either procedures or functions, since it takes nothing away from
the arguments that follow.
PL/SQL packages have the concept of an interface, or package
specification, which is the list of all public variables, types,
methods (procedures and functions) and exceptions. In the body of the
package you can have private versions of these that nobody else can
use. You can encapsulate data structures within packages, either known
(as in records of pre-defined tables) or customized within the package
as types or record types.
As a procedural language it has all the standard constructs: loops, if,
while, etc. It has a very robust and flexible exception mechanism (much
better than Java's checked exceptions, in my opinion.)
Much more important, it is very easy to write code that writes code in
PL/SQL using the dynamic capabilites. Lack of something like an eval
function that could look at a block of code and convert it into a
method to be executed, or directly execute it is a sgnificant handicap
with Java. Pyhton has an eval function, Lisp is of course superb with
its macros, Ruby and Smalltalk and Python have blocks. Anyway, this was
an aside going away from features of OO.
Reuse is easy with PL/SQL since you can easily re-use packaged
procedures. Abstraction is a concept that has nothing to do with OO per
se, but more to do with design. PL/SQL permits overloaded
functions/procedures. So, of the four concepts abstraction,
encapsulation, polymorphism and inheritance, the first three can be
done using PL/SQL. That leaves inheritance. As I've said before, I do
not find inheritance very useful; most of my obnject model uses
composition. In the cases where I use inheritance, it is never more
than two layers deep, most often only a layer deep. I can write PL/SQL
procedures to perform the tasks, and invoke the appropriate overloaded
version. With dynamic code, I do not even have to do if then
evaluations that is found in many Java applications. A judicious use of
arrays and HashMaps lets me do many things in PL/SQL that I want to.
These are the reasons I feel that OO is an over-hyped technology, much
of what it claims to be its sole prerogative, is achievable using
procedural languages. When OO practitioners start touting their
philosophy and denigrating other approaches, it makes me realize that
most of them have only seen bad examples of code in procedural.
Therefore, they set up these strawman examples, demolish them and feel
happy.
Tim