Realistic use case for static factory method?
Asked Answered
O

6

14

I'm familiar with the idea and benefits of a static factory method, as described in Joshua Bloch's Effective Java:

  • Factory methods have names, so you can have more than one factory method with the same signature, unlike constructors.
  • Factory methods don't have to create a new object; they can return a previously-created object. This is good for immutable objects or value objects.
  • Factory methods can return an object of any subtype of their return type, unlike constructors.

Now I'm trying to explain static factory methods for someone who is learning Java and OO principles. She learns best from concrete scenarios instead of abstractions. If she can see the pattern at work, solving some problem, she'll get it. But she finds it harder to read an abstract list of characteristics like the above to understand how to apply the pattern.

Can you help me come up with a realistic example of using a static factory method, that makes its benefits clear, but which is still simple enough to show someone in an introductory Java class?

This person does have programming experience in PL/SQL but never got around to learning OOP patterns.

Overijssel answered 16/11, 2010 at 16:23 Comment(1)
Checked this out? #929521Flybynight
C
15

Use javax.swing.BorderFactory as an example of all three points.

This class is used to make borders for swing objects. These border objects can be easily re-used, and this factory method allows for this. Here is the javadoc. This factory is a great example of all three points:

  • There are multiple static methods with different names like createEmptyBorder() and createEtchedBorder().
  • These methods will return previously created objects when possible. It's quite frequent that the same border would be used throughout an application.
  • Border itself is actually an interface, so all objects created through this factory are actually classes which implement this interface.
Consecutive answered 16/11, 2010 at 16:26 Comment(1)
@Erick I liked your example. But the book implies this application as well: One application of this flexibility is that an API can return objects without making their classes public. But in your example case, all classes are public. So, is there any such example which covers this application too?Kapp
M
4

The textbook example of your second point is Integer.valueOf(int) (similar for Boolean, Short, Long, Byte). For parameter values -128 to 127, this method returns a cached instance instead of creating a new Integer. This makes (auto)boxing/unboxing much more performant for typical values.

You can't do that with new Integer() since the JLS requires that new create a new instance every time it is called.

Moua answered 16/11, 2010 at 16:26 Comment(0)
D
4

My current favorite example of this pattern is Guava's ImmutableList. Instances of it can only be created by static factories or a builder. Here are some ways that this is advantageous:

  • Since ImmutableList doesn't expose any public or protected constructors, it can be subclassed within the package while not allowing users to subclass it (and potentially break its immutability guarantee).
  • Given that, its factory methods are all able to return specialized subclasses of it without exposing their types.
  • Its ImmutableList.of() factory method returns a singleton instance of EmptyImmutableList. This demonstrates how a static factory method doesn't need to create a new instance if it doesn't have to.
  • Its ImmutableList.of(E) method returns an instance of SingletonImmutableList which is optimized because it will only ever hold exactly 1 element.
  • Most of its other factory methods return a RegularImmutableList.
  • Its copyOf(Collection) static factory method also does not always need to create a new instance... if the Collection it is given is itself an ImmutableList, it can just return that!
Digitate answered 16/11, 2010 at 16:42 Comment(2)
OK I'll bite. Exactly what would one do with an empty immutable list?Tonnie
@Chris: The same thing one does with, say, Collections.emptyList() (which is also an immutable singleton). When a method that returns a List doesn't have any data to return, it should always return an empty List rather than null. Since an empty, immutable collection can never hold any data, a singleton instance can be used everywhere it's needed. Plus, that instance can be as lightweight as possible since it doesn't even need any data structures internally to hold anything.Digitate
P
3

Wouldn't Calendar.getInstance() be a good exmaple? It creates depending on the locale a BuddhistCalendar, JapaneseImperialCalendar or by default a GregorianCalendar.

Phrygian answered 16/11, 2010 at 16:29 Comment(0)
E
0

Here is one I had to do a while back. At a job interview, I was asked to program a deck of cards where they can be shuffled. Really simple problem. I created:

Card:
  suit
  rank

Deck:
  card[]

I think what was the distinguishing factor is that there can only 52 cards at all times. So I made the constructor for Card() private and instead create static factory valueOf(suit, rank) This allowed me to cache the 52 cards and make the immutable. It taught many important basic lessons in that books.

  1. immutable
  2. control creation of object
  3. static methods
  4. possibly subclassing and return a card from another source. ( I didn't do this)

This is similar to Boolean and Byte, except I used a common homework example to show why its important to control the instances. I also created a helper function for deck called newDeck() because I wanted to show an instance where the constructor might not need to be private but it would still be nice to have a helper static factory.

I hope this helps!

Eisteddfod answered 16/11, 2010 at 16:31 Comment(2)
With this particular example I might even pursue an enum, since it is known at compile time every single instance of your class that can ever be created.Moua
I actually did an enum! I did two, three, four...jack, queen, king, and ace. I also created hearts, clubs, spades and diamonds. This worked perfectly because everything was statically checked at compile time and my parameters for valueOf(Rank rank, Suit suit). Very simple but yet teaches so much about OO programming.Eisteddfod
C
0

The simple case. Suppose you have a class which operates some sort of printer, but it doesn't care if it is epson, canon or something else. So, you just create an Interface Printer, create some implementations of it and create a class which has only one method: createPrinter.

So, the code will be simple:

   public interface Printer {
       print();
    }

    class CanonPrinter implements Printer {
       print() {
    // ...
       }
    }


    public PrinterFactory {

    Printer createPrinter() {
   if (... ) {
      return new CanonPrinter();
   } else {
      return new EpsonPrinter();
   }
}
}

client code:

Printer printer = PrinterFactory.createPrinter();
printer.print();

Here you abstract you clinet code from any details of what printers you can operate with or how they manage printing. It's PrinterFactory who cares what printer to choose if one for example malfunctions.

Calque answered 16/11, 2010 at 16:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.