Java Advice - UA-ScriptEase/scriptease GitHub Wiki

##Read the Wiki It's gonna save you a lot of time when the documentation is waiting right there. Don't read it all at once, that is, but go and read the relevant sections for the systems you're working with. It's the project's version of "Google it" (in fact, the search in this wiki is pretty good). And, if you find one of those rarer spots with inadequate documentation, write some up while you figure out the system for the next time you or the next person needs a refresher.

##Research first

There's so much stuff in Java that makes your life so much easier that you just don't know about.

-- Abbie Schenk, day 20 of her internship. (Seriously; she said this of her own volition.)

It's important to know what's already been done before you go about trying to do something. Java's got a bunch of little features (like CaretListeners, Swing button Actions, and TransferHandlers) that already do some of the heavy lifting for you. If it's not already in Java core or you don't like their solution, look for a library. Only do it yourself when you can either do it better suited to our situation, or if nobody else has done it before. That said, balance this advice with the knowledge that every new library dependency can get you into a sort of Dependency Hell, so actually think before adding.

Similarly, if a task seems really advanced, like a code generation problem you can't figure out, try asking Duane. It's probably already been solved by some academic in the 80s. I've (remiller) seen this exact thing happen several times already.

Even further, there are a bunch of things in ScriptEase itself that are already there to make your life easier. For example, Abbie Schenk and I (remiller) were doing some work in the code generation system, and we needed a method for getting all of the relevant code blocks. I poked into Context.java and saw that there was a method for doing exactly what we needed. I didn't even know it was there, and at that point I'd been working on this project for years. The same thing goes for more than just methods - the StoryComponent system was built specifically so that you wouldn't have to change it too much to be able to handle whacky cases. Likely if something needs to change, it's how the StoryComponents are used, not the StoryComponents themselves.

##Prefer final Variables Most variables can be made final. If you don't already do this, this is a style thing that really helps out a lot. An argument for using final already exists, so I won't go into too much detail. The big reason why I like it is that it highlights variables that are important. I like to also do my declarations at the top of a method so that I can ignore all that text when I'm reading through to see what a method does.

##Building GUIs

It's about standards. - Dr. Horrible

GUIs in Java are simultaneously easy and difficult. The best way to design GUIs in Java is through a set of factories (yeah, I know the Java factory jokes) whose sole purpose is to build those pesky panels that you so desire. Here's why you shouldn't extend Swing classes to compose some of them together:

  1. It's bad inheritance. This is the the way the Java tutorials do it and it's awful. If you think about it, that way of doing things is wrong twice. It violates IS-A because while, yes, the subclass is extending the superclass, it's not really the same thing. The intention of JPanel is to be a configurable widget. The intention of, say, a StoryToolbarPanel is a ridiculously specialised instance of JPanel that should only be used once and is not really meant to be modified.
  2. Constructor misuse. If the only unique thing happening in the subclass is building a panel in a particular way, that's not a constructor. That's a factory method.
  3. **Class bloat. Extending Swing widgets shifts around all your code so you can't share things between similarly laid-out component easily. You end up with all these encapsulated classes whose entire purpose is to build a widget, which is a similar goal for many of them. It makes reuse awkward.
  4. Confusion of concerns. You remember Separation of Concerns. This is the opposite of that. Extending Swing widgets tends to go hand-in hand with implementing listeners. Those are two separate tasks. One is showing a GUI. The other is responding to it. Make the factory do the first, and either an Action object or a specialised class (perhaps anonymous) listener
  5. It encourages casting and type rigidity. If you know you want the specific subtype, then you cast it, right? But what if we change things down the road? What if that panel gets replaced? Then all this special case code gets broken and needs to be fixed. It's paralysing to have code like that.
  6. Overwhelming data. When debugging, you'll want to view the variables in the little window that eclipse provides. If you extend a swing widget, you get a metric bajillion variables in there, when you only care about two or three. Your life is easier if you contain all that display data away from your program logic.
  7. Swing is weird. Like heebie-jeebies weird. Sometimes, anyways. The less you include it in your hierarchy, the better off your code will be.

There. I hope you're convinced. Yes, there may be some times that we legitimately need a subclass of a Swing widget, and that's really only going to be when we're making a totally new kind of generic widget (like a binding slot). So really, if you find yourself making some GUI, unless it's a custom generic widget, avoid subclassing Swing.

##Use Swing Actions

Wheeeeeeee! - Children on swings

Really. For just about everything clicky. Any button* should have an Action backing it. There is no good reason not to. It's not even easier.

*which, if you look, includes: radio buttons, check boxes, menu items, and vanilla JButton.

