简体   繁体   中英

Realistic use case for static factory method?

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.

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.

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.

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!

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

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!

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM