Opening low-level design
Progress is saved as you study.
Opening low-level design
Progress is saved as you study.
Opening your learning path
Progress is saved as you study.
Factory & Abstract Factory is a core lesson in the Strategy & Factory Patterns family of the Thita low level design course.
#1 and #4 most tested patterns: eliminate if/else chains with Strategy and create objects without specifying exact classes with Factory.
It is part of the Interview Soon path: the LLD material that appears in a standard interview loop rather than only in a specialist one.
Factory patterns help when object creation leaks into business logic: if paymentType == STRIPE create StripeClient, if vehicleType == TRUCK create Truck, if os == WINDOWS create WindowsButton.
In interviews, candidates usually fail in two ways:
if/else creation chain in several services.Use a factory when callers should work with an interface and avoid knowing concrete classes. Put construction in one factory or in the DI container.
Object creation can leak details. new Car() is fine when the caller wants a Car. It becomes a design smell when the caller wants any Vehicle and the exact type comes from config, request data, or rules.
Bad version:
1OrderService:
2 if type == CAR: vehicle = new Car()
3 else if type == TRUCK: vehicle = new Truck()
4 else if type == MOTORCYCLE: vehicle = new Motorcycle()Every caller that repeats this logic knows every concrete class, enum value, and construction rule.
Better version:
1OrderService:
2 vehicle = vehicleFactory.create(type)OrderService depends on the Vehicle interface. DefaultVehicleFactory owns the mapping from type to class.
Know three levels: Simple Factory, Factory Method, and Abstract Factory. Pick the simplest one that removes the concrete-class dependency.
| Pattern | What it creates | Who decides concrete class? | Use when |
|---|---|---|---|
| Simple Factory | One product hierarchy | One factory class or static method | A type or config maps to one interface |
| Factory Method | One product hierarchy | Subclass or overridable factory method | Creation varies by subclass or module |
| Abstract Factory | Multiple related product hierarchies | Concrete factory family | Created objects must belong to the same provider, platform, or theme |
1Do callers need to hide concrete classes?
2 No -> use constructor directly.
3 Yes ->
4 Are you creating one kind of interface?
5 Yes ->
6 Is one central mapping enough?
7 Yes -> Simple Factory.
8 No -> Factory Method.
9 No ->
10 Are created objects a related family?
11 Yes -> Abstract Factory.
12 No -> separate factories.Interview default:
Simple Factory usually has one concrete method, such as VehicleFactory.create(type). The original GoF book does not include Simple Factory. Teams still use it often, and it is often the right interview answer.
Factory Method puts creation behind a method that subclasses or implementations override. Simple Factory centralizes creation. Factory Method lets each creator decide what to create.
Use this when the caller says: “I need a Vehicle, and the exact class may be Car, Truck, or Motorcycle.”
Avoid a factory when the caller always creates one concrete class and there is no branching, config, dependency inversion, or testing need.
1User user = new User(name);UserFactory adds no value in that case.
A delivery app may choose vehicle type based on package size, city rules, or vendor config. The dispatching service should ask for a Vehicle. The factory should construct the matching vehicle class.
1enum VehicleType { CAR, TRUCK, MOTORCYCLE }
2
3interface Vehicle {
4 String drive();
5}
6
7class Car implements Vehicle {
8 @Override public String drive() { return "Driving car"; }
9}
10
11class Truck implements Vehicle {
12 @Override public String drive() { return "Driving truck"; }
13}
14
15class Motorcycle implements Vehicle {
16 @Override public String drive() { return "Riding motorcycle"; }
17}
18
19interface VehicleFactory {
20 Vehicle create(VehicleType type);
21}
22
23class DefaultVehicleFactory implements VehicleFactory {
24 @Override public Vehicle create(VehicleType type) {
25 return switch (type) {
26 case CAR -> new Car();
27 case TRUCK -> new Truck();
28 case MOTORCYCLE -> new Motorcycle();
29 };
30 }
31}
32
33public class Main {
34 public static void main(String[] args) {
35 VehicleFactory factory = new DefaultVehicleFactory();
36 Vehicle vehicle = factory.create(VehicleType.TRUCK);
37 System.out.println(vehicle.drive());
38 }
39}Abstract Factory creates a set of related objects. Use it when several objects must match the same provider, platform, or theme.
Classic UI example:
1UIFactory
2 createButton()
3 createCheckbox()
4
5WindowsUIFactory
6 createButton() -> WindowsButton
7 createCheckbox() -> WindowsCheckbox
8
9MacUIFactory
10 createButton() -> MacButton
11 createCheckbox() -> MacCheckboxThe caller should never mix WindowsButton with MacCheckbox. One concrete factory owns the whole set.
Use Abstract Factory when the prompt says:
Avoid Abstract Factory when you create one interface. For VehicleFactory.create(type), Abstract Factory adds extra classes without protecting a matched set.
A payment system may support Stripe and PayPal. Each provider needs a payment client and a refund client. Both clients must use the same provider credentials and API behavior.
A checkout caller asks one gateway factory for a payment client and a refund client, so Stripe and PayPal pieces can never be mixed.
1interface PaymentClient {
2 String pay(int amount);
3}
4
5interface RefundClient {
6 String refund(String paymentId);
7}
8
9interface PaymentGatewayFactory {
10 PaymentClient createPaymentClient();
11 RefundClient createRefundClient();
12}
13
14class StripePaymentClient implements PaymentClient {
15 @Override public String pay(int amount) {
16 return "Stripe paid " + amount;
17 }
18}
19
20class StripeRefundClient implements RefundClient {
21 @Override public String refund(String id) {
22 return "Stripe refunded " + id;
23 }
24}
25
26class PaypalPaymentClient implements PaymentClient {
27 @Override public String pay(int amount) {
28 return "PayPal paid " + amount;
29 }
30}
31
32class PaypalRefundClient implements RefundClient {
33 @Override public String refund(String id) {
34 return "PayPal refunded " + id;
35 }
36}
37
38class StripeFactory implements PaymentGatewayFactory {
39 @Override public PaymentClient createPaymentClient() {
40 return new StripePaymentClient();
41 }
42
43 @Override public RefundClient createRefundClient() {
44 return new StripeRefundClient();
45 }
46}
47
48class PaypalFactory implements PaymentGatewayFactory {
49 @Override public PaymentClient createPaymentClient() {
50 return new PaypalPaymentClient();
51 }
52
53 @Override public RefundClient createRefundClient() {
54 return new PaypalRefundClient();
55 }
56}
57
58public class Main {
59 public static void main(String[] args) {
60 PaymentGatewayFactory factory = new StripeFactory();
61 System.out.println(factory.createPaymentClient().pay(100));
62 System.out.println(factory.createRefundClient().refund("pay_1"));
63 }
64}A Strategy object holds interchangeable behavior. A factory can create the right strategy so callers do not contain selection logic.
Example:
1RateLimiterFactory.create(config)
2 if config.algorithm == TOKEN_BUCKET -> TokenBucketLimiter
3 if config.algorithm == SLIDING_WINDOW -> SlidingWindowLimiterThe caller depends on RateLimiter. The factory maps config to implementation.
Use Factory + Strategy when:
Avoid Factory + Strategy when:
Lead with the number of product types. Factory Method creates one product abstraction. Abstract Factory creates several related abstractions.
| Question | Factory Method | Abstract Factory |
|---|---|---|
| How many product types? | Usually one | Multiple related types |
| Example | VehicleFactory.create() | UIFactory.createButton() and createCheckbox() |
| Main benefit | Hide concrete implementation | Keep a family consistent |
| Common mistake | Making it static and calling it Factory Method | Using it for one object type |
| Interview answer | Caller works with the Vehicle interface | Caller needs provider-matched or platform-matched objects |
If the prompt says “create the right Vehicle,” choose Simple Factory or Factory Method.
If the prompt says “create all UI controls for Windows or Mac,” choose Abstract Factory.
Start with a normal constructor.
If callers branch on type to create one interface, introduce a Simple Factory.
If different modules or subclasses must own creation, use Factory Method.
If you must create compatible families like WindowsButton + WindowsCheckbox or StripePaymentClient + StripeRefundClient, use Abstract Factory.
Interviewers often punish over-design. A clear Simple Factory usually beats an unnecessary Abstract Factory.
Common failure modes:
new Object() with no branching, abstraction, or construction complexity.if/else creation chain in multiple services after adding a factory.VehicleFactory.create(type) is clearer.No related case studies are assigned for this subpattern.