AbstractStaticFactory.java
package com.morphiqlabs.wavelet.api;
import com.morphiqlabs.wavelet.util.NullChecks;
/**
* Abstract base class for static factory implementations.
*
* <p>This class provides a foundation for implementing the Factory pattern
* with static methods. It includes common validation and error handling logic
* that can be reused across different factory implementations.</p>
*
* <h2>Implementation Pattern</h2>
* <pre>{@code
* public final class MyStaticFactory extends AbstractStaticFactory<MyType, MyConfig> {
*
* private static final MyStaticFactory INSTANCE = new MyStaticFactory();
*
* private MyStaticFactory() {
* // Private constructor for singleton
* }
*
* public static MyType create() {
* return INSTANCE.createInstance();
* }
*
* public static MyType create(MyConfig config) {
* return INSTANCE.createInstance(config);
* }
*
* @Override
* protected MyType doCreate() {
* return new MyType(getDefaultConfiguration());
* }
*
* @Override
* protected MyType doCreate(MyConfig config) {
* // Implementation-specific creation logic
* return new MyType(config);
* }
*
* @Override
* protected MyConfig getDefaultConfiguration() {
* return MyConfig.defaultConfig();
* }
* }
* }</pre>
*
* @param <T> the type of object created by this factory
* @param <C> the configuration type used to create objects
*/
public abstract class AbstractStaticFactory<T, C> implements Factory<T, C> {
/**
* Default constructor for subclasses.
*/
protected AbstractStaticFactory() {
// For subclasses
}
/**
* Creates a new instance with default configuration.
*
* <p>This method delegates to {@link #doCreate()} for the actual
* instance creation.</p>
*
* @return a new instance of type T
*/
@Override
public final T create() {
return doCreate();
}
/**
* Creates a new instance with the specified configuration.
*
* <p>This method performs null checking and validation before
* delegating to {@link #doCreate(Object)} for instance creation.</p>
*
* @param config the configuration to use
* @return a new instance of type T
* @throws IllegalArgumentException if the configuration is invalid
* @throws NullPointerException if config is null
*/
@Override
public final T create(C config) {
NullChecks.requireNonNull(config, "config");
if (!isValidConfiguration(config)) {
throw new IllegalArgumentException(
"Invalid configuration for " + getDescription());
}
return doCreate(config);
}
/**
* Protected method for creating an instance without exposing it publicly.
* This allows static factory methods to use the instance methods internally.
*
* @return a new instance created with default configuration
*/
protected final T createInstance() {
return create();
}
/**
* Protected method for creating an instance without exposing it publicly.
* This allows static factory methods to use the instance methods internally.
*
* @param config the configuration to use
* @return a new instance created with the given configuration
*/
protected final T createInstance(C config) {
return create(config);
}
/**
* Creates a new instance with default configuration.
*
* <p>Subclasses must implement this method to provide the actual
* instance creation logic.</p>
*
* @return a new instance of type T
*/
protected abstract T doCreate();
/**
* Creates a new instance with the specified configuration.
*
* <p>Subclasses must implement this method to provide the actual
* instance creation logic. The configuration is guaranteed to be
* non-null and valid when this method is called.</p>
*
* @param config the validated configuration
* @return a new instance of type T
*/
protected abstract T doCreate(C config);
/**
* Gets the default configuration for this factory.
*
* <p>Subclasses should override this method if they support
* creating instances with default configuration.</p>
*
* @return the default configuration
* @throws UnsupportedOperationException if default configuration is not supported
*/
protected C getDefaultConfiguration() {
throw new UnsupportedOperationException(
getDescription() + " does not support default configuration");
}
}