When I started programming in Java, I took private and public as fairly necessary access levels. It's only in the last couple of years that I've realised that you can achieve better encapsulation than private - by making the variable disappear, or at least not have a name in the relevant scope.
As an exercise, I thought I'd take a look at my codebase and see how it is impacted if I get rid of private. But of course, I didn't want to expose implementation details either, so I did it with the power of thought, a scarce resource.
Low-hanging fruit first: Private constructors. Sometimes these are used to prevent instantiation of a utility class. IDEA actually warns me if I instantiate a utility class, so I don't need the constructor. It's a no-brainer to delete unused code.
Ok, now private fields with get/set methods. There are two kinds - the first is the simple problem of having a private field for no reason - the get and set methods just pass through to the variable with no checking. Make the variable public and get rid of the methods. IDEA helps me with this - Ctrl-Alt-N inlines a method.
Private fields with more interesting get/set methods. In my code, so far, I don't have any interesting get methods, just some set methods that do some work after the value has been set. This isn't an original idea:
Make a Property interface, using generics (no evil casting) with get, set and addPropertyListener. Provide an implementation as a static factory method. Now change the private field to be a Property, adding the logic from the set method to the Property via a PropertyListener. Fix the get and set method so that they delegate to the Property, make sure everything compiles, then inline the get and set methods. If your get and set methods are more interesting than mine, then perhaps you'll need a more advanced Property type, e.g., addPrecondition, addPostcondition, etc.
Now for the fruit at the top of the tree. I use a Map to allocate IDs for Cables (it's a network simulator, and the IDs are useful for the event log). I don't want to expose the Map, but I do want to expose the operation I have on it - cableIDFor(Cable cable). How can I do that without using private? The Map has to persist between invocations, so I can't create it inside cableIDFor.
I'll make a field, called cableIDFor. It will hold an object that has one method. That method will take in a Cable and return an Integer. In other words, it's a Function<Cable,Integer>.
public final Function>Cable,Integer> cableIDFor=
new Function<Cable,Integer>()
{
final Map<Integer,Cable> cableIDs=hashMap();
public Integer invoke(Cable cable)
{
for (final Map.Entry<Integer, Cable> entry:
cableIDs.entrySet())
if (Caster.equalT(entry.getValue(), cable))
return entry.getKey();
for (int a=0;;a++)
if (!cableIDs.containsKey(a))
{
cableIDs.put(a, cable);
return a;
}
}
}
You call that like so: cableIDFor.invoke(cable), and of course now you get it as a Function for free, so if you want to pass it to some other method, you can. Oh, and yes, it's not a pure function, but I know that and treat it accordingly.
Thankfully I only call cableIDFor in one place, so I don't mind that IDEA doesn't provide an automated refactoring for this. Perhaps the structural search and replace will do it.
One more, a small one. I have a private field in a class that implements IncomingPacketListener - it's a StringBuffer, used for unit testing only. Easy solution, make the class anonymous and the name of the StringBuffer variable disappears from relevance.
I haven't lost any encapsulation in any of this, so I'm quite pleased. This all makes me realise that it isn't a problem that Common Lisp's OO support doesn't include private. That might actually be a good thing.
I expect a lot of people will be thinking "No! You're losing flexibility", but that's just not true. None of this code is external - I'm not actually impacting anything outside my own codebase. Externally-facing code gets put in separate projects that I normally leave closed so that I don't mess with it by accident. So, suppose that I decide that one of my public fields does need some validation - I can use IDEA's refactoring to encapsulate it, and then convert it into a Property or leave it as a private field with get/set. I'm not losing anything.
It's not less readable either. Because I use bits of functional programming in my code, it's actually fairly useful to be able to refer to a 'method' without executing it, which I can do if I convert it to a field that holds a Function. I end up with less anonymous classes this way. Likewise, even for 'normal' programmers, it's useful to be able to refer to a property instead of grabbing its value, for instance, for binding to a graphical object, etc.
14 comments:
"Low-hanging fruit first: Private constructors. Sometimes these are used to prevent instantiation of a utility class. IDEA actually warns me if I instantiate a utility class, so I don't need the constructor. It's a no-brainer to delete unused code."
You are assuming everyone using your code uses IDEA and has it setup to warn about this. If that is a good assumption, then this is good. If you are writing open source code, this isn't a good assumption so it would be bad to delete that constructor. IMHO.
It's not a no-brainer to delete unused code. If you do this, you'd delete error checking because if things go well, it is not used, right? Deleting this constructor assumes programmers will do the right thing so it is like error checking code.
If I couldn't rely on IDEA then I'd do it in some other way - see this blog post. Why wouldn't you have IDEA anyway?
Error checking isn't unused code. You will invoke error checking code as part of normal operation. I would not normally invoke that constructor in normal operation. It is unused code, gaining me only the idea that instantiating that class is pointless.
I'm a smart enough developer that I don't need a private constructor to tell me that - if there are no non-static members then I don't need an instance.
Removing a private constructor is no gain no loss, except for it not being visible. Calling the constructor of a utility class also is no harm, you just end with an instance of no use only having static methods/fields. So, I see no problem here except for the missing documentation part of a private constructor.
To give you an idea of interesting getters: lazy instantiation/loading.
Btw. I missed the property stuff in your example. What's hashmap() doing?
Stefan, I agree about the private constructor, and about interesting getters. My Property interface would cater for those with no problem.
What about the property stuff do you not understand?
hashMap() is somewhat misleading, here's the implementation:
public static <K,V> Map<K,V> hashMap()
{
. . return new LinkedHashMap<K,V>();
}
"What about the property stuff do you not understand?"
I am just missing them in the example you gave. :)
Ghar ... sorry. I just spotted my mistake. :)
"I'm a smart enough developer that I don't need a private constructor to tell me that"
That's great. So you and only you are the only one that uses your code base? Why do you need to have runtime checking? Make everything Object. You are a smart enough developer to use the correct type of object at the right time.
Most of us write code that is used by many developers so making the code prevent honest mistakes is a good thing, so stuff like a private constructor isn't unused code. It is code to prevent someone from introducing bugs when working at 1am. Other programmers, not us, we are too smart to make mistakes like this. :)
I'm not saying that checking isn't necessary. What bugs do private constructors prevent that an IDE inspection checking against instantiating a utility class would not?
What you've done with the cableID map reminds me a lot of javascript programmers emulating private members. That is, returning a closure that captures non-global context.
Good provocative post.
I'm confused... what prevents someone from breaking encapsulation by accessing cableIDFor.cableIDs?
cableIdFor is a Function, and Function only has invoke. I expect there's a reflective way to get at the field, but I'm not interested in preventing reflection.
Try this out:
Object o=new Object()
{
. . public void x(){}
}
o.x(); //won't compile
..whereas:
new Object()
{
. . public void x(){}
}.x(); //will compile.
One more reason to use private is when you have a recursive block of code - at first glance it seems hard to put one of those inside an existing method. You can, though:
new Object()
{
. . public int fac(int x)
. . {
. . . . return x<=1 ? x : x*fac(x);
. . }
}.fac(10);
I think this is one case that won't benefit from closures, because thus far, there seems to be no syntax for a closure invoking itself.
Ah, I see, thanks. BTW, javascript has a syntax for a lambda calling itself -- arguments.callee is available in a function body. Oh, and if you're living in a functional language, there's always the Y-combinator. *ducks*
Interesting thought experiment, for sure. If this were a practical development style in the long haul, then I'd want built-in language features such as properties and closures, since they would reduce the window dressing associated with the code.
re: IDEA, I don't have IDEA, I do have Eclipse. Why? Because the price is right. Still I agree with this sentiment - if you were going to release such code in the wild, you would be imposing your development style on them. Some of it is obvious, but some of it is hidden. As a thought experiment, this is fine, in reality, you would need more common agreement.
More often than not, the cost of clone() outweighs its benefits, so few people use it. Similarly, the benefits of private outweigh its costs, and so everyone uses it.
One last thought: how would the lack of privacy impact VM optimizations?
Fun exercise, perhaps, but what's the point?
The central theme in the points below is: Why pollute the namespace for other programmers? Here 'other programmers' includes yourself, when you are working on a different logical unit. - So no 'this isn't externally facing code' excuses, please.
* I have a constructor with way too many parameters / I like my generics inference when I construct objects / it just feels more natural to go with a static method that calls the constructor / These thingies are meant to be returned by other classes in this package only, so why make the constructor public? Now what? - You can't hide constructors in inner classes.
* I have a state-change function which also needs to do some validity checking. If not valid, throw an exception. The design model of 'I'll let a PropertyChangeListener throw the exception' is just insanely bad design. I'll leave it as an exercise to the reader why that is. An alternative PropertyChangeListener explicitly designed to do validation, that'd be allright with me.
Finally, on the notion of returning less specific types in order to hide public methods, which is really what's happening with your cableForID trick: Cute. Much more verbose than just tossing a 'private' in there, though.
You've convinced me that you can probably design a language with encapsulation that doesn't have access modifiers, but I'm not at all convinced such a language would be an improvement over something simple, such as 'private', 'friends', and 'external', which are roughly analogous to java's 'private', 'module public', and 'public', assuming the module stuff goes through voor java7 and works the way I think it will.
Incidentally, do you do much java programming in your daily routine these days? Just interested.
Post a Comment