Simple factory vs Factory Method vs Abstract Factory, how they differ, implementing each in Java, pros and cons, where a factory genuinely simplifies creation, real Abstract Factory scenarios, and how these patterns support scaling a codebase.
Published September 25, 2026
"Factory" means three different things in interviews. Separate them clearly:
Then show a modern twist: in Spring, a factory is often a map of beans keyed by type.
Short answer: A factory encapsulates object creation behind a method, so callers ask for an object by what they need (a type, a key, a configuration), not by which concrete class implements it. It centralises creation logic (validation, caching, choosing an implementation), and lets you add implementations without changing callers.
public final class PaymentGateways { // simple (static) factory
private PaymentGateways() { }
public static PaymentGateway forCountry(String iso) {
return switch (iso) {
case "IN" -> new RazorpayGateway();
case "US", "GB" -> new StripeGateway();
default -> throw new IllegalArgumentException("unsupported country " + iso);
};
}
}
Key points to cover:
List.of, Calendar.getInstance, NumberFormat.getInstance, Executors.newFixedThreadPool, ByteBuffer.allocate.Learn it in depth → Factory & Abstract Factory
Short answer:
Common trap: "Abstract Factory is a factory of factories". It isn't. It creates a family of products. Concrete factories are often singletons chosen once, not produced by another factory.
Short answer: A creator class declares an abstract (or overridable) method that returns a product interface. Subclasses override it to decide the concrete product, while the creator's other methods use the product without knowing its class.
abstract class ReportExporter {
public final byte[] export(Report r) { // template logic, independent of format
Writer w = createWriter(); // the factory method
return w.write(r.rows());
}
protected abstract Writer createWriter();
}
class CsvExporter extends ReportExporter { protected Writer createWriter() { return new CsvWriter(); } }
class PdfExporter extends ReportExporter { protected Writer createWriter() { return new PdfWriter(); } }
Key points to cover:
Supplier<Writer> (composition), which gives the same flexibility with less inheritance.Short answer:
switch factory still changes whenever a product is added. Registration-based factories avoid that.Short answer: Notification channels. The caller says "notify via SMS", and the factory returns the right sender, already configured with credentials, retry policy and rate limits. In Spring, the most elegant factory is a map of beans: every implementation registers itself, so adding WhatsApp means adding one class, with no factory change.
public interface NotificationSender { Channel channel(); void send(Message m); }
@Component
class NotificationSenderFactory {
private final Map<Channel, NotificationSender> senders;
NotificationSenderFactory(List<NotificationSender> all) { // Spring injects every implementation
this.senders = all.stream().collect(Collectors.toUnmodifiableMap(NotificationSender::channel, s -> s));
}
NotificationSender forChannel(Channel c) {
return Optional.ofNullable(senders.get(c)).orElseThrow(() -> new UnsupportedChannelException(c));
}
}
Key points to cover:
DriverManager and DataSource factories), but today it's usually handled by configuration plus auto-configuration, not hand-written factories.Short answer: Abstract Factory provides an interface for creating families of related objects without naming their concrete classes. The client receives one factory, and gets matching products from it, which guarantees that products from different families are never mixed. A factory method, in contrast, produces a single kind of product.
Short answer:
CloudFactory with storage(), queue() and secrets(). AwsCloudFactory returns S3/SQS/Secrets Manager adapters, and AzureCloudFactory returns Blob Storage, Service Bus and Key Vault adapters. A deployment picks one factory, and every component is consistent.Short answer:
public interface Storage { void put(String key, byte[] data); }
public interface MessageQueue { void publish(String topic, String msg); }
public interface CloudFactory { // the abstract factory
Storage storage();
MessageQueue queue();
}
public final class AwsCloudFactory implements CloudFactory {
public Storage storage() { return new S3Storage(); }
public MessageQueue queue() { return new SqsQueue(); }
}
public final class AzureCloudFactory implements CloudFactory {
public Storage storage() { return new BlobStorage(); }
public MessageQueue queue() { return new ServiceBusQueue(); }
}
@Configuration
class CloudConfig {
@Bean @ConditionalOnProperty(name = "cloud.provider", havingValue = "aws")
CloudFactory aws() { return new AwsCloudFactory(); }
@Bean @ConditionalOnProperty(name = "cloud.provider", havingValue = "azure")
CloudFactory azure() { return new AzureCloudFactory(); }
}
Short answer:
The trade-off: adding a new product kind (say, cache()) means changing the factory interface and every concrete factory. The pattern makes adding families easy, and adding product types hard.
Short answer: It scales the codebase and the organisation, rather than runtime throughput:
if statements.Combined with DI and configuration, one deployment artifact serves many environments.
Key points to cover:
Q: What's the difference between a static factory method and a constructor?
A: Static factories have names (Duration.ofSeconds), can return cached instances or subtypes, and can return Optional or null-safe results. Constructors always create a new instance of exactly that class. Effective Java item 1: "Consider static factory methods instead of constructors".
Q: Where does Spring use factories?
A: BeanFactory and FactoryBean (for example, LocalContainerEntityManagerFactoryBean), @Bean methods, ObjectProvider, and DataSourceBuilder.
Q: How do you avoid a giant switch in a factory?
A: Use registration: implementations declare the key they handle, and the factory builds a Map from them (with Spring collection injection, or ServiceLoader). New implementations need no factory change.
Q: Factory vs Builder? A: A factory decides which object to create, in one call. A builder decides how to assemble one complex object, step by step. They combine well: a factory can return a preconfigured builder.