Re: Global variables
iamfractal_at_hotmail.com
Date: 10/29/04
- Next message: Andrew McDonagh: "Re: Opinions on the Law Of Demeter"
- Previous message: Kavvy: "Re: beginner - stuck on data storage"
- In reply to: ShadowMan: "Re: Global variables"
- Next in thread: H. S. Lahman: "Re: Global variables"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Date: 29 Oct 2004 07:18:31 -0700
"ShadowMan" <shadman@despammed.com> wrote in message news:<clr6lt$9122@intra09.infocamere.it>...
> <iamfractal@hotmail.com> ha scritto nel messaggio
> news:4890bcd3.0410280733.e6b23a6@posting.google.com
> > "ShadowMan" <shadman@despammed.com> wrote in message
> > news:<kLRfd.93395$b5.4505422@news3.tin.it>...
> >> H. S. Lahman <h.lahman@verizon.net> wrote:
> >>> Responding to Grant...
> >>
> >
> > For what it's worth, I do more or less as you do.
>
> Well I'm happy to be on the right way...
> BTW, I have a litte OT question: how do you initialize the Repository?
> (Startup class, contextlistner, startup servler, other??)
>
> thanx
I'm glad you asked that.
Initialising the interface repository is part of a process, and one of
the greatest dangers associated with this process is, perhaps
surprisingly, circular dependencies.
This all starts with the framework upon which we're building our
application. If we're building a stand-along application, then our
starter class will be the one with the main() method; if we're
building a servlet, then it'll be the class with the init()
method. Whichever framework we're building upon, we'll call this
first class triggered the Starter class. Our Starter class will be
responsible for instantiating some key classes in other packages.
According to Mister GoF, in his, "Design Patterns," book, the first
principle of object orientation is to design towards an interface, not
an implementation. Starter, however, is a shameless violation of this
principle: it will call either new on normal classes or getInstance()
on singletons in other packages; it will depend directly on
implementations and not interfaces.
And there's nothing (short of reflection) we can do about it.
So instead of giving up on OO before we start, we'll decide to carve
up our appilcation into two stages: the internal configuration stage,
and
the operations stage.
During the operations stage, our application will earn its money:
it'll fulfil requirements and do whatever our customer told it to
do. More importantly (though our customer might disagree with this
evalution), it will accord with the first principle of OO; during this
stage, no first-principle violations will be allowed, and we will
design towards interfaces.
During the internal configuration stage, we'll do everything we need
to prepare for entry into the operations stage. We will, however,
we'll walk all over the first principle: but we'll do it in a limited
and controlled manner. And our first step will be to decree that the
Starter class must not reside in the interface respository package.
So, to begin with we have our Starter class, and a Repository class
whose sole job is to return interfaces to clients; we'll put the
Starter class in a package called, "starter," and the Respository
class in a package called, "ir1," (Interface Repository 1, because
expience hints that we will end up with many interface repository
packages in our application). We then, of course, have all the other
class and packages in our application.
Essentially, once our Starter class is called, it will call (at least
indirectly) all the other classes in the application that must
register in the Repository, and they will both perform their
registrations, and retrieve from the repository those interfaces upon
which they depend when they enter the operations stage.
Each of the classes that Starter calls will typically implement three
of the industry-standard GoF patterns: it will be a singleton, the
interface it stores in the repository will be the facade for that
package, and it will be the mediator for that package.
The Starter class will instantiate at most one class per package, this
will be the mediator class. For all other classes that must register
in the repository from within that package, the mediator will call
them to order them to register and look up other interfaces. This thus
limits the Starter class to the implementations within the package.
Here we see the point of having the Starter class ourside the
interface repository package: if the Starter had been in the interface
respository package then it would have dependencies on implementations
spread throughout the system, and those implementations - through
their registrations - would have dependencies back on the interface
repository pacakge, thus creating the package-level circular
dependencies that ruin our application's re-use strategies.
Another crucial point of both the internal configuration stage, and
the
Repository class itself, is that they must be designed to be as simple
as possible. These are first-principle violators, and so any changes
to the design of these classes may have implications for the entire
system later on.
It will happen, for example, that an application class instantiated
during this internal configuration stage will depend for its
configuration on another class that is instanciated during this stage.
Take, for example, class ConcreteJuice, in the, "liquid," package,
that depends on the interface Drinkable that will be stored in the
Repository; this Drinkable is implemented by the ConcreteDrinkable
mediator of package, "sustenance." If we do not order how Starter
instantiates the application's classes, then it may happen that
ConcreteJuice tries to retreive Drinkable from the Respository before
ConcreteDirnkable has stored the interface.
We cannot have, "Lazy instantiation," in this case, as this will
introduce unwanted dependencies on the sustenance package.
For example, ConcreteJuice could have lazy instantiation as follows:
public class ConcreteJuice {
Repository repository = null;
Drinkable drinkable = null;
public void start() {
repository = Repository.getInstance();
repository.put(this);
drinkable = repository.getDrinkable();
if (drinkable == null) {
drinkable = new ConcreteDrinkable(); // DEPENDENCY!
}
}
}
But this introduces a dependency between two implementation
repositories, which we do not want; we would like all our dependencies
to be on interface repositories (except for the starting dependencies,
or course).
We could also have the lazy dependency in the Repository:
public class Repository {
Drinkable drinkable = null;
public Drinkable getDrinkable() {
if (drinkable == null) {
drinkable = new ConcreteDrinkable(); // DEPENDENCY!
}
return drinkable;
}
}
But this, even worse, would be a circular depencency between the
interface repository package and the sustence package.
So we divide the internal configuration stage into phases; intially we
chose two phases: during the first, all relevant implementation class
stores their facade interfaces in the Repository; in the second phase,
Starter contacts all the same classes again, but this time the only
retrieve interfaces from the Repository.
This may look sound to begin with, but experience tells that some
implementation classes will need further phases. Take the example
above: in the first phase, both Drinkable and Juice will be stored in
the Repository, and in the second phase, ConcreteJuice will retrieve
the Drinkable interface. What if we have a third class,
ConcreteCocktail, which needs an implementation of Juice prior to
entering the operations stage, but only when Juice has secured its
Drinkable implemenation? In this case, we have the ordering problem
again: we must make sure that ConcreteCocktail does try to retrieve
Juice before both Juice and Drinkable are stored.
This will need a third phase.
And we may need many phases before all dependencies can guaranteed to
be satisified before embarking on our operations stage.
Thus, each implementation class being started can all implement a
PhasedStart interface such as this:
public interface PhasedStart {
public void start(int phase);
}
And each implementation class must only perorm necessary look-ups
during appropriate phases. The ConcreteCocktail's method might look
like this:
public class ConcreteCocktail implements Cocktail {
public void start(int phase) {
switch (phase) {
case (Options.PHASE1):
repository = Repository.getInstance();
repository.put(this);
break;
case (Options.PHASE3): // Phase 3, not 2
juice = repository.getJuice();
break;
}
}
}
And of course Starter gathers a List of PhasedStarter objects, and
loops through them calling start(Options.PHASE1),
start(Options.PHASE2), etc.
That's how I initialize my Repository ...
.ed
www.EdmundKirwan.com - Home of The Fractal Class Composition
- Next message: Andrew McDonagh: "Re: Opinions on the Law Of Demeter"
- Previous message: Kavvy: "Re: beginner - stuck on data storage"
- In reply to: ShadowMan: "Re: Global variables"
- Next in thread: H. S. Lahman: "Re: Global variables"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]