Law of Demeter and accessing classpath resources

C

chrislewis

Hello group,

I was having a Monday refactoring sweep through some code and came
across this line:

URL resourceUrl = this.getClass().getClassLoader().getResource
(resourceName);

The class this lives in is perfectly small and single-minded, but as
I'm reading through Clean Code my eyes are a bit more on edge.
According to what Clean Code says about the Law of Demeter, it seems
like this is Bad Thing, as I'm calling a method on an object yielded
through chained calls (not to mention the class loader could be
considered a global resource). I'd like to hear what your opinions are
on this, and if it is indeed a violation of the LoD, how can one
access resources on the classpath in a "clean" manner?
 
J

John B. Matthews

chrislewis said:
I was having a Monday refactoring sweep through some code and came
across this line:

URL resourceUrl = this.getClass().getClassLoader().getResource
(resourceName);

The class this lives in is perfectly small and single-minded, but as
I'm reading through Clean Code my eyes are a bit more on edge.
According to what Clean Code says about the Law of Demeter, it seems
like this is Bad Thing, as I'm calling a method on an object yielded
through chained calls (not to mention the class loader could be
considered a global resource). I'd like to hear what your opinions
are on this, and if it is indeed a violation of the LoD, how can one
access resources on the classpath in a "clean" manner?

A somewhat more reliable alternative is the context class loader:

<http://groups.google.com/group/comp.lang.java.programmer/
msg/eadead6597f78af8>
<http://java.sun.com/javase/6/docs/api/java/lang/Thread.html
#getContextClassLoader()>
 
R

Roedy Green

URL resourceUrl = this.getClass().getClassLoader().getResource
(resourceName);

The class this lives in is perfectly small and single-minded, but as
I'm reading through Clean Code my eyes are a bit more on edge.
According to what Clean Code says about the Law of Demeter, it seems
like this is Bad Thing, as I'm calling a method on an object yielded
through chained calls (not to mention the class loader could be
considered a global resource). I'd like to hear what your opinions are
on this, and if it is indeed a violation of the LoD, how can one
access resources on the classpath in a "clean" manner?

the law of Demeter would have a convenience method Class.getResource.
It is not up to you to provide that. The best you could do is:

static URL MyTools.getResource( class someClass ) to hid the chain.


HOWEVER, if you look in the Javadoc for Class, you will find a method,
Class.getResource, so Demeter, the god of distribution, has already
smiled on you. There is nothing for you to do.
--
Roedy Green Canadian Mind Products
http://mindprod.com

"Don’t worry about people stealing an idea; if it’s original, you’ll have to shove it down their throats."
~ Howard Aiken (born: 1900-03-08 died: 1973-03-14 at age: 73)
 
M

markspace

Roedy said:
HOWEVER, if you look in the Javadoc for Class, you will find a method,
Class.getResource, so Demeter, the god of distribution, has already
smiled on you. There is nothing for you to do.


Class.getResource() and ClassLoader.getResource() do two different
things. One is not a drop-in replacement for the other. Read the docs
carefully, but if it's working I say don't touch it.

The only thing I would change is the "this". It's unneeded, so remove
it. Aside from that, use your own common sense, the code tool is only a
suggestion box.
 
M

markspace

chrislewis said:
Hello group,

I was having a Monday refactoring sweep through some code and came
across this line:

URL resourceUrl = this.getClass().getClassLoader().getResource
(resourceName);


So I looked up what Wikipedia says about the LoD:

"The Law of Demeter (LoD), or Principle of Least Knowledge, is a design
guideline for developing software... [which] can be succinctly
summarized as “Only talk to your immediate friends.” The fundamental
notion is that a given object should assume as little as possible about
the structure or properties of anything else (including its subcomponents)."


Which in my opinion is B.S. in this case, since all methods referenced
are in an extremely stable API.

Conclusion: your tool is broken and should have an exception for the
public Java API.
 
M

markspace

Steven said:
From reading on, I get the impression that the article is talking about
genuine member objects - not mere references. Since that can't strictly
happen in Java, the nearest thing is that an object contains a reference
to some resource exclusive to that object. An Object certainly doesn't
exclusively reference a Class, nor does a Class exclusively reference a
ClassLoader, so again, I don't think it applies.


I happened to find this, which appears to be the orginal paper
describing the Law of Demeter, from OOPSLA, 1988.

<http://www.ccs.neu.edu/research/demeter/papers/law-of-demeter/oopsla88-law-of-demeter.pdf>

They do say that LoD is similar to other concepts like encapsulation and
loose coupling, but it goes further. However, what I've read of their
premise so far is that it all applies to the costs of refactoring:

"The motivation behind this Law is to ensure that the
software is as modular as possible. Any method written
to obey this Law will only know about the immediate
structure of the class to which it is attached.
The structure of the arguments and the sub-structure
of C are hidden from M. Therefore, should a change
to the structure of the class C be necessary we need
only to look at those methods attached to C and
its subclasses for possible conflicts."


That clearly doesn't apply here. First the API used is very stable.
Sun will not decide to just remove the Class.getClassLoader() method.
That just won't happen. So there's no chance that the owner of the
Class class will send us a note saying we have to refactor our code.

And second, there's no way for the OP to refactor Class anyway, which is
what LoD would demand. It's Class that needs to change, not the OP's code.

