dispatch class, modularity, initialisation?

B

bugbear

I would like to have a base class
"Widget", that amongst other things
has a method getWidgetFor(String name).

I would like this method to be able to use (e.g.)
a Widget class static HashMap to look up Widgets.

Each entry in the Map will be an instance
of a sub-class of widget; some sub-classes
may "want" to put more than one entry in the map.

So - how do I populate the map?

If I have a static block in each sub-class,
I have no trivial way of ensuring
that ALL my subclasses have been referred
to (thus loading them, and running any static
code blocks).

If I have an "init()" method in each sub-class,
it might be called more than once.

I welcome advice, tip, examples, opinions etc.

BugBear
 
I

Ingo R. Homann

Hi,

sounds like some kind of Plugin-Mechanism?

In fact, since you cannot make sure that all subclasses are loaded, you
also cannot make sure that all static initializers are called (I think
you have realized that problem already), and of course you cannot make
sure that the init-methods of all classes are called (same problem)! (Of
course it would be no problem to secure that they are not called twice
by using a flag.)

You say, you need a mapping from "widget name" to "widget class". You do
not say if you need a list of all available widgets. If you do not need
such list, a quite easy solution would be to locate the widget classes
in a certain package and - e.g. if the widget is called "Foo", call the
class "myWidgetPackage.FooWidget". Then you can easily map the name to a
class just by calling Class.forName()!

However, if you need a list of all widgets, you will either have to
manually list them in a property file or e.g. locate them in a certain
directory and scan this directory at startup.

Ciao,
Ingo
 
S

SadRed

I would like to have a base class
"Widget", that amongst other things
has a method getWidgetFor(String name).

I would like this method to be able to use (e.g.)
a Widget class static HashMap to look up Widgets.

Each entry in the Map will be an instance
of a sub-class of widget; some sub-classes
may "want" to put more than one entry in the map.

So - how do I populate the map?

If I have a static block in each sub-class,
I have no trivial way of ensuring
that ALL my subclasses have been referred
to (thus loading them, and running any static
code blocks).

If I have an "init()" method in each sub-class,
it might be called more than once.

I welcome advice, tip, examples, opinions etc.

BugBear

As the value of the Map, use Collection<Entry> instead of a bare Entry
object.
 
B

bugbear

SadRed said:
As the value of the Map, use Collection<Entry> instead of a bare Entry
object.

I don't understand what part of my problem this helps;
please expand?

BugBear
 
L

Lew

By this, do you mean that a subclass may want to add more than one key, each
with its own value?

Have each class register itself with the parent class's registry as part of
its static initializer.

public class Sub extends Widget
{
static
{
synchronized ( Widget.registry )
{
registry.put( aKey, someValue );
registry.put( bKey, otherValue );
...
}
}
}

Obviously all get()s from the registry must be synchronized on the registry, too.

Nope. Just the ones that have initialized.

One cannot, in general, determine all subclasses from the parent class,
because new subclasses can be loaded at any moment, including /just/ after
taking inventory.

OTOH, each subclass knows of its own existence as it's born, thus can register
itself as part of its initialization. Thus, while you cannot determine /a
priori/ that each subclass has been registered, each subclass can guarantee
that it will not be used until it's registered.

Not if you code it right, make it no more visible than package-private, and
use a factory method instead of a constructor.
I don't understand what part of my problem this helps;
please expand?

It's a bad idea to keep a collection of Map.Entry as a value, if that's what
was meant by the advice. It might be a good idea to keep a Collection<V> or
Collection<? extends V> where V is the type of your values.

I think SadRed is solving your problem of subclasses registering more than one
entry, on the reading that you wanted to keep more than one value per key, but
I'm not sure that's what was meant. I do know that I had to read the original
post to see if that's what you wanted, or if you wanted to keep several values
per subclass that have different keys.

One also wonders how you prevent key collisions between subclasses. Perhaps
you need a key that is a Pair< Class<? extends Widget>, ? extends KeyType > as
the key to your map. Thus each subclass entry is keyed by its own class and
its own key values,
 
B

bugbear

Lew said:
By this, do you mean that a subclass may want to add more than one key,
each with its own value?