The benefits of using actions are:

  1. Consistency. At the time of writing (May 9, 2012), we've used them for all the menu items. It's easier for new programmers to pick up when there's only one way of doing it.
  2. It's what they're for. Swing Actions are designed to be used across multiple buttons that do that same action.
  3. Shared state. Sort of related to the above, actions allow you to share state across multiple buttons that do the same thing. So, if one gets disabled, they all do. Really handy, and removed the fear that you may have forgotten one. This reduces bugs.
  4. Free features. Actions give you mnemonics, hot keys, display text, enabled state, tool tips (and more) all for free. Use 'em. Makes you look really competent for little effort.

So when making a button, just make a new Action object. Mimic the Actions that already exist. There are some other Action classes that are prebuilt to, say, listen to whether a story component is selected. Go poke about. check out the Java tutorials for them. They're cool.

Cool like you.

##Avoid instanceof It's a dirty ten-letter word. Checking the type of something is sometimes a sign of bad design, though sometimes it can also be necessary because the intent of that class hierarchy is to distinguish based on type. If that's the case, use a visitor instead of an instanceof check.

Think of it this way: StoryComponents come in many types. When generating code, we need ScriptEase to behave differently for ScriptIts and KnowIts. Using instanceof checks will work, yes, but what if we add a BehaveIt later? Now we need to find all the places that did an instanceof on each and every one of the StoryComponent types. You can't just pick only one to search by because that type may not have been used everywhere. This system really sucks. It's hard-coding things to types and spreading those references all over.

However, implementing a StoryComponentVisitor interface allows you to just search through the implementations of that interface! Now you can't miss anything because it's all linked together though that visitor's type. Our use of visitors isn't always over a collection: sometimes its just one element, but that's still totally valid. If you notice that there's a grouping of things that could, though, it's probably worth doing. And if there's already a visitor for it, you definitely must use it.

Now, that said, not everything can be done this way (JComponents, for example), or doing so would be overly verbose (checking if something is a ComplexStoryComponent to see if it can accept children). Use your judgement.

Avoid Listener GC

LUKE: (over comlink) Shut down all the garbage mashers on the detention level.

THREEPIO: (to Artoo) No. Shut them all down! Hurry!

THREEPIO: Listen to them! They're dying, Artoo! Curse my metal body! I wasn't fast enough. It's all my fault! My poor master!

-- Star Wars IV: A New Hope

The way that StoryComponents store their observer lists is via Weak References. If the only references to an object are weak references (or none at all) the garbage collector will destroy it. They're a way of keeping a link to an object without keeping it in memory. We've done this for StoryComponentObserver lists (and some other observers) because they don't need to keep the listener around, but they do need to know about them if they still do exist. Just be aware of this, because if you don't have a strong reference to a listener, it'll disappear. The reason that those are weak references, was to solve a problem where listeners would stick around longer than they should, causing either memory leaks, undesired behavior, or sometimes even both.

Since we're trying to keep GUI widgets separate from the listeners that update them, its common practice to store some kind of hard reference in the Factory or Builder that creates them.

##You can see java source Most of Java's source code can be viewed in debug mode in eclipse. All you need to do is install and use the java JDK (Java Development Kit) rather than the plain JRE (Java Runtime Environment).

http://techiedan.com/2009/10/19/set-up-jdk-in-eclipse/

##Be explicit with "this" When using anonymous classes and referring to the containing class, remember to use the outer classname eg. ContainingClass.this. We had a problem in StoryComponentPanel where selection wasn't properly handled because we were checking if selectedPanels.contains(this). However, the "this" in that case referred to the anonymous listener, not the StoryComponentPanel. Since "contains" takes any Object as a parameter, no exceptions or errors were created. It just always returned false. Be careful with this. Pun intended.

##Creating Components with Gradients Note: If you are creating a simple JPanel with a gradient based on its background, see PanelFactory#buildGradientPanel(double factor).

To create a component with a gradient, extend a JComponent and override the paintComponent(Graphics g) method with the following:

 @Override
protected void paintComponent(Graphics grphcs) {
        final int SCALE  = 1.4;
 final Color topColour;
 final Color bottomColour;

 final Graphics2D g2d;
 final GradientPaint gp;

 topColour = this.getBackground();
 bottomColour = GUIOp.scaleWhite(topColour, SCALE);

 g2d = (Graphics2D) grphcs;
 gp = new GradientPaint(0, 0, topColour, 0, this.getHeight(),
 bottomColour);

 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
 RenderingHints.VALUE_ANTIALIAS_ON);

 g2d.setPaint(gp);

 g2d.fillRect(0, 0, this.getWidth(), this.getHeight());
 super.paintComponent(grphcs);
}

You must also call component.setOpaque(false); since we are actually drawing the gradient under the usual background of the component. Now, you can change the gradient colour by using component.setBackground(Color). You can also play around with the gradient by changing the value of SCALE.

If you're just overriding this method and want to follow the advice mentioned in "Building GUIs", then you can always create the component like so:

 JComponent component = new JPanel() {
        // Override code goes here
};