Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Make your apps fly

Implement Flyweight to improve performance

  • Print
  • Feedback

Page 5 of 5

Now let's implement a Line class and use line objects to draw lines instead of using the graphics context directly.

A naive Line implementation

Perhaps you think using a graphics context to draw lines is not very object-oriented, so you want to implement a Line class to encapsulate that functionality. Let's do that. Example 4 lists the revised application.

Example 4. Drawing lines with a heavyweight line object

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class Test extends JFrame{
   public Test() {
      ...
      button.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent event) {
            Graphics g = panel.getGraphics();
            for(int i=0; i < NUMBER_OF_LINES; ++i) {
               Color color = getRandomColor();
               System.out.println("Creating " + color + " line");
               Line line = new Line(color,
                                    getRandomX(), getRandomY(), 
                                    getRandomX(), getRandomY());
               line.draw(g);
            }
         }
      });
   }
   ...
}


I only included pertinent code; the rest of the application is identical to the one listed in Example 3. The preceding application creates 10,000 line objects and tells each one to draw itself. Example 5 lists the Line class.

Example 5. A heavyweight Line class

import java.awt.*;
public class Line {
   private Color color = Color.black;
   private int x, y, x2, y2;
   
   public Line(Color color, int x, int y, int x2, int y2) {
      this.color = color;
      this.x = x;   this.y = y;
      this.x2 = x2; this.y2 = y2;
   }
   public void draw(Graphics g) {
      g.setColor(color);
      g.drawLine(x, y, x2, y2);
   }
}


The preceding Line class maintains its color and endpoints, which it uses to draw a line.

A flyweight Line implementation

Obviously, we don't want to create 10,000 lines as we did in the preceding application, so let's reduce that number to six, one line for each line color. Example 6 lists the revised application.

Example 6. Drawing lines with a flyweight line object

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class Test extends JFrame {
   ...
   public Test() {
      ...
      button.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent event) {
            Graphics g = panel.getGraphics();
            for(int i=0; i < NUMBER_OF_LINES; ++i) {
               Line line = LineFactory.getLine(getRandomColor());
               line.draw(g, getRandomX(), getRandomY(), 
                            getRandomX(), getRandomY());
            }
         }
      });
   }
   ...
}


That's more like it. Now line objects are obtained from a line factory, which returns one of six shared line instances. Example 7 lists that factory.

Example 7. The line factory

import java.util.HashMap;
import java.awt.Color;
public class LineFactory {
   private static final HashMap linesByColor = new HashMap();
   public static Line getLine(Color color) {
      Line line = (Line)linesByColor.get(color);
      if(line == null) {
         line = new Line(color);
         linesByColor.put(color, line);
         System.out.println("Creating " + color + " line");
      }
      return line;
   }
}


The line factory maintains a HashMap of lines, keyed by color. When you ask the factory for a line by calling getLine(), the factory first checks to see if a line with that color exists in the HashMap; if so, it returns it. Otherwise, it creates a line, stores it in the HashMap, and returns it. Either way, the caller winds up with a shared Line instance.

Example 8 lists the revised Line class.

Example 8. A flyweight Line implementation

import java.awt.*;
public class Line {
   private Color color;
   public Line(Color color) {
      this.color = color;
   }
   public void draw(Graphics g, int x, int y, int x2, int y2) {
      g.setColor(color);
      g.drawLine(x, y, x2, y2);
   }
}


Notice the preceding Line class is simpler than Example 5's Line class. Why? Because the preceding Line class has been purged of extrinsic state, namely the line's endpoints. Because the preceding Line class does not maintain that extrinsic state, and because it's passed to the draw() method from the client, those line instances can be shared.

Your apps can soar

Historically, allocating a huge number of (typically small) objects can be detrimental to your Java application's performance, although modern JVMs have greatly reduced the penalty you must pay for such excess. If you find that your application is instantiating numerous objects of a particular type, you might consider using the Flyweight pattern to share a limited number of those objects.

About the author

David Geary is the author of Core JSTL Mastering the JSP Standard Tag Library (Prentice Hall, 2002; ISBN: 0131001531), Advanced JavaServer Pages (Prentice Hall, 2001; ISBN: 0130307041), and the Graphic Java series (Prentice Hall). David has been developing object-oriented software with numerous object-oriented languages for 18 years. Since reading the GOF Design Patterns book in 1994, David has been an active proponent of design patterns, and has used and implemented design patterns in Smalltalk, C++, and Java. In 1997, David began working full-time as an author and occasional speaker and consultant. David is a member of the expert groups defining the JSP Standard Tag Library and JavaServer Faces, and is a contributor to the Apache Struts JSP framework.

Read more about Core Java in JavaWorld's Core Java section.

  • Print
  • Feedback

Resources