Cache being halfway final - java

I have a small question i came up with reading this thread:
Why are static variables considered evil?
In my application i have a really massive amount of lets say configuration variables. E.g. fonts, colors, cached images, ... As most of them never change i considered them being static. Nevertheless my application is an applet and as the client has been executing the applet once, some of these static information may change, as a given configuration may have been changed. Therefore lets say this kind of data is used to change rarely, but is not considered being final.
In addition as the amount of information i handle that way is huge, i mapped them onto own Enums like that:
public enum Fonts {
COLOR_CHOOSER, MAP_META_DATA;
private Font localFont;
public Font getValue() {
return localFont;
}
private void setValue(Font newFont) {
localFont = newFont;
}
}
protected static void initFonts() {
Fonts.COLOR_CHOOSER.setValue(new Font("Arial", Font.PLAIN, 15));
Fonts.MAP_META_DATA.setValue(font_race.deriveFont(Font.BOLD, 11));
}
By using enums like that, I was able to identify the value I am looking for pretty easily, while i can maintain all of them at just one place.
Someone may say, since this is static, I could alternatively place them within the objects, they are used in anyway. Nevertheless, I considered the current behavior being more easy to read.
Besides of that the initFonts() method becomes replaced in future by a mapping method, which gets the currently hard coded values from an external source like json or xml. Therefore if i work OO this would mean to forward any of the incoming data to the corresponding objects, which I consider of being NOT easy to read.
To come up with my question:
How would somebody of you map/cache halfway final parameters (I also considered having a hashmap with an enum as key value). E.g for Images, Fonts, Colors, pixel margins, etc. Is the way i am using these enums appropriate or may I consider them being evil, since they are static? If yes - what would be an appropriate way, which keeps being easy to read and easy to maintain?!
I considered my solution of being a possible way to go, but were going to rethink the whole design after reading the above mentioned thread.
Thanks a lot for any advice.
Kind regards.

Enums are alright to represent static final data, which is why I don't think it's proper to have such an initFonts() method, modifying the content of the enum values, even if it's using a private method. What you should have is more like:
public enum Fonts {
COLOR_CHOOSER(new Font("Arial", Font.PLAIN, 15)),
MAP_META_DATA(new Font("Arial", Font.BOLD, 11)); // No reference to font_race, of course
private final Font localFont;
private Fonts(Font font) {
localFont = font;
}
public Font getValue() {
return localFont;
}
}
I don't see why you say that your kind of configuration data is not final: it doesn't seem to change while the application is running.
However, if the runtime initialization needs to load values that may change, as opposed to being determined at compilation time, an enum may not be the correct model anymore, if it's unable to initialize its values itself in a simple manner (calling too much stuff from the static initializer of an enum might not be the best think to do). In that case, I'd replace the direct use of enums by an intermediate service, for example holding an immutable Map<Fonts, Font> (which might be an EnumMap<Fonts, Font> wrapped with Collections.unmodifiableMap()) initialized once and for all using the proper method, and instead of calling Fonts.COLOR_CHOOSER.getValue(), you'd call FontService.getFont(Fonts.COLOR_CHOOSER).

Related

What is wrong in sharing Mutable State? [duplicate]