Have each class register itself with the parent class's registry as part
of its static initializer.

public class Sub extends Widget
{
static
{
synchronized ( Widget.registry )
{
registry.put( aKey, someValue );
registry.put( bKey, otherValue );
...
}
}
}

Obviously all get()s from the registry must be synchronized on the
registry, too.


Nope. Just the ones that have initialized.

One cannot, in general, determine all subclasses from the parent class,
because new subclasses can be loaded at any moment, including /just/
after taking inventory.

OTOH, each subclass knows of its own existence as it's born, thus can
register itself as part of its initialization. Thus, while you cannot
determine /a priori/ that each subclass has been registered, each
subclass can guarantee that it will not be used until it's registered.

Yes. I view this behaviour as more from the "problem ste"
than from the "solution set"
Not if you code it right, make it no more visible than package-private,
and use a factory method instead of a constructor.

Good Thoughts. Thanks you.

It's a bad idea to keep a collection of Map.Entry as a value, if that's
what was meant by the advice. It might be a good idea to keep a
Collection<V> or Collection<? extends V> where V is the type of your
values.

I think SadRed is solving your problem of subclasses registering more
than one entry, on the reading that you wanted to keep more than one
value per key, but I'm not sure that's what was meant. I do know that I
had to read the original post to see if that's what you wanted, or if
you wanted to keep several values per subclass that have different keys.

One also wonders how you prevent key collisions between subclasses.
Perhaps you need a key that is a Pair< Class<? extends Widget>, ?
extends KeyType > as the key to your map. Thus each subclass entry is
keyed by its own class and its own key values,

Ah - well, this goes to the heart of the modularity/context issue
I'm trying to think about. Clearly (?) the *callers*
of the registry.get() method SOMEHOW know the names
they want to use, so "someone" knows "something".

I guess what this boils down to "who" should know
the names (and existence) of the various widgets,
and how should this knowledge should be aggregated in
(what you nicely called) the registry.

I'm starting to think that the base class is the correct
place for this knowledge, and that a static block
in the base class would be a perfectly "clean" place
to populate the registry by simple performing code along
the lines of:

registry.add("nigel", new WidgetA());
registry.add("george", new WidgetB("arbitrary"));
registry.add("henry", new WidgetB("something"));

BugBear (still coding against 1.4)
 
L

Lew

bugbear said:
I'm starting to think that the base class is the correct
place for this knowledge, and that a static block
in the base class would be a perfectly "clean" place
to populate the registry by simple performing code along
the lines of:

registry.add("nigel", new WidgetA());
registry.add("george", new WidgetB("arbitrary"));
registry.add("henry", new WidgetB("something"));

Indeed. If you take that one step more, you will externalize the sub-class
names to a properties file and let them all have just the default constructor.
Let each subclass handle its own "arbitrary" or "something" in its own
initialization (instance preferred to static). Use Class.newInstance() off
the class object stored as the value, which class object was reflectively
created upon base class static initialization or static init() method (or even
reInit()) based on the externalized properties.

E.g., (untried, uncompiled)

public class Base
{
private static final Map registry = new HashMap();
// or could use Collections.synchronizedMap()

private static void initRegistry()
{
Properties props = getProperties();
Map mappings = new HashMap();
for ( Iterator iter = props.keySet().iterator(); iter.hasNext(); )
{
String key = (String) iter.next();
// here you might have logic to decide whether to use this key
String name = props.getProperty( key );
Class clazz = Class.forName( name ); // try...catch omitted for clarity
mappings.put( key, clazz );
}
synchronized ( registry )
{
registry.putAll( mappings );
}
} // end initRegistry()

private static Properties getProperties()
{
Properties p;
// read the properties from a resource
return p;
}
}
 
R

Roedy Green

I would like to have a base class
"Widget", that amongst other things
has a method getWidgetFor(String name).

I would like this method to be able to use (e.g.)
a Widget class static HashMap to look up Widgets.

Each entry in the Map will be an instance
of a sub-class of widget; some sub-classes
may "want" to put more than one entry in the map.

So - how do I populate the map?

