Matt said:
Yann-Erwan Perio wrote:
Sure, why not?
Maintenance costs are probably the best reason for preferring to employ
someone with good understanding of what they are doing. Not necessarily
because they will write code that is easy for all others to understand
but because, in acquiring their knowledge and experience, they are
likely to be in a position to be writing code that addresses the likely
issues from the outset and so creating code that does not need nearly as
much maintenance.
Consider the way in which amateurs and the inexperienced often approach
multi-browser coding. They discover an approach that works with one
browser, and then add branching and variations to accommodate other
browsers they encounter. An example might be this original code from an
externally soured script that I am responsible for maintaining:-
| if(ISIE){
| document.body.onload=load_functionality;
| }else{
| document.body.setAttribute("onload","load_functionality(event);");
| }
(and yes the ISIE test was - var ISIE = document.all ? true : false
The author had discovered that attaching an onload handler to the body
element in IE worked, and then discovered that Mozilla/Netscape/Gecko
was not interested and so trawled about for an alternative that would
work. As this in Intranet code and the spec is to support IE and
Mozilla/Gecko the code above satisfies that, but we all know that it is
twice as complex as it needs to be and extremely vulnerable to minor
revisions and new bugs in newer versions of even those two browser. It
is also code that is not very friendly to its environment, hogging the
onload event, and doing so differently in different browsers. These
things represent potential maintenance costs that would not exist in
code written by a more experienced author.
You will recall that Opera 6 was not a particularly dynamic browser. At
the time of the release of the significantly better, and genuinely
dynamic, Opera 7 much code had been written to exclude Opera 6 just
because it was an Opera browser, and so Opera 7 was also excluded. The
authors/owners of that code, assuming they perceived a desire to take
advantage of the new features of Opera 7, were faced with a cost for
code updating that could only be considered a maintenance cost.
Those of us who had been using feature detection, and particularly an
interest in dynamic DOM standard features, welcomed the arrival of Opera
7 as another dynamic visual browser with which our code would happily
act. I did not have to change a single line of my code at the time to
accommodate the arrival of Opera 7, it all just noticed the availability
of the features it needed and so used them. The total maintenance cost
of accommodating a new, and very different, version of a browser was
zero.
Do you think most of the code written in the world is
written by experts in the languages used? Far from it.
There are few experts. Most code that I've seen is written
by moderately skilled programmers. Increasingly, much code
is written by junior programmers in India or similar places.
Skilled programmers who are experts in the language they are
using are very rare, IMO.
It is in the nature of any skill that is more than trivial than any
population exhibiting that skill will vary in their mastery of that
skill. There will be relative novices alongside experts, and a spectrum
of knowledge and experience in-between. Things have to be that way else
the skills in question would die out as the individuals who posses them
age and die.
This is a reality that lends itself to hierarchical organisation. In a
big OO project there may be a couple of individuals with the skills to
handle the architectural aspects of the software design, a larger group
capable of designing, implementing and testing the main objects in the
design, and a majority who are best employed in writing the code that
uses those objects.
An important aspect of getting such a hierarchy to work for the benefit
of the project is drawing the important distinction between the internal
details of an object implementation and its public interface. The
programmes that use the objects in a system don't need to concern
themselves with the internal details of those objects, they just need to
understand the public interface. Internal complexity is not their
concern, and they are not going to be the people who maintain that code
(though they may acquire the experience to become the people who
maintain that code, but in doing so they would also become capable of
coping with its internal complexities).
Such a hierarchical organisation of skills gives everyone a place, and
reason to improve their skills; to move up the hierarchy (if they want
to). An organisation that says that everything should be written so that
the next trainee through the door can understand and maintain everything
would be throwing away the advantages of having more experienced
programmers, and removing the motivation of the programmers it does have
to improve their skills.
Or would you rather defend that conceptions based on global
functions can be as good as conceptions using the javascript
paradigm to its full?
It can be a complicated issue... I understand (now even more
than previously) how to use closures and other techniques which
are more advanced than most people ever face when touching
javascript. [Note that I do not consider myself an expert in the
js language, but I do consider myself much more experienced than
the average person writing javascript] But in developing code that
will be maintained, implemented, and possibly enhanced and
customized by novice javascript programmers, it seems to me like
a good choice to program in a way that they will comprehend.
The code that I cited above was inexpensively sourced form India. It is
simple in that it does not use any 'advanced' javascript techniques and
is relentlessly procedural, but it is also 4000 lines long, uses 200-odd
global variables to maintain its state and features, for example, a 500
line mouse move event handler.
In principal there is no single aspect of that code that could not be
understood by a relative novice at javascript. In reality the totality
is such a spaghetti mass of indirect interactions that I don't find it
easy to understand, and a novice would be utterly lost.
You cannot blame the Indian programmers for creating this code. They had
a specification and they got code out of the door that satisfied that
specification. The problem was that the authors of the specification
left the code design up to the implementers, and the implementers didn't
have the experience to do anything other than a massively elaborated
procedural implementation.
The result satisfied the specification but is nearly useless to us, as
even minor changes require a massive effort back tracking the spaghetti
to find all the points where they need to be enacted, and the nature of
the existing implementation makes more extreme changes completely
impossible. i.e. it is a maintenance nightmare, squandering all the
saving derived from its external sourcing because the time I spend
working on it is relatively expensive. But it would not help to have a,
theoretically cheaper, relative novice work on it either, as they would
spend significantly longer making the same changes and so cost as much
overall.
Eventually I will get the time to re-write it from scratch, and do the
OO implementation that it should have been from the outset. The result
will be unintelligible to its original author and many other novices,
but it will be objectively simpler, easier to maintain and (most
importantly) possible to extend in the direction that current thinking
looks like it wants to take it.
If I write code that is highly compact and obfuscated by advanced
language techniques, it becomes meaningless to the average javascript
programmer. Then they'll seek out a less-complex solution which they
understand, but may be written by someone at their own skill level
and have problems. Which is better? It's not as obvious of a decision
as some would like to believe, IMO.
This is where the distinction between the internal details of an
implementation and its public interface becomes significant. The
(non-public) web application that I am currently working on is a
windowing GUI inside a browser (in-window pop-ups) with 2000-odd
server-side forms potentially displayed in the windows within the
browser. So the client-side code mostly represents an environment that
is, more or less, used by the server-side forms. And the server-side
forms are written by server-side programmers who have very little (and
in most cases no) understanding of javascript, but that doesn't matter
as they are not using javascript directly they are using templates and
tag libraries. Other, more experienced server-side programmers have
written the templates and tab libraries and they have been using
javascript to interact with the client-side environment, but they also
have minimal understanding of javascript.
My responsibility is to provide the server-side programmers who work on
the templates with interfaces to the client-side environment that are
simple enough for them to understand and use, while hiding the
implementation details from them. Their responsibility is then to
conceal those details (and much else) form the bulk of the individuals
programming the server-side code.
The intention is very deliberately to have a series of black boxes that
do no more than precisely what they are advertised as doing. The
back-end programmers don't care in the slightest how the front-end is
implemented. And those working on the intermediate layer between the
back-end and the front-end don't care that much either, so long as the
front-end can do what they need it to do.
The approach taken to having code executed onload listed above can be
contrasted with the way the rest of the application code achieves the
same. Every page loaded into the application includes a single JS file
that includes the following:-
/**
* A global function named - initializeMe - is created that can be used
* to arrange the execution of an arbitrary number (but probably
* implementation limited to the order of about 1500) of functions from
* the - onload - handler of a web page. The intention being to allow an
* indefinite number of other scripts to use a common interface for
* triggering their onload initialisation functions without having to
* worry about conflicts in the use of the onload handler.
*
* NOTE: This function must be included before any code that attempts
* to use it.
*
* The - initializeMe - function is called with a reference to a
* function object as its first argument, and up to 4 optional
* additional arguments:-
*
* initializeMe(functRef, "idOfElement", "nameOfForm");
*
* - The function reference and any additional arguments are stored in a
* function stack, the base of which is assigned as to window.onload
* handler (or using W3C DOM addEventListener or IE attachEven, if
* available) and when the onload event is triggered the execution of
* the base of the function stack results in the execution of the
* function passed as the first argument, followed by the next function
* in the stack (which executes its function argument, And so on until
* the stack is excused. The execution of functions passed to the -
* initializeMe - function is in the order in which they are passed to
* the - initializeMe - function (first in, first executed).
*
* The function object passed as - funcRef - is called with its first
* argument being the (onload) - event - object and the values
* originally passed to the call to - initializeMe - as the optional 2nd
* to 5th arguments as its 2nd to 5th arguments (in the same order). So,
* in the example call to - initializeMe - above, the function object
* passed by reference as the first argument would have the pattern:-
*
* function(event, idString, nameString){
* ...
* }
*
* - and the two string arguments passed to - initializeMe - would be
* passed on when it was called during the onload event.
*
* @param Function Reference <code>funcRef</code>
* @param Any (Reference or Value) <code>arg1</code> - Optional.
* @param Any (Reference or Value) <code>arg2</code> - Optional.
* @param Any (Reference or Value) <code>arg3</code> - Optional.
* @param Any (Reference or Value) <code>arg4</code> - Optional.
*/
var initializeMe = (function(){
var global = this, base = null, safe = false;
var listenerType = (global.addEventListener && 2)||
(global.attachEvent && 3)|| 0;
function getStackFunc(funcRef, arg1,arg2,arg3,arg4){
var next = null;
function l(ev){
funcRef((ev?ev:global.event), arg1,arg2,arg3,arg4);
if(next){next = next(ev);}
return (arg1 = arg2 = arg3 = arg4 = funcRef = null);
};
l.addItem = function(d){
if(next){
next.addItem(d);
}else{
next = d;
}
};
return l;
};
return (function(funcRef, arg1,arg2,arg3,arg4){
if(base){
base.addItem(getStackFunc(funcRef, arg1,arg2,arg3,arg4));
}else{
base = getStackFunc(funcRef, arg1,arg2,arg3,arg4);
}
if(!safe){
switch(listenerType){
case 2:
global.addEventListener("load", base, false);
safe = true;
break;
case 3:
global.attachEvent("onload", base);
safe = true;
break;
default:
if(global.onload != base){
if(global.onload){
base.addItem(getStackFunc(global.onload));
}
global.onload = base;
}
break;
}
}
});
})();
- except in a more concise form. The effect is to provide a single
interface to having code called onload. Internally it is apparently
complex, using closures and building private structures, but externally
it is simplicity itself. One function call is the entire public
interface. It doesn't care how many items of initialisation code employ
it or whether it is never used at all. It does its job reliably and
cleans up after itself; a perfect black box.
When I introduced that function to replace all of the ad-hock
initialisation code I was certainly the only individual who understood
how it worked. Even the javascript programmer who wrote the original
in-window pop-up code had never seen anything like it, but he did not
even consider rejecting its use as he recognised the value of a single
simple interface to the problem. And the template authors, who are not
even interested in how it works, instantly recognised it as ideal for
use in their code.
Generally, if the public interface is sufficiently simple and concise
and the internal details authored by someone who knows what they are
doing (and so are robust, reliable, complete and well tested) there
should be no problems with the result being used as a black box by the
most unskilled authors. Even when they have no hope of understanding the
internal details.
Indeed it might be that the internal details only gain an unjustified
significance in javascript because it is difficult to actually hide them
from others. In, for example, Java you don't concern yourself with
asking whether you understand the implementation of the HashMap class,
do you? You cannot easily see it, and so long as it works as advertised
you don't have to care. So why should being presented with a
closure-based javascript HashMap implementation become more alarming?
Just because you can see that you don't understand its source code?
I guess a fundamental question is: Given two sets of code
- one written in a manner that uses the language and its
features to its fullest and one which uses global functions
and procedural style - that perform the same task effectively,
is one necessarily better than the other? And is that a
subjective or objective decision? Based ono what?
Working at the end of 50-odd years of thought and research into software
design and development we are in a position to apply more criteria to
decision making than "perform the same task effectively". Why has
machine code authoring been replaced with higher level languages? Why
have complex systems moved towards OO? Why have a choice of languages at
all?
When, for example, under the heading "Measuring the Quality of an
Abstraction", Grady Booch discusses the concepts of: Coupling, Cohesion,
Sufficiency, Completeness and Primitiveness, isn't the implication that
there are many criteria for assessing all sorts of aspects of software
design?
The notion that experienced programmers use "advanced" techniques for no
other reason than just because they can is ridiculous. While it may be
true that novice programmers do not use "advanced" techniques just
because they cannot, the more experienced have good reasons for the
implementation decisions that they make. Reasons informed by experience,
and reasons informed by the study of the subject. And if the novice
cannot comprehend the results then that is a manifestation of being a
novice not a justification for adopting a lowest common denominator
approach to software authoring.
Richard.