Escolar Documentos
Profissional Documentos
Cultura Documentos
Two classes that uses each other are called "coupled". The coupling between
classes can be loose or tight, or somewhere in between. The tightness of a
coupling is not binary. It is not either "loose" or "tight". The degrees of
tightness are continuous, not discrete. You can also characterize
dependencies as "strong" or "weak". A tight coupling leads to strong
dependencies, and a loose coupling leads to weak dependencies, or even no
dependencies in some situations.
As you may know, an InputStream can be obtained from either a File object, a
network Socket, a URLConnection class, a Class object
(Class.getResourceAsStream(String name)), a column in a database via
JDBC etc. Now the CalendarReader is not coupled to the local file system
anymore. It can read calendar event files from many different sources.
Class Dependencies
Interface Dependencies
The more methods an interface has, the less chance there is that developers
will provide their own implementation for that interface, unless they are
required to. Therefore, the more methods an interface has the larger the
probability is that developers will just stick to the default implementation of that
interface. In other words, the larger and more complex an interface becomes,
the tighter it is coupled to its default implementation!
Below is an example of what this means. The code example shows a tree
node for a hierarchical tree structure.
Imagine that you want to be able to count descendents of a given node. At first
you might be tempted to add a countDescendents() method to
the ITreeNode interface. However, if you do so anyone who wants to implement
the ITreeNode interface will also have to implement
the countDescendent() method.
This is not the same as saying that you should never use hidden
dependencies. Hidden dependencies are often the result of providing
sensible defaults. For instance, in this example it may not be a problem:
public MyComponent(){
this.dependency = new MyDefaultImpl();
}
It looks simple, right? And even if the login method needs more parameters,
you don't need to change the calling code.
But the login method now has what I call an "unnecessarily extensive
dependency" on the HttpServletRequest interface. It depends on more than it
needs to carry out its work. The LoginManageronly needs a user name and a
password to lookup a user, but takes a HttpServletRequest as parameter for
the login method. An HttpServletRequest contains a lot more information than
the LoginManager needs.
2. The LoginManager requires the names of the user name and password
parameters to be called "user" and "password". This is also an
unnecessary dependency.
A much better interface for the LoginManager's login method would be:
For a general purpose component any classes belonging to the component (or
API) are "local". The rest of the application is the "context". If a general
purpose component depends on application specific classes, this is called a
"context dependency". Context dependencies are bad because it makes the
general purpose component unusable outside of the application too.
In some situations though, depending on JDK classes is not the best thing to
do. For example, lets say a method needs 4 strings for its configuration. Then
you method takes 4 strings as parameters. An example could be the driver
name, database url, user name and password needed for a database
connection. If all of these 4 strings are always used together, it may be clearer
for the users of that method if you group the 4 strings into a class, and pass
instances of the class around, instead of the 4 strings.
Summary
By now you have seen several different types and characteristics of
dependencies. In general interface dependencies are preferable over class
dependencies. In some situations though, you may find that a class
dependency may be preferable to an interface dependency. Method and field
dependencies can be very useful, but remember that they are also typically
hidden dependencies, and hidden dependencies makes it harder for users of
your component to detect it, and thereby satisfy it.
This text has only described dependencies. It does not tell you what to do
about them. Other texts on this training site will delve into this topic.