The first chapter of the book is about creating and destroying objects, and the next few chapters are about that.
This article corresponds to the first in the book: replace the constructor with a static factory method.
What is the static factory method
To start with, a Boolean class has the following constructor
public Boolean(boolean value) {
this.value = value;
}
Copy the code
Static methods such as the following are also provided, which can also return Boolean instances
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
Copy the code
This is the static factory method: use a static method to provide an instance of itself. (Unofficial definition)
Note that the static factory approach here does not correspond to the factory pattern in design mode. Length reasons (mainly lazy, here is not detailed.
Advantages of the static factory approach
Advantage 1: Have a name.
One disadvantage of constructors is that they may not have a clear description of the object being created. For example, the constructor BigInteger(int, int, Random) may return a BigInteger that is prime, But using the BigInteger probablePrime(int, Random) method is more explicit.
Another disadvantage of constructors is that a class can only have one constructor with a specified signature. If you want to get around this limitation, you can provide two constructors whose argument lists are only in different order, but this can confuse users. Static factory methods are not subject to this limitation.
Advantage 2: You don’t have to create a new object every time you call them.
For example, in the boolea.valueof method above, the return value is already pre-built and no new object is created. At the same time, the singleton pattern, which is often used, is generally implemented by static factory method. This is much cheaper than creating a new object each time.
Advantage 3: Objects of any subtype of the original return type can be returned.
Provides great flexibility and is ideal for interface oriented programming. For example, in everyday work, you can define the return value of a method as an interface, which in reality may be any implementation class of that interface.
Alternatively, you can return an object without making the object’s class public.
There are many uses of this in java.util.collections:
public static <E> Set<E> newSetFromMap(Map<E, Boolean> map) {
return new SetFromMap<>(map);
}
Copy the code
And this SetFromMap is a private class
private static class SetFromMap<E> extends AbstractSet<E> implements Set<E>, Serializable{... }Copy the code
Advantage 4: The class of the returned object can change with each call, depending on the parameter values of the static factory method.
This is similar to the previous point, but with more emphasis on returning different classes by parameter values, either for performance reasons or for business reasons. Such as returning different implementations based on the size of the array.
Advantage 5: When writing the class containing this method, the class of the returned object does not need to exist.
This is the basis of the service provider framework. The typical application scenario is JDBC. The specific implementation classes are provided in each database driver package and do not exist when writing JDBC-related code.
You may be hearing the term service provider framework for the first time, but you’ve probably already been around it.
A service provider framework is one in which multiple service providers implement a service, and the system provides multiple implementations to clients and decouples them from the multiple implementations.
Changes by service providers are transparent to their clients, providing better scalability. For example, JDBC, JMS, and so on use the service provider framework.
There are four components
- Service Interface: A Service Interface that declares services through abstraction and is invoked by clients and implemented by Service providers.
- Provider Registration API: Service Provider Registration API used by the system to register a service Provider so that clients can access the services it implements.
- Service Access API: Service Access API that allows user clients to obtain corresponding services.
- Service Provider Interface: Service Provider interfaces that are responsible for creating instances of their Service implementations. (optional)
Don’t worry if you can’t read it, you can go to JDBC
- Service Interface:
Connection
The client calls are based onConnection
. - The Provider Registration API:
DriverManager.registerDriver
Register the service provider’s API, which the database driver calls to register itself. - The Service Access API:
DriverManager.getConnection
Get the API of the service. - The Service Provider Interface:
Driver
, used to createConnection
.
This pattern is very easy to use, recent code has been using this pattern, interested in the JDBC source code.
Disadvantages of the static factory approach
Disadvantage 1: Classes without public or protected constructors cannot be subclassed
This is fine; if you write your own class, you can provide a public or protected constructor along with static factory methods if necessary. If not, you can use composition instead of inheritance. You should know what it means, but I will write about it later.
Disadvantage 2: They’re hard to find
Don’t laugh, this is actually happening. I wrote a service, and the parameter was an object of ValueFilter type, which WAS defined by myself. Then my colleague told me that you needed a lot of parameters to create this class, which was very troublesome to create. In fact, I had already built a static factory method to facilitate others to use. But other people don’t necessarily look at your methods when they use your classes, and they subconsciously create objects with constructors in mind.
Here are some common names for static factory methods (copy
- From — a type conversion method that takes a single argument and returns a corresponding instance of the type, for example:
Date d = Date.from(instant);
- Of — Aggregate method, with multiple arguments, that returns an instance of the type and merges them together, for example:
Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING);
- ValueOf — a more verbose alternative to from and to, for example:
BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
- Instance or getInstance — The returned instance is described by the method’s (if any) argument, but cannot be said to have the same value as the argument, for example:
StackWalker luke = StackWalker.getInstance(options);
- Create or newInstance — like instance or getInstance, but create or newInstance guarantees that each call returns a newInstance, for example:
Object newArray = Array.newInstance(classObject, arrayLen);
- GetType – like getInstance, but used when factory methods are in a different class. Type indicates the Type of object returned by the factory method, for example:
FileStore fs = Files.getFileStore(path);
- NewType – like newInstance, but used when factory methods are in different classes. Type indicates the Type of object returned by the factory method, for example:
BufferedReader br = Files.newBufferedReader(path);
- Type — shorter versions of getType and newType for example:
List litany = Collections.list(legacyLitany);
conclusion
Instead of providing a public constructor first when an instance needs to be provided, static factories are preferred.
reference
- Service Provider Framework