If I have a static block in each sub-class,
I have no trivial way of ensuring
that ALL my subclasses have been referred
to (thus loading them, and running any static
code blocks).

If I have an "init()" method in each sub-class,
it might be called more than once.

I welcome advice, tip, examples, opinions etc.

This is what is called a "factory". Look up the factory design
pattern for some ideas.

Here are three ways you could handle it:

1. create constant array:
PossibleWidgets[] pw = { new Gizmo(), new Thingamabob[]... };

Then loop through the array getting the string name and adding to a
HashMap. Lookup by name. Factory hands out the original or a clone
of the exemplar object.

2. Look in the HashMap for the string to look up an exemplar object (
clone or use original as needed) If it is not there, try
Class.forName, and add it.

3. Turn the string into an enum with valueOf. Let that enum constant
either create the object you want or be the object you want.
 
B

bugbear

Lew said:
Indeed. If you take that one step more, you will externalize the
sub-class names to a properties file and let them all have just the
default constructor. Let each subclass handle its own "arbitrary" or
"something" in its own initialization (instance preferred to static).

That's a lot of typing if I want (say) 30 different
objects(instances) in the registry, where some
of them vary in ways that are trivially parameterisable.
e.g. days of week.

(I read your code before snipping)

I guess (as with all code) there's no ONE-size-fits
all solution, which is why programmers still have a job.

BugBear
 
L

Lew

bugbear said:
That's a lot of typing if I want (say) 30 different
objects(instances) in the registry, where some
of them vary in ways that are trivially parameterisable.
e.g. days of week.

It's the same amount of typing whether you load one object or one hundred.
That's the beauty of algorithms - one block of code handles arbitrary data set
sizes.

I don't understand your objection.
 
B

bugbear

Lew said:
It's the same amount of typing whether you load one object or one
hundred. That's the beauty of algorithms - one block of code handles
arbitrary data set sizes.

I don't understand your objection.

Because in some cases, something may be easily expressed
by simply parameterising a class; your approach
(if I've understood) requires a separate class for
each instance in the registry (since you're using
Use Class.newInstance() with no parameters
as the only way to get an instance).

Creating a class, even using inheritance, just to
parameterise an instance, is what I'm suggesting
is a lot of typing.

BugBear
 
L

Lew

bugbear said:
Because in some cases, something may be easily expressed
by simply parameterising a class; your approach
(if I've understood) requires a separate class for
each instance in the registry (since you're using
Use Class.newInstance() with no parameters
as the only way to get an instance).

Creating a class, even using inheritance, just to
parameterise an instance, is what I'm suggesting
is a lot of typing.

It was not clear from your earlier posts that you were using the same class
with different parameters to configure each instance.

The same idea still works, just externalize the parameters. Jeez, doesn't
anyone extract an apply principles from fragmentary examples any more?
 
B

bugbear

Lew said:
It was not clear from your earlier posts that you were using the same
class with different parameters to configure each instance.

Not everytime, I just like the option:
The example was:

registry.add("nigel", new WidgetA());
registry.add("george", new WidgetB("arbitrary"));
registry.add("henry", new WidgetB("something"));

The same idea still works, just externalize the parameters. Jeez,
doesn't anyone extract an apply principles from fragmentary examples any
more?

Jeez - doesn't anyone read the posts they're replying to :)

BugBear
 
L

Lew

bugbear said:
Not everytime, I just like the option:
The example was:

registry.add("nigel", new WidgetA());
registry.add("george", new WidgetB("arbitrary"));
registry.add("henry", new WidgetB("something"));



Jeez - doesn't anyone read the posts they're replying to :)

You mean like where you said,
Each entry in the Map will be an instance
of a sub-class of widget; some sub-classes
may "want" to put more than one entry in the map.
?

I provided a general pattern, handling the subclass part of your request as
being the subtler and more directly connected to the pattern. I figured the
Gentle Reader would extract what they needed from it. I showed the pattern
for sub-classes; surely you can generalize it to same class, different
parameters. (Hint: Use a factory instead of a constructor, or don't use the
default constructor but one that takes the required parameters, or use a builder.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,776
Messages
2,569,603
Members
45,189
Latest member
CryptoTaxSoftware

Latest Threads

Top