NOTE: I just realized that there IS a getResource() method in Class.
Maybe that's what the tool is talking about? Prefer this:

getClass().getResource(x);

instead of the longer version? These methods do not work the same, so
be wary of a quick refactoring. However, these methods do work very
similarly, so you could do it with some effort. (I think you'll have to
prepend a "/" for this version compared to the old one.)
 
M

markspace

Steven said:
From reading on, I get the impression that the article is talking about
genuine member objects - not mere references. Since that can't strictly
happen in Java, the nearest thing is that an object contains a reference
to some resource exclusive to that object. An Object certainly doesn't
exclusively reference a Class, nor does a Class exclusively reference a
ClassLoader, so again, I don't think it applies.


I happened to find this, which appears to be the orginal paper
describing the Law of Demeter, from OOPSLA, 1988.

<http://www.ccs.neu.edu/research/demeter/papers/law-of-demeter/oopsla88-law-of-demeter.pdf>

They do say that LoD is similar to other concepts like encapsulation and
loose coupling, but it goes further. However, what I've read of their
premise so far is that it all applies to the costs of refactoring:

"The motivation behind this Law is to ensure that the
software is as modular as possible. Any method written
to obey this Law will only know about the immediate
structure of the class to which it is attached.
The structure of the arguments and the sub-structure
of C are hidden from M. Therefore, should a change
to the structure of the class C be necessary we need
only to look at those methods attached to C and
its subclasses for possible conflicts."


That clearly doesn't apply here. First the API used is very stable.
Sun will not decide to just remove the Class.getClassLoader() method.
That just won't happen. So there's no chance that the owner of the
Class class will send us a note saying we have to refactor our code.

And second, there's no way for the OP to refactor Class anyway, which is
what LoD would demand. It's Class that needs to change, not the OP's code.

NOTE: I just realized that there IS a getResource() method in Class.
Maybe that's what the tool is talking about? Prefer this:

getClass().getResource(x);

instead of the longer version? These methods do not work the same, so
be wary of a quick refactoring. However, these methods do work very
similarly, so you could do it with some effort. (I think you'll have to
prepend a "/" for this version compared to the old one.)
 
C

chrislewis

Thanks for all the responses. I'm not sure which tool you are saying
is broken, but there is still the potential issue of consistent unit
tests. In this case, as has been pointed out, the api is very stable.
However, because the contents of the classpath are environmental, unit
tests will still be forced to make environmental assumptions (for
example, that /resource.gif is present). In my case that shouldn't be
a problem, and abstracting it further wouldn't buy me anything. Thanks
again!

chris
 
A

Andrew Thompson


After some further testing, I discovered the real
problem was not /specifically/ those methods, but the
nature of 'this'. In my attempts to 'load resources
in main' (where I first encountered the problem) I
was using something like..

JFrame f = new JFrame();
URL iconUrl = f.getClass().get..

This will not work, since the JFrame was loaded by the
system classloader, and that classloader was not intended
for resources. If you create a *custom* object in main(),
it should be usable to locate resources using either set
of methods.
 
M

markspace

Andrew said:
If you create a *custom* object in main(),
it should be usable to locate resources using either set
of methods.


Well, if you want system resources, you load those with one of the
system class loaders. If you want a resource from your application,
reference your application.

In Main, you might want to start off with the class for Main:

class Main {
public static void main( String... args ) {
URL url = Main.class.getResource( ...
}
}

If you're in a different class, you should probably reference that
class, or use the appropriate class if the resources is local to some
other class.

class OtherClass {
public void someMethod() {
URL url = YetAnotherClass.class.getResource( ...
}
}

The OP actually has a good point. I think "Law of Demeter" is wrong
here, but from a testability standpoint injecting resources into the
class rather than assuming they're at some global location (the leading
slash in "/resource" makes it a global resource, not part of any class)
is probably a better idea.
 
A

Andrew Thompson

Well, if you want system resources, you load those with one of the
system class loaders.  If you want a resource from your application,
reference your application.

I think that sums it up well. (snipped examples)
 
J

John B. Matthews

Andrew Thompson said:
After some further testing, I discovered the real
problem was not /specifically/ those methods, but the
nature of 'this'. In my attempts to 'load resources
in main' (where I first encountered the problem) I
was using something like..

JFrame f = new JFrame();
URL iconUrl = f.getClass().get..

This will not work, since the JFrame was loaded by the
system classloader, and that classloader was not intended
for resources. If you create a *custom* object in main(),
it should be usable to locate resources using either set
of methods.

Thanks for clarifying this. I had misunderstood at the time, and
subsequently forgotten all about, this later discussion:

<http://groups.google.com/group/comp.lang.java.programmer/msg/
8b764d978e73122e>

In that thread, and in the example below, getSystemResource() fails when
run via jnlp, while it works fine as an application. Instead of

URL url = ClassLoader.getSystemResource(helpText);

I should do one of these:

URL url = this.getClass().getClassLoader().getResource(helpText);
URL url = RCHelp.class.getClassLoader().getResource(helpText);

I one preferable to the other?

<http://robotchase.svn.sourceforge.net/viewvc/robotchase/trunk/src/
org/gcs/robot/RCHelp.java>

I agree with markspace about the LoD not applying.
 

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

Forum statistics

Threads
473,982
Messages
2,570,189
Members
46,735
Latest member
HikmatRamazanov

Latest Threads

Top