Conundrum with package name in "dynamic" static factory method

N

Nobody

Hi,

In the spirit of "most maintainable" code, I'm applying a factory method
to instantiate objects using part of a class name that's passed to the
factory method, followed by reflection and newInstance(). The code goes
something like this:

public static Product loadProductDynamically(String requestedPF)
throws UnsupportedProductFactoryException
{
Product theProduct;
try {
theProduct=
(Product) Class.forName(requestedPF + "Product").
newInstance();
} catch (InstantiationException e) {
throw new UnsupportedProductFactoryException();
} catch (IllegalAccessException e) {
throw new UnsupportedProductFactoryException();
} catch (ClassNotFoundException e) {
throw new UnsupportedProductFactoryException();
}
return theProduct;
}

This works great if everything (this class, the *Product classes) is in
the "default" package. As soon as I put the code in another named
package, the forName() method above doesn't work. Obviously you have to
specify the package part of the class.

But, how can I get the package name in a static method like the one
above without hard-coding the package name anywhere in the class?
Ideally, this kind of code should work for any class in any package,
e.g., expecting that the "*Product" classes are in the same package.

Thanks in advance!
 
M

Mike Schilling

Nobody said:
Hi,

In the spirit of "most maintainable" code, I'm applying a factory method
to instantiate objects using part of a class name that's passed to the
factory method, followed by reflection and newInstance(). The code goes
something like this:

public static Product loadProductDynamically(String requestedPF)
throws UnsupportedProductFactoryException
{
Product theProduct;
try {
theProduct=
(Product) Class.forName(requestedPF + "Product").
newInstance();
} catch (InstantiationException e) {
throw new UnsupportedProductFactoryException();
} catch (IllegalAccessException e) {
throw new UnsupportedProductFactoryException();
} catch (ClassNotFoundException e) {
throw new UnsupportedProductFactoryException();
}
return theProduct;
}

This works great if everything (this class, the *Product classes) is in
the "default" package. As soon as I put the code in another named package,
the forName() method above doesn't work. Obviously you have to specify the
package part of the class.

But, how can I get the package name in a static method like the one above
without hard-coding the package name anywhere in the class? Ideally, this
kind of code should work for any class in any package, e.g., expecting
that the "*Product" classes are in the same package.

Thanks in advance!

*Someone* needs to know the package name. You can hardcode it in the
factory or have the caller pass it in. If you want to the class to be in
the same package as some other object, then change your signature to:


public static Product loadProductDynamically(String requestedPF, Object
inSamePackage)

and calculate the package name from

inSamePackage.getClass().toString()
 
R

Raymond DeCampo

Nobody said:
Hi,

In the spirit of "most maintainable" code, I'm applying a factory method
to instantiate objects using part of a class name that's passed to the
factory method, followed by reflection and newInstance(). The code goes
something like this:

public static Product loadProductDynamically(String requestedPF)
throws UnsupportedProductFactoryException
{
Product theProduct;
try {
theProduct=
(Product) Class.forName(requestedPF + "Product").
newInstance();
} catch (InstantiationException e) {
throw new UnsupportedProductFactoryException();
} catch (IllegalAccessException e) {
throw new UnsupportedProductFactoryException();
} catch (ClassNotFoundException e) {
throw new UnsupportedProductFactoryException();
}
return theProduct;
}

This works great if everything (this class, the *Product classes) is in
the "default" package. As soon as I put the code in another named
package, the forName() method above doesn't work. Obviously you have to
specify the package part of the class.

But, how can I get the package name in a static method like the one
above without hard-coding the package name anywhere in the class?
Ideally, this kind of code should work for any class in any package,
e.g., expecting that the "*Product" classes are in the same package.

Assume that your code above lives in the class ProductFactory. I also
assume your Product implementations live in the same package. Then you
can use the following (untested, uncompiled):

public class ProductFactory
{
public static Product loadProductDynamically(String requestedPF)
throws UnsupportedProductFactoryException
{
Product theProduct;
try {
theProduct = (Product)
Class.forName(
/****** Here's the "magic" ***************/
ProductFactory.class.getPackage().getName()
+ '.' + requestedPF + "Product")
.newInstance();
} catch (InstantiationException e) {
throw new UnsupportedProductFactoryException();
} catch (IllegalAccessException e) {
throw new UnsupportedProductFactoryException();
} catch (ClassNotFoundException e) {
throw new UnsupportedProductFactoryException();
}
return theProduct;
}
}

Of course, this won't work in the default package, but I assume those
days are behind you.

HTH,
Ray
 
W

Wibble

