Hi Tim,
I wholeheartedly endorse the idea of removing gratuitous globals, even
if I'm only agreeing from the peanut gallery. Hopefully my response
will spur some conversation from folks that you were probably hoping to
hear from more than me.
It's probably not worth doing in one swell foop, but an agreement on a
direction combined with an incremental approach where cleanup is done as
a part of fixing issues (like you are doing now with $wgLinkCache) seems
like the smart way to go.
More inline:
On Thu, 2006-01-05 at 12:20 +1100, Tim Starling wrote:
In many cases, use of globals obscures data flow and
makes classes less
flexible, inhibiting reuse. This is patently true in the case of $wgTitle
and $wgArticle, the existence of which encourage lazy programmers to write
code which fails in the common case where more than one of these objects
exist. At present, these two objects are almost exclusively used in the
output phase, so it would make sense to make them members of OutputPage or
Skin instead of globals.
I'm not sure much reuse is gained by replacing $wgTitle with
$wgSkin->mTitle or even $wgSkin->getTitle(). It's just a global with a
bigger name.
That said, I agree with the sentiment. The more that developers can be
encouraged to write code that relies on "a Title object" as opposed to
"the Title object", the better.
The most extreme anti-global architecture would be one
involving application
objects:
$mw = new MediaWiki;
$mw->executeWebRequest();
The application object could theoretically be passed to most class
constructors, providing a form of global context. That, however, would make
writing new classes a bit tedious. In my experience, it turns out to be
easier to make the application object a global, and pull it in wherever it
is needed. This would have advantages when MediaWiki needs to be embedded as
a library, since it keeps the global scope cleaner, but it's not really more
flexible than what we're doing now.
I disagree. If that level of embedding were possible, I think a lot of
unanticipated reuse would fall out from it. A good example of where
(possibly) unanticipated reuse happened on a big scale is when Gtk
became an independent project from the Gimp. There are chunks of the
MediaWiki codebase that probably don't need to be married to MediaWiki.
I started thinking about these issues in building Electowidget. In the
end, I created a light abstraction layer around some MediaWiki services
(the "HostApplicationObject") which made it possible for me to port my
app to other frameworks.
http://electorama.com/2005/electowidget/classdocs/classHostApplicationObjec…
It hasn't proven itself yet. I've created a minimal standalone object,
and there's a developer who is working on a version for Drupal, so we'll
see how it goes.
The ideal situation for me and other plugin writers would be that
MediaWiki uses a type of hostapp object in its "native" code, such that
other apps could share that same hostapp implementation (or at least
share an API).
After some thinking, I was forced to admit that there
are some cases where
globals make sense, from a data flow perspective. The clearest example is
caching. A cache should have the widest possible scope. If you have two
application objects, you would want them to share the same caches if
possible. Indeed, it's better if different threads, processes and even
servers can share their caches.
There are, however, disadvantages to using global variables for this or any
other similar purpose. The problem is that the use of global variables
inhibit lazy initialisation. The familiar solution is to use an accessor
function, and indeed this approach has already been implemented in several
places in MediaWiki. I would like to make such accessor functions more
pervasive.
There is also the problem that the global namespace is somewhat crowded.
Using a global function for an accessor just moves this problem to somewhere
else.
The alternative [to global accessor functions] is to
use a static
class member as an accessor. This concept is well known, and where the
static object is the only one ever needed, the object is called a
singleton.
The disadvantage to the singleton pattern is that it
requires the class name
to be hard-coded throughout the code base, removing some flexibility. We
could get around that by having base classes construct derived classes, if
you don't mind the dependency implications.
This may be a good reason to use a global accessor function, like
wfGetDB to get the Database object. The db code is effectively used as
a singleton (well, perhaps a "poolington" ;-), and seems to work alright
as an approach.
The nice thing about a global function is that you get slightly more
flexibility to rename/redesign the base class. You can still use a
static variable rather than a global to actually store the object
instance. The downside is that it's a little less standard; it's nice
for a programmer new to the codebase to see "singleton" right off the
bat rather than need to figure out that they're dealing with something
that's roughly like a singleton. As far as the global function
namespace being too crowded, one could easily create a generic class
factory if a hierarchy is absolutely required.
We need to be guided by our applications, and choose
the simplest
architecture which supports all of them. Are we interested in:
* Embedding? Need to avoid namespace pollution.
* Per-wiki daemons to do background tasks? Need a means for periodically
refreshing configuration and caches.
* A daemon that responds to requests for multiple wikis? Needs multiple
language objects, and a caching system which discriminates between different
wikis.
These all seem like interesting apps, and I don't think the needs of any
of them are mutually exclusive. Of course, this conversation is always
amusing, and seems apropos:
http://c2.com/cgi/wiki?PrematureGeneralization
...so I also agree that any grand generalization work needs to be app
driven. My 2c would be prioritize and order the work based on immediate
demand (e.g. sounds like the daemon for multiple wikis is what you're
most interested in), but at least think about all of the apps above as
you/we/others are doing the work.
Rob