Collection, enum or something else?

T

Todd

Hello,

Suppose one has a class called Shrub that has well-defined behavior
and state. Now suppose one wants to create references (pardon the
loose wording, hopefully this will become clear shortly) to shrubs of
the world by continent.

One could extend the original Shrub class for each type of shrub and
then collect objects of the different shrubs in variables named by
continent, e.g., Collection NorthAmericanShrubs, etc. Or one could
declare an enum that uses the base shrub class as a constructor
argument, e.g.,

public enum NorthAmericanShrubs
{
firstNAshrub( new Shrub( "firstNAshrub" ) ),
secondNAshrub( new Shrub( "secondNAshrub" ) );

public NorthAmericanShrubs( Shrub shrub )
{
this.shrub = shrub;
}

private Shrub shrub = null;
}

In some ways, the enum is cleaner as it doesn't require multiple
classes that would only be different by constructor (each shrub
type), however, I can't access 'shrub' directly as firstNAshrub is
of type NorthAmericanShrubs, not type Shrub.

Without the addition of a getter method, I don't have access to
Shrub behavior, but that adds undesirable complexity such as
when one might want to iterate over the shrubs:

for( NorthAmericanShrubs naShrub : NorthAmericanShrubs.values() )
{
Shrub shrub = naShrub.getShrub();

// do something here
}

Is there something cleaner than multiple, essentially identical
classes or the enum construct? Or am I just being too picky
about not being able to access Shrub behavior directly through
my enum (as if the enum extended a type)? Or is this an
instance where abstracting out an interface for both Shrub and
NorthAmericanShrubs is appropriate (which would then create
object delegation code)?

Thanks,
Todd
 
M

Mark Space

Todd said:
Hello,

Suppose one has a class called Shrub that has well-defined behavior
and state. Now suppose one wants to create references (pardon the
loose wording, hopefully this will become clear shortly) to shrubs of
the world by continent.

I think you might be over complicating things. What's the matter with:

public class Shrub
{
String name;
String continent;

public Shrub( String name, String continent )
{
this.name = name;
this.continent = continent;
}
}


Then you just can just do this:

public class ShrubsOfTheWorld
{
Map<String,Shrub> byName = new HashMap<String,Shrub>();
Map<String,Shrub> byContinent = new TreeMap<String,Shrub>();

public void addShrub( Shrub s )
{
byName.put( s.name, s );
byContinent( s.continent, s );
}
}

Or something. I think you're over complicating things by using an enum,
for sure. There are probably not a finite list of shrubs that you want
to enumerate, and more will be discovered. My primary clue here was that
just a Shrub object was good enough inside your enum. If it is, then
you don't need the enum or a subclasse. "Prefer composition to
inheritance." And that's what I did. Rather than subclass or make an
enum, I just composed Shrug so that it had the property needed.

I guess an enum for the continents makes sense though. Unlikely that
any new continents will come along.

BTW I chose TreeMap because I think it will handle the large number of
collisions better that storing by continent will cause than a hash map
would. There's probably a better way to do this, I just did a quick
first pass at it.
 
A

Arved Sandstrom

Larry A Barowski said:
I would think that one type of shrub could be found on more
than one continent, so (including Lew's suggestion), that
should be: List<Continent> continents;

And for the real world problem, you'll want to have hundreds of defined
geographical regions (it's relatively meaningless, for example, to say that
a species of shrub can be found in Asia), so you'll probably be using a
database anyway.

AHS
 
T

Todd

Mark,

Thanks for your very clear response. I agree that it I had
complicated
my example a bit. I have been trying to come up with ways to ask
questions here without exposing too much of what I am really working
on and not using names that invoke the ire of would be respondents (my
example started with Tree, but then I thought that Shrub would be less
prone to Java-naming collisions). That said, I have some follow-on
questions.

With your set-up, I then suppose that you would have some sort of
getters for the collections within ShrubsOfTheWorld and then
iterate on the returned collection as needed? The direct access
to Shrub methods is again lost without an intermediate object.

How would you handle a memory intensive application wherein an
intermediate object could be have a significant impact on memory?
I am coming back to wondering whether an interface provides the
proper mechanism for reducing unnecessary objects.

An example to (hopefully) provide clarity (assume an known finite
list of shrubs):

public interface Shrub
{
Color getColor();
double getHeight();
}

public class ShrubProperties implements Shrub
{
public ShrubProperties( Color color, double height )
{
this.color = color;
this.height = height;
}

public Color getColor()
{
return color;
}

public double getHeight()
{
return height;
}

private Color color = null;
private double height = 0.d;
}

public enum NorthAmericanShrub implements Shrub
{
// Assume that these are all the shrubs that I
// will ever need - this is an example, not meant
// to be true to life
aShrub( new Shrub( Color.BLUE, 20.d ) ),
bShrub( new Shrub( Color.GREEN, 1.d ) ),
cShrub( new Shrub( Color.RED, 34.56d ) );

public NorthAmericanShrub( Shrub shrub )
{
this.shrub = shrub;
}

public Color getColor()
{
return shrub.getColor();
}

public double getHeight()
{
return shrub.getHeight();
}

private Shrub shrub = null;
}

Elsewhere, it is possible to access the enumerated
Shrub directly due to the interface (which I guess
isn't _required_ it just enforces the contract), such
as:

public void someMethod()
{
for( Shrub shrub : NorthAmericanShrubs.values() )
{
System.out.println( "Shrub color:" + shrub.getColor() );
System.out.println( "Shrub height:" + shrub.getHeight() );
}
}

This eliminates the need for the intermediate object
shown in the original post. This is likely still not
the most proper way to do this. I tend to see things
in a very complicated fashion (simplicity eludes me).

I am not intending to discount your response in anyway,
just trying to grasp good design and maybe learn more
about proper use of the interface class.

Thanks again,
Todd

BTW, none of the above code has been compiled let alone tested
 
R

Roedy Green

enum that uses the base shrub class as a constructor
argument,

You would us an enum to select different custom code for each group.
If you were merely categorising data, you would not use an enum,
though you might use an enum data field is your shrub record for the
various broad classes.

The main drawback of an enum is it requires a programmer and
recompilation to add a new category. It might be reasonable to have a
enum genus, but certainly not a enum species.

To create a simple hierarchy, you would use a database with genus
records for example Acer, Ilex, Cedrus etc.

Then you would have species records, with the genus name embedded
which could be used to look up the genus record with info about the
entire genus.

If you use a boolean array to track which regions a species inhabited,
you could get SQL to find you all the species of a given genus that
inhabited a given region, etc.
 
D

David A. Redick

In cases like this I typically make a very broad generic class and
have the actual data in a database or something.
'Database driven', I think is the term for it.
 
M

Mark Space

Todd said:
How would you handle a memory intensive application wherein an
intermediate object could be have a significant impact on memory?
I am coming back to wondering whether an interface provides the
proper mechanism for reducing unnecessary objects.

Well, here you blow right past anything we could do with a simple
example. If you have some concerns you should try to be explicit,
because it becomes darn hard to guess the right way to optimize this.
(Not that I'm an expert or anything.)

Profiling is the right way to optimize, but dealing with lots of data is
best done at a high level, by design, so you *can* optimize. And
without some particulars it's hard for us to comment on any architecture
that someone might propose.


One thing that does occur to me is immutable objects. If you make the
fields of ShrupProperties below final, you and the compiler both can
make a lot of optimizations on the object itself.

But that's a pretty micro optimization, not the kind of architectural
decision that you need.



This eliminates the need for the intermediate object
shown in the original post. This is likely still not

I don't think eliminating intermediate objects should be your goal.
Sounds dubious at best, if not a known bad practice. I'd have to see
more of your design (how many shrubs are we talking about here?) but
"substitute an enum for a Map" just seems like a very controversial
attempt at optimization, regardless of the design.
 
T

Todd

Mark,

Thanks again for distilling to the core or my concerns. The
example I am given is entirely imagined and not complete
enough to fully describe my questions - however, you seem to
have found the crux.

From what I can tell, it would be better for me to be more
concerned about readability, usability, and maintainability
than attempting to force a pattern or design axiom that may
likely be inappropriate for the problem.

Thank you and all others for your input,
Todd
 

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,772
Messages
2,569,593
Members
45,104
Latest member
LesliVqm09
Top