Raymond said:
Assume that your code above lives in the class ProductFactory. I also
assume your Product implementations live in the same package. Then you
can use the following (untested, uncompiled):

public class ProductFactory
{
public static Product loadProductDynamically(String requestedPF)
throws UnsupportedProductFactoryException
{
Product theProduct;
try {
theProduct = (Product)
Class.forName(
/****** Here's the "magic" ***************/
ProductFactory.class.getPackage().getName()
+ '.' + requestedPF + "Product")
.newInstance();
} catch (InstantiationException e) {
throw new UnsupportedProductFactoryException();
} catch (IllegalAccessException e) {
throw new UnsupportedProductFactoryException();
} catch (ClassNotFoundException e) {
throw new UnsupportedProductFactoryException();
}
return theProduct;
}
}

Of course, this won't work in the default package, but I assume those
days are behind you.

HTH,
Ray

I forgot... Why is this ugly reflective mess that drops exception
details and instantiates arbitrary classes "most maintainable"? Smells
kinda bad to me. I hope theres a good reason for this thing.
 
N

Nobody

Wibble said:
Raymond DeCampo wrote: [snip of java code]
I forgot... Why is this ugly reflective mess that drops exception
details and instantiates arbitrary classes "most maintainable"? Smells
kinda bad to me. I hope theres a good reason for this thing.

Have you ever done a factory method before without reflection? It's a
big, uglier "if" statement with hard-coded class names - smells like
hard-coded methods crying for polymorphism. If that approach is more
maintainable, I don't see how, but I'm willing to listen to your
explanation.

Although a big if statement with "new" statements may be more readable,
adding support for a new sub-class is harder to maintain, because you
have to add the new if/then statement with the instantiation. If your
saying ultimately that the factory method design pattern is what's
harder to maintain, then maybe that's because design patterns are always
less obvious to those who've never seen them. The beauty of the approach
proposed here is that you simply create the new XyzProduct class and
drop it into the package. No code in the factory has to be changed.
There are arguments that this is risky in terms of security (which I
suppose was the meaning of your "arbitrary classes" comment).

The exceptions would exist in either factory implementation
(with/without reflection), but the example has simplified them. BTW,
this example was shown in Bruce Eckel's book, Thinking in Java, if I
recall correctly.
 
N

Nobody

Raymond DeCampo wrote:
[snippage]
try {
theProduct = (Product)
Class.forName(
/****** Here's the "magic" ***************/
ProductFactory.class.getPackage().getName()
+ '.' + requestedPF + "Product")
.newInstance();
} catch (InstantiationException e) {
[snippage]

Thanks! That did the trick!

I feel a bit silly - but I forgot about the "class" literal in Java
(http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html#251530)
- It's interesting that Eclipse's Java editor treats ".class" as a
static attribute of the ProductFactory (or any class for that matter),
while it suggests possibilities to complete the text "ProductFactory."
(dot completion).

I actually started trying to find a solution before posting on usenet,
and I looked for such a static attribute in the Object class - that
seems like a logical and intuitive place for it. However, this is a
feature of the language, not Object.

Anyway, the code done the way you propose above should even tolerate a
refactoring rename of the class ProductFactory without breaking, thus
respecting my criterion or it being non hard-coded. As I understand
..class, the above "magic" could have been written
Class.forName("ProductFactory").getPackage().getName() - but this would
likely not get cleanly refactored in a class displacement/rename as the
class name is in a string literal.
 
H

Hemal Pandya

Nobody said:
Wibble said:
Raymond DeCampo wrote: [snip of java code]
I forgot... Why is this ugly reflective mess that drops exception
details and instantiates arbitrary classes "most maintainable"? Smells
kinda bad to me. I hope theres a good reason for this thing.

Have you ever done a factory method before without reflection?

I have. Almost always, when the range of classes that can be
instantiated by the factory is known at compile time.
It's a
big, uglier "if" statement with hard-coded class names -

My code has a lot many other hard-coded class names. Most of my
variables are of hard-coded types actually. And I respectfully suspect
so are yours. said:
If that approach is more
maintainable, I don't see how, but I'm willing to listen to your
explanation.

It maybe slightly less maintainable, because as you point out the
factory method has to be modified every time a candidate class is added
to the code base. But I am willing to tolerate the incremental effort
over creating that candidate class itself.
Although a big if statement with "new" statements may be more readable,

Besides readability, it provides compile-time type checking and
declares in-code the range of classes that can be instantiated by the
factory.
 

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,769
Messages
2,569,578
Members
45,052
Latest member
LucyCarper

Latest Threads

Top