This question already has answers here:
How shall we write get method, so that private fields don't escape their intended scope? [duplicate]
(2 answers)
Closed 3 years ago.
In Java Concurrency in Practice chapter # 3 author has suggested not to share the mutable state. Further he has added that below code is not a good way to share the states.
class UnsafeStates {
private String[] states = new String[] {
"AK", "AL"
};
public String[] getStates() {
return states;
}
}
From the book:
Publishing states in this way is problematic because any caller can modify its contents. In this case, the states array has escaped its intended scope, because what was supposed to be private state has been effectively made public.
My question here is: we often use getter and setters to access the class level private mutable variables. if it is not the correct way, what is the correct way to share the state? what is the proper way to encapsulate states ?
For primitive types, int, float etc, using a simple getter like this does not allow the caller to set its value:
someObj.getSomeInt() = 10; // error!
However, with an array, you could change its contents from the outside, which might be undesirable depending on the situation:
someObj.getSomeArray()[0] = newValue; // perfectly fine
This could lead to problems where a field is unexpectedly changed by other parts of code, causing hard-to-track bugs.
What you can do instead, is to return a copy of the array:
public String[] getStates() {
return Arrays.copyOf(states, states.length);
}
This way, even the caller changes the contents of the returned array, the array held by the object won't be affected.
With what you have it is possible for someone to change the content of your private array just through the getter itself:
public static void main(String[] args) {
UnsafeStates us = new UnsafeStates();
us.getStates()[0] = "VT";
System.out.println(Arrays.toString(us.getStates());
}
Output:
[VT, AR]
If you want to encapsulate your States and make it so they cannot change then it might be better to make an enum:
public enum SafeStates {
AR,
AL
}
Creating an enum gives a couple advantages. It allows exact vales that people can use. They can't be modified, its easy to test against and can easily do a switch statement on it. The only downfall for going with an enum is that the values have to be known ahead of time. I.E you code for it. Cannot be created at run time.
This question seems to be asked with respect to concurrency in particular.
Firstly, of course, there is the possibility of modifying non-primitive objects obtained via simple-minded getters; as others have pointed out, this is a risk even with single-threaded programs. The way to avoid this is to return a copy of an array, or an unmodifiable instance of a collection: see for example Collections.unmodifiableList.
However, for programs using concurrency, there is risk of returning the actual object (i.e., not a copy) even if the caller of the getter does not attempt to modify the returned object. Because of concurrent execution, the object could change "while he is looking at it", and in general this lack of synchronization could cause the program to malfunction.
It's difficult to turn the original getStates example into a convincing illustration of my point, but imagine a getter that returns a Map instead. Inside the owning object, correct synchronization may be implemented. However, a getTheMap method that returns just a reference to the Map is an invitation for the caller to call Map methods (even if just map.get) without synchronization.
There are basically two options to avoid the problem: (1) return a deep copy; an unmodifiable wrapper will not suffice in this case, and it should be a deep copy otherwise we just have the same problem one layer down, or (2) do not return unmediated references; instead, extend the method repertoire to provide exactly what is supportable, with correct internal synchronization.

Unit testing code that relies on constant values

Consider the following (totally contrived) example:
public class Length {
private static final int MAX_LENGTH = 10;
private final int length;
public Length(int length) {
if (length > MAX_LENGTH)
throw new IllegalArgumentException("Length too long");
this.length = length;
}
}
I would like to test that this throws an exception when called with a length greater than MAX_LENGTH. There are a number of ways this can be tested, all with disadvantages:
#Test(expected = IllegalArgumentException.class)
public void testMaxLength() {
new Length(11);
}
This replicates the constant in the testing case. If MAX_LENGTH becomes smaller this will silently no longer be an edge case (though clearly it should be paired with a separate case to test the other side of the edge). If it becomes larger this will fail and need to be changed manually (which might not be a bad thing).
These disadvantages can be avoided by adding a getter for MAX_LENGTH and then changing the test to:
new Length(Length.getMaxLength());
This seems much better as the test does not need to be changed if the constant changes. On the other hand it is exposing a constant that would otherwise be private and it has the significant flaw of testing two methods at once - the test might give a false positive if both methods are broken.
An alternative approach is to not use a constant at all but, rather, inject the dependency:
interface MaxLength {
int getMaxLength();
}
public class Length {
public static void setMaxLength(MaxLength maxLength);
}
Then the 'constant' can be mocked as part of the test (example here using Mockito):
MaxLength mockedLength = mock(MaxLength.class);
when(mokedLength.getMaxLength()).thenReturn(17);
Length.setMaxLength(mockedLength);
new Length(18);
This seems to be adding a lot of complexity for not a lot of value (assuming there's no other reason to inject the dependency).
At this stage my preference is to use the second approach of exposing the constants rather than hardcoding the values in the test. But this does not seem ideal to me. Is there a better alternative? Or is the lack of testability of these cases demonstrating a design flaw?
As Tim alluded to in the comments, your goal is to make sure that your software behaves according to the specifications. One such specification might be that the maximum length is always 10, at which point it'd be unnecessary to test a world where length is 5 or 15.
Here's the question to ask yourself: How likely is it that you'll want to use your class with a different value of the "constant"? I've quoted "constant" here because if you vary the value programmatically, it's not really a constant at all, is it? :)
If your value will never ever change, you could not use a symbolic constant at all, just comparing to 10 directly and testing based on (say) 0, 3, 10, and 11. This might make your code and tests a little hard to understand ("Where did the 10 come from? Where did the 11 come from?"), and will certainly make it hard to change if you ever do have reason to vary the number. Not recommended.
If your value will probably never change, you could use a private named constant (i.e. a static final field), as you have. Then your code will be easy enough to change, though your tests won't be able to automatically adjust the way your code would.
You could also relax to package-private visibility, which would be available to tests in the same package. Javadoc (e.g. /** Package-private for testing. */) or documentation annotations (e.g. #VisibleForTesting) may help make your intentions clear. This is a nice option if your constant value is intended to be opaque and unavailable outside of your class, like an URL template or authentication token.
You could even make it a public constant, which would be available to consumers of your class as well. For your example of a constant Length, a public static final field is probably best, on the assumption that other pieces of your system may want to know about that (e.g. for UI validation hints or error messages).
If your value is likely to change you could accept it per-instance, as in new Length(10) or new Length().setMaxLength(10). (I consider the former to be a form of dependency injection, counting the constant integer as a dependency.) This is also a good idea if you wanted to use a different value in tests, such as using a maximum length of 2048 in production but testing against 10 for practicality's sake. To make a flexible length validator, this option is probably a good upgrade from a static final field.
Only if your value is likely to change during your instance's lifetime would I bother with a DI-style value provider. At that point, you can query the value interactively, so it doesn't behave at all like a constant. For "length", that'd be obvious overkill, but maybe not for "maximum allowed memory", "maximum simultaneous connections", or some other pseudo-constants like that.
In short, you'll have to decide how much control you need, and then you can pick the most straightforward choice from there; as a "default", you may want to make it a visible field or constructor parameter, as those tend to have good balance of simplicity and flexibility.

How to use ThreadLocal (specifically with Servlets)?

I have a web application that is using a framework where I have to implement an interface named Plot:
interface Plot {
Image getImage();
String getTitle();
}
I know the framework calls the getImage() before the getTitle(). In some cases, I need the results from the image generation in order to create the title.
I know if I do something naive like this:
class MyNaivePlot implements Plot {
private String title;
public Plot getImage() {
title = "...";
}
public String getTitle() { return title; }
}
Then I could introduce a race condition. It seems I can fix this by using a ThreadLocal but I haven't seen enough examples to know if my solution is correct (and these sorts of things are hard to test with certainty). So here's what I've come up with:
class MyThreadLocalPlot implements Plot {
private ThreadLocal<String> title = new ThreadLocal<String>();
public Plot getImage() {
title.set("...");
}
public String getTitle() {
return title.get();
}
}
Is this sufficient? Am I using ThreadLocal correctly? Note that I only need the title to hang around long enough until it is called for by getTitle(). I don't care what it's value is after that nor before getImage() is called.
Also note that I believe the framework "long lives" the MyPlot object, and a new one isn't created for each request / thread, otherwise this would be a non-issue.
Thanks!
To directly answer your question - it sounds ok.
However, I would consider some additional points:
(1) If you have a hook for a beginning/end of request - you might want to clear the thread local at the end of each such request (e.g. if it's a servlet I'd use a filter). That's for two reasons: release it for the garbage collection, and for cases of errors (so that if the next request runs into some parsing error, it will see an empty image and not the previous user's).
(2) Make sure your framework indeed guarantees a single thread (and same machine) during those 2 requests. Perhaps also check if it's going to work on upcoming versions, and on horizontal scaling/clusters.
(3) As a side note, one might also consider other solutions - e.g. a cache (which would help you as a side effect). Obviously this requires some though as to cache size, periodical clearing/updating etc.
You code is quite right; you don't have a setter method but I guess there is a typo and instead of getImage you want to write setTitle().
threadLocal has also a remove method that you should invoke when you don't need the title attribute anymore. You could find some usage examples here and here
Before deploying a ThreadLocal based version of Plot I suggest you to check if your framework create one o or more instances; simply create a regolare class with a counter and increase the counter value in the get method; you can log it to see how the counter value changes with different calls. If you use a logging framework such as log4j or logback I suggest to put the thread name in the log so you can check how/if the counter value changes with different checks.
I also suggest you to test it with multiple clients concurrently, if you have a "serial client" you may end up using always the same server thread if you are using a dedicated test instance.

Java static methods pros & cons

I havent used a lot of static methods before, but just recently I tend to use more of them. For example if I want to set a boolean flag in a class, or acess one without the need to pass the actual object through classes.
For example:
public class MainLoop
{
private static volatile boolean finished = false;
public void run()
{
while ( !finished )
{
// Do stuff
}
}
// Can be used to shut the application down from other classes, without having the actual object
public static void endApplication()
{
MainLoop.finished = true;
}
}
Is this something I should avoid? Is it better to pass a object so you can use the objects methods? Does the boolean finished counts as a global now, or is it just as safe?
A problem with using a static variable in this case is that if you create two (or more) instances of MainLoop, writing code that looks like it is shutting down only one of the instances, will actually shut down both of them:
MainLoop mainLoop1 = new MainLoop();
MainLoop mainLoop2 = new MainLoop();
new Thread(mainLoop1).start();
new Thread(mainLoop2).start();
mainLoop1.finished = true; // static variable also shuts down mainLoop2
This is just one reason (amongst many) for choosing to not use static variables. Even if your program today only creates one MainLoop, it is possible that in the future you may have reason to create many of them: for unit testing, or to implement a cool new feature.
You may think "if that ever happens, I'll just refactor the program to use member variables instead of static variables." But it's generally more efficient to pay the cost up front, and bake modular design into the program from the start.
There's no question that statics often make a quick and dirty program easier to write. But for important / complex code that you intend to test, maintain, grow, share, and use for years to come, static variables are generally recommended against.
As other answers to this question have noted, a static variable is a kind of global variable. And there's lots of information about why (generally) global variables are bad.
Yes, passing objects around is better. Using a singleton or static methods makes OO programming look like procedural programming. A singleton is somewhat better because you can at least make it implement interfaces or extend an abstract class, but it's usually a design smell.
And mixing instance methods with static variables like you're doing is even more confusing: you could have several objects looping, but you stop all of them at once because they all stop when a static variable changes.
Is this something i should avoid?
In general, yes. Statics represent global state. Global state is hard to reason about, hard to test in isolation, and generally has higher thread-safety requirements.
If I want to test what happens to an object in a certain state, I can just create the object, put it into that state, perform my tests, and let it get garbage collected.
If I want to test what happens to global state, I need to make sure I reset it all at the end of my test (or possibly at the start of every test). The tests will now interfere with each other if I'm not careful about doing that.
Of course, if the static method doesn't need to affect any state - i.e. if it's pure - then it becomes somewhat better. At that point all you're losing is the ability to replace that method implementation, e.g. when testing something that calls it.
In general, by making finished static like that you create a situation where there can only be one instance of your MainLoop class executing run at any one time. If there is more than one instance then setting finished will end them all -- not what is usually desired.
However, in this particular scenario, where you want to "end application", presumably meaning you want to end all instances of MainLoop, the approach may be justified.
However, the number of situations where this approach may be merited are few, and a "cleaner" way to handle this scenario would be to keep a static list of instances and work through the list, setting the instance variable finished in each instance. This allows you to also end individual instances, gives you a natural count of existing instances, etc.

Java instance variables vs. local variables

I'm in my first programming class in high school. We're doing our end of the first semester project.
This project only involves one class, but many methods. My question is about best practice with instance variables and local variables. It seems that it would be much easier for me to code using almost only instance variables. But I'm not sure if this is how I should be doing it or if I should be using local variables more (I would just have to have methods take in the values of local variables a lot more).
My reasoning for this is also because a lot of times I'll want to have a method return two or three values, but this is of course not possible. Thus it just seems easier to simply use instance variables and never having to worry since they are universal in the class.
I haven't seen anyone discuss this so I'll throw in more food for thought. The short answer/advice is don't use instance variables over local variables just because you think they are easier to return values. You are going to make working with your code very very hard if you don't use local variables and instance variables appropriately. You will produce some serious bugs that are really hard to track down. If you want to understand what I mean by serious bugs, and what that might look like read on.
Let's try and use only instance variables as you suggest to write to functions. I'll create a very simple class:
public class BadIdea {
public Enum Color { GREEN, RED, BLUE, PURPLE };
public Color[] map = new Colors[] {
Color.GREEN,
Color.GREEN,
Color.RED,
Color.BLUE,
Color.PURPLE,
Color.RED,
Color.PURPLE };
List<Integer> indexes = new ArrayList<Integer>();
public int counter = 0;
public int index = 0;
public void findColor( Color value ) {
indexes.clear();
for( index = 0; index < map.length; index++ ) {
if( map[index] == value ) {
indexes.add( index );
counter++;
}
}
}
public void findOppositeColors( Color value ) {
indexes.clear();
for( index = 0; i < index < map.length; index++ ) {
if( map[index] != value ) {
indexes.add( index );
counter++;
}
}
}
}
This is a silly program I know, but we can use it to illustrate the concept that using instance variables for things like this is a tremendously bad idea. The biggest thing you'll find is that those methods use all of the instance variables we have. And it modifies indexes, counter, and index every time they are called. The first problem you'll find is that calling those methods one after the other can modify the answers from prior runs. So for example, if you wrote the following code:
BadIdea idea = new BadIdea();
idea.findColor( Color.RED );
idea.findColor( Color.GREEN ); // whoops we just lost the results from finding all Color.RED
Since findColor uses instance variables to track returned values we can only return one result at a time. Let's try and save off a reference to those results before we call it again:
BadIdea idea = new BadIdea();
idea.findColor( Color.RED );
List<Integer> redPositions = idea.indexes;
int redCount = idea.counter;
idea.findColor( Color.GREEN ); // this causes red positions to be lost! (i.e. idea.indexes.clear()
List<Integer> greenPositions = idea.indexes;
int greenCount = idea.counter;
In this second example we saved the red positions on the 3rd line, but same thing happened!?Why did we lose them?! Because idea.indexes was cleared instead of allocated so there can only be one answer used at a time. You have to completely finish using that result before calling it again. Once you call a method again the results are cleared and you lose everything. In order to fix this you'll have to allocate a new result each time so red and green answers are separate. So let's clone our answers to create new copies of things:
BadIdea idea = new BadIdea();
idea.findColor( Color.RED );
List<Integer> redPositions = idea.indexes.clone();
int redCount = idea.counter;
idea.findColor( Color.GREEN );
List<Integer> greenPositions = idea.indexes.clone();
int greenCount = idea.counter;
Ok finally we have two separate results. The results of red and green are now separate. But, we had to know a lot about how BadIdea operated internally before the program worked didn't we? We need to remember to clone the returns every time we called it to safely make sure our results didn't get clobbered. Why is the caller forced to remember these details? Wouldn't it be easier if we didn't have to do that?
Also notice that the caller has to use local variables to remember the results so while you didn't use local variables in the methods of BadIdea the caller has to use them to remember results. So what did you really accomplish? You really just moved the problem to the caller forcing them to do more. And the work you pushed onto the caller is not an easy rule to follow because there are some many exceptions to the rule.
Now let's try doing that with two different methods. Notice how I've been "smart" and I reused those same instance variables to "save memory" and kept the code compact. ;-)
BadIdea idea = new BadIdea();
idea.findColor( Color.RED );
List<Integer> redPositions = idea.indexes;
int redCount = idea.counter;
idea.findOppositeColors( Color.RED ); // this causes red positions to be lost again!!
List<Integer> greenPositions = idea.indexes;
int greenCount = idea.counter;
Same thing happened! Damn but I was being so "smart" and saving memory and the code uses less resources!!! This is the real peril of using instance variables like this is calling methods is order dependent now. If I change the order of the method calls the results are different even though I haven't really changed the underlying state of BadIdea. I didn't change the contents of the map. Why does the program yield different results when I call the methods in different order?
idea.findColor( Color.RED )
idea.findOppositeColors( Color.RED )
Produces a different result than if I swapped those two methods:
idea.findOppositeColors( Color.RED )
idea.findColor( Color.RED )
These types of errors are really hard to track down especially when those lines aren't right next to each other. You can completely break your program by just adding a new call in anywhere between those two lines and get wildly different results. Sure when we're dealing with small number of lines it's easy to spot errors. But, in a larger program you can waste days trying to reproduce them even though the data in the program hasn't changed.
And this only looks at single threaded problems. If BadIdea was being used in a multi-threaded situation the errors can get really bizarre. What happens if findColors() and findOppositeColors() is called at the same time? Crash, all your hair falls out, Death, space and time collapse into a singularity and the universe is swallows up? Probably at least two of those. Threads are probably above your head now, but hopefully we can steer you away from doing bad things now so when you do get to threads those bad practices don't cause you real heartache.
Did you notice how careful you had to be when calling the methods? They overwrote each other, they shared memory possibly randomly, you had to remember the details of how it worked on the inside to make it work on the outside, changing the order in which things were called produce very big changes in the next lines down, and it only could only work in a single thread situation. Doing things like this will produce really brittle code that seems to fall apart whenever you touch it. These practices I showed contributed directly to the code being brittle.
While this might look like encapsulation it is the exact opposite because the technical details of how you wrote it have to be known to the caller. The caller has to write their code in a very particular way to make their code work, and they can't do it without knowing about the technical details of your code. This is often called a Leaky Abstraction because the class is suppose to hide the technical details behind an abstraction/interface, but the technical details leak out forcing the caller to change their behavior. Every solution has some degree of leaky-ness, but using any of the above techniques like these guarantees no matter what problem you are trying to solve it will be terribly leaky if you apply them. So let's look at the GoodIdea now.
Let's rewrite using local variables:
public class GoodIdea {
...
public List<Integer> findColor( Color value ) {
List<Integer> results = new ArrayList<Integer>();
for( int i = 0; i < map.length; i++ ) {
if( map[index] == value ) {
results.add( i );
}
}
return results;
}
public List<Integer> findOppositeColors( Color value ) {
List<Integer> results = new ArrayList<Integer>();
for( int i = 0; i < map.length; i++ ) {
if( map[index] != value ) {
results.add( i );
}
}
return results;
}
}
This fixes every problem we discussed above. I know I'm not keeping track of counter or returning it, but if I did I can create a new class and return that instead of List. Sometimes I use the following object to return multiple results quickly:
public class Pair<K,T> {
public K first;
public T second;
public Pair( K first, T second ) {
this.first = first;
this.second = second;
}
}
Long answer, but a very important topic.
Use instance variables when it's a core concept of your class. If you're iterating, recursing or doing some processing, then use local variables.
When you need to use two (or more) variables in the same places, it's time to create a new class with those attributes (and appropriate means to set them). This will make your code cleaner and help you think about problems (each class is a new term in your vocabulary).
One variable may be made a class when it is a core concept. For example real-world identifiers: these could be represented as Strings, but often, if you encapsulate them into their own object they suddenly start "attracting" functionality (validation, association to other objects, etc.)
Also (not entirely related) is object consistency - an object is able to ensure that its state makes sense. Setting one property may alter another. It also makes it far easier to alter your program to be thread-safe later (if required).
Local variables internal to methods are always prefered, since you want to keep each variable's scope as small as possible. But if more than one method needs to access a variable, then it's going to have to be an instance variable.
Local variables are more like intermediate values used to reach a result or compute something on the fly. Instance variables are more like attributes of a class, like your age or name.
The easy way: if the variable must be shared by more than one method, use instance variable, otherwise use local variable.
However, the good practice is to use as more local variables as possible. Why? For your simple project with only one class, there is no difference. For a project that includes a lot of classes, there is big difference. The instance variable indicates the state of your class. The more instance variables in your class, the more states this class can have and then, the more complex this class is, the hard the class is maintained or the more error prone your project might be. So the good practice is to use as more local variable as possible to keep the state of the class as simple as possible.
Short story: if and only if a variable needs to be accessed by more than one method (or outside of the class), create it as an instance variables. If you need it only locally, in a single method, it has to be a local variable.
Instance variables are more costly than local variables.
Keep in mind: instance variables are initialized to default values while local variables are not.
Declare variables to be scoped as narrowly as possible. Declare local variables first. If this isn't sufficient, use instance variables. If this isn't sufficient, use class (static) variables.
I you need to return more than one value return a composite structure, like an array or an object.
Try to think about your problem in terms of objects. Each class represents a different type of object. Instance variables are the pieces of data that a class needs to remember in order to work, either with itself or with other objects. Local variables should just be used intermediate calculations, data that you don't need to save once you leave the method.
Try not to return more than one value from your methods in first place. If you can't, and in some cases you really can't, then I would recommend encapsulating that in a class. Just in last case I would recommend changing another variable inside your class (an instance variable). The problem with the instance variables approach is that it increases side effects - for example, you call method A in your program and it modifies some instance(s) variable(s). Over time, that leads to increased complexity in your code and maintenance becomes harder and harder.
When I have to use instance variables, I try to make then final and initialize then in the class constructors, so side effects are minimized. This programming style (minimizing the state changes in your application) should lead to better code that is easier to maintain.
Generally variables should have minimal scope.
Unfortunately, in order to build classes with minimized variable scope, one often needs to do a lot of method parameter passing.
But if you follow that advice all the time, perfectly minimizing variable scope, you
may end up with a lot of redundancy and method inflexibility with all the required objects passed in and out of methods.
Picture a code base with thousands of methods like this:
private ClassThatHoldsReturnInfo foo(OneReallyBigClassThatHoldsCertainThings big,
AnotherClassThatDoesLittle little) {
LocalClassObjectJustUsedHere here;
...
}
private ClassThatHoldsReturnInfo bar(OneMediumSizedClassThatHoldsCertainThings medium,
AnotherClassThatDoesLittle little) {
...
}
And, on the other hand, imagine a code base with lots of instance variables like this:
private OneReallyBigClassThatHoldsCertainThings big;
private OneMediumSizedClassThatHoldsCertainThings medium;
private AnotherClassThatDoesLittle little;
private ClassThatHoldsReturnInfo ret;
private void foo() {
LocalClassObjectJustUsedHere here;
....
}
private void bar() {
....
}
As code increases, the first way may minimize variable scope best, but can easily lead to a lot of method parameters being passed around. The code will usually be more verbose and this can lead to a complexity as one refactors all these methods.
Using more instance variables can reduce the complexity of lots of method parameters being passed around and can give a flexibility to methods when you are frequently reorganizing methods for clarity. But it creates more object state that you have to maintain. Generally the advice is to do the former and refrain from the latter.
However, very often, and it may depend on the person, one can more easily manage state complexity compared with the thousands of extra object references of the first case. One may notice this when business logic within methods increases and organization needs to change to keep order and clarity.
Not only that. When you reorganize your methods to keep clarity and make lots of method parameter changes in the process, you end up with lots of version control diffs which is not so good for stable production quality code. There is a balance. One way causes one kind of complexity. The other way causes another kind of complexity.
Use the way that works best for you. You will find that balance over time.
I think this young programmer has some insightful first impressions for low maintenance code.
Use instance variables when
If two functions in the class need the same value, then make it an instance variable
or
If the state is not expected to change, make it an instance variable. For example: immutable object, DTO, LinkedList, those with final variables
or
If it is an underlying data on whom actions are performed. For example: final in arr[] in the PriorityQueue.java source code file
or
Even if it is used only once and state is expected to change, make it an instance if it is used only once by a function whose parameter list should be empty. For example: HTTPCookie.java Line: 860 hashcode() function uses 'path variable'.
Similarly, use a local variable when none of these conditions match, specifically if the role of the variable would end after the stack is popped off. For example: Comparator.compare(o1, o2);

Categories