This is a mobile version, full one is here.
Yegor Bugayenko
6 July 2015
Public Static Literals ... Are Not a Solution for Data Duplication
I have a new String(array,"UTF-8")
in one place and exactly
the same code in another place in my app. Actually, I may have
it in many places. And every time, I have to use that "UTF-8"
constant
in order to create a String
from a byte array. It would be very
convenient to define it once somewhere and reuse it, just like Apache Commons
is doing; see CharEncoding.UTF_8
(There are many other static literals there).
These guys are setting a bad example! public
static
“properties” are as bad as
utility classes.
Here is what I’m talking about, specifically:
package org.apache.commons.lang3;
public class CharEncoding {
public static final String UTF_8 = "UTF-8";
// some other methods and properties
}
Now, when I need to create a String
from a byte array, I use this:
import org.apache.commons.lang3.CharEncoding;
String text = new String(array, CharEncoding.UTF_8);
Let’s say I want to convert a String
into a byte array:
import org.apache.commons.lang3.CharEncoding;
byte[] array = text.getBytes(CharEncoding.UTF_8);
Looks convenient, right? This is what the designers of Apache Commons think (one of the most popular but simply terrible libraries in the Java world). I encourage you to think differently. I can’t tell you to stop using Apache Commons, because we just don’t have a better alternative (yet!). But in your own code, don’t use public static properties—ever. Even if this code may look convenient to you, it’s a very bad design.
The reason why is very similar to
utility classes
with public static
methods—they are unbreakable hard-coded dependencies. Once you
use that CharEncoding.UTF_8
, your object starts to depend on this
data, and its user (the user of your object) can’t break this dependency. You may say that
this is your intention, in the case of a "UTF-8"
constant—to make
sure that Unicode is specifically and exclusively being used. In this particular
example, this may be true, but look at it from a more global perspective.
Let me show you the alternative I have in mind before we continue.
Here is what I’m suggesting instead to convert a byte array
into a String
:
String text = new UTF8String(array);
It’s pseudo-code, since Java designers made class String
final and we
can’t really extend it and create UTF8String
, but you get the idea. In the real
world, this would look like this:
String text = new UTF8String(array).toString();
As you see, we encapsulate the “UTF-8” constant somewhere inside the class
UTF8String
, and its users have no idea how exactly this “byte array to string”
conversion is happening.
By introducing UTF8String
, we solved the problem of “UTF-8”
literal duplication. But we did it in a proper object-oriented way—we encapsulated the functionality inside a class and let everybody
instantiate its objects and use them. We resolved the problem of functionality
duplication, not just data duplication.
Placing data into one shared place (CharEncoding.UTF_8
)
doesn’t really solve the duplication problem; it actually makes it worse, mostly
because it encourages everybody to duplicate functionality using the same
piece of shared data.
My point here is that every time you see that you have some data duplication
in your application, start thinking about the functionality you’re duplicating.
You will easily find the code that is repeated again and again. Make a new
class for this code and place the data there, as a private
property (or private
static
property). That’s how you will improve your design and truly get rid
of duplication.
PS. You can use a method instead of a class, but not a static literal.
PPS. You can also use enums, but only this way.
How do you get rid of the duplication of magic numbers (it's Ruby)? #elegantobjects
— Yegor Bugayenko (@yegor256) January 26, 2020
class Time
def hours
msec / (60 * 60 * 1000)
end
def days
msec / (24 * 60 * 60 * 1000)
end
end