Factory Method Pattern Interview Questions | JiQuest

add

#

Factory Method Pattern

Java design pattern deep dive

Factory Method Pattern in Java: 100 interview questions with professional answers.

Learn how the Factory Method pattern lets subclasses decide which concrete class to instantiate, how it differs from Simple Factory and Abstract Factory, and how it holds up in payment processing, plugin systems, multi-tenant SaaS, and other real production designs.

100Questions
4GoF roles
15+Production domains
OrderServiceclient code CheckoutFlowclient code RefundJobclient code PaymentProcessor-Factoryabstract createProcessor() StripeProcessorConcreteProduct A PayPalProcessorConcreteProduct B ConcreteCreator picks the class

What makes a good Factory Method answer?

Interviewers are testing whether you understand object-creation responsibility, not just whether you can subclass something. They want to hear intent, SOLID trade-offs, testability, and whether Factory Method is even the right tool.

Defers constructionSubclasses decide which concrete Product to instantiate, not the base Creator.
Programs to an interfaceClients depend on the abstract Product or Creator type, never the concrete class name.
Stays Open/ClosedAdding a new product/creator pair should never require editing existing classes.
Matches real complexityReach for Factory Method when variation is real and growing, not for one or two stable POJOs.
Defer creationto subclasses? Factory Methodpolymorphic creators Simple Factorysingle switch/enum Need families ofrelated objects? AbstractFactory
ApproachUse whenWatch out for
Classic Factory Method (subclassed Creator)Creation genuinely varies by subclass and the product family is expected to grow.More classes to maintain; overkill for one or two stable variants.
Simple/Static Factory (switch or enum)A small, stable set of product types behind one convenience method.Violates Open/Closed as types grow; often mislabeled as "Factory Method" in interviews.
Supplier<T> / lambda-based factoryModern Java codebases that want a Map<String, Supplier<T>> registry instead of subclassing.Loses the template-method hook points that a real Creator subclass provides.
Registry / ServiceLoader-based factoryPlugin architectures where third parties contribute creators at runtime.Harder to trace at compile time; module visibility and classpath ordering issues.

Topics

Payment processor factory Q1Static factory vs FM Q2Creator/Product roles Q3 Refactor away from new Q4Open/Closed shapes Q5FM vs Abstract Factory Q6 FM vs Simple Factory Q7Generics factory method Q8Parameterized FM pitfall Q9 Shipping calculator selection Q10FM with Spring DI Q11Supplier-based FM Q12 Performance vs pooling Q13Cached instance bug Q14Unit testing a Creator Q15 Template method plus FM Q16Class explosion Q17Business logic in factory Q18 Varying constructor args Q19FM vs registry factory Q20Optional return handling Q21 Virtual constructor idea Q22FM plus Singleton products Q23Strategy selection via FM Q24 Framework/app decoupling Q25Long parameter list Q26GC and flyweights Q27 Legacy migration to FM Q28Exception handling in factory Q29FM vs Prototype Q30 Reflection-based factory Q31Multi-tenant pricing engine Q32Default vs abstract method Q33 Versioned product creation Q34Covariant return types Q35Small stable variant count Q36 Checked exceptions in ctor Q37Thread-safe cached factory Q38Dependency Inversion link Q39 Cross-platform file handles Q40Migrating FM to DI Q41Testing created product type Q42 Lazy factory initialization Q43Tax calculator incident Q44Liskov Substitution risk Q45 Conditional-branch smell Q46Benchmarking dispatch cost Q47Form field validators Q48 Instance vs static factory Q49Fallback creator chain Q50Event handler dispatch Q51 FM vs Simple Factory mix-up Q52Open/Closed test design Q53FM plus Observer pattern Q54 Factory method in constructor Q55Android Fragment factories Q56Compile-time vs runtime pick Q57 Logging appender factory Q58Serializing FM products Q59Overusing FM for POJOs Q60 Feature-flagged product pick Q61Program to interface Q62Immutable vs mutable products Q63 Database query builder factory Q64Spotting a misnamed factory Q65Generic repository factory Q66 Multiple dispatch limitation Q67Switch-to-registry refactor Q68Wrapping third-party SDKs Q69 Public vs package-private Creator Q70Circular Creator/Product refs Q71Debugging wrong product type Q72 Rules engine evaluator factory Q73FM vs Builder pattern Q74Mockito testability issues Q75 ServiceLoader plugin factories Q76Profiling virtual dispatch Q77Records for immutable products Q78 Multi-phase object lifecycle Q79Swallowed constructor errors Q80FM plus Visitor for AST Q81 Insurance calculator mixup Q82Abstract class vs interface Q83Serialization format abstraction Q84 Package-private product ctor Q85Font-rendering backend factory Q86FM vs naming convention Q87 Transparent object pooling Q88Jurisdiction consent validators Q89Checked vs unchecked errors Q90 Retry policy factory Q91Interface segregation leak Q92Message broker integration tests Q93 Chart renderer pipeline Q94FM vs Map of Suppliers Q95Static reference memory leak Q96 A/B testing algorithms Q97Java module system boundaries Q98Product hierarchy versioning Q99 Enum vs class ConcreteCreators Q100

Interview questions and answers

Each answer gives the design direction, the trade-off worth naming out loud, and the production concern that makes the answer sound senior.

1. Explain the intent of the Factory Method pattern and walk through a Java example where a PaymentProcessorFactory subclass decides at runtime whether to instantiate a StripeProcessor or a PayPalProcessor.

Factory Method's intent is to define an interface for creating an object, but let subclasses decide which concrete class to instantiate. The Creator's algorithm stays the same; only the returned Product type changes. The runtime decision is made when you choose which ConcreteCreator to instantiate, not by branching inside a shared method.

public interface PaymentProcessor {
    void charge(BigDecimal amount);
}

public abstract class PaymentProcessorFactory {
    // the factory method
    protected abstract PaymentProcessor createProcessor();

    public void processPayment(BigDecimal amount) {
        PaymentProcessor processor = createProcessor();
        processor.charge(amount);
    }
}

public class StripeProcessorFactory extends PaymentProcessorFactory {
    protected PaymentProcessor createProcessor() {
        return new StripeProcessor();
    }
}

public class PayPalProcessorFactory extends PaymentProcessorFactory {
    protected PaymentProcessor createProcessor() {
        return new PayPalProcessor();
    }
}

// client only knows PaymentProcessorFactory
PaymentProcessorFactory factory = tenantUsesStripe ? new StripeProcessorFactory() : new PayPalProcessorFactory();
factory.processPayment(new BigDecimal("49.99"));
Deferred instantiationConcreteCreator selects ProductClient depends on abstraction

2. How does the Factory Method pattern differ structurally from a simple static factory method that just has a switch statement over an enum? Show both implementations for creating NotificationSender objects and discuss when each is appropriate.

A true Factory Method relies on polymorphism: each ConcreteCreator subclass overrides the factory method to return its own Product, so adding a type means adding a class. A static switch-based factory centralizes the decision in one method that must be edited every time a type is added, which is really the Simple Factory idiom, not GoF Factory Method.

// Simple Factory: one method, one switch, must edit to extend
public class NotificationSenderFactory {
    public static NotificationSender create(NotificationType type) {
        switch (type) {
            case EMAIL: return new EmailSender();
            case SMS: return new SmsSender();
            case PUSH: return new PushSender();
            default: throw new IllegalArgumentException("Unknown type: " + type);
        }
    }
}

// Factory Method: polymorphic, no switch to edit
public abstract class NotificationSenderCreator {
    public abstract NotificationSender createSender();
}
public class EmailSenderCreator extends NotificationSenderCreator {
    public NotificationSender createSender() { return new EmailSender(); }
}

Use the switch-based Simple Factory when the type set is small and rarely changes; use Factory Method when new sender types are added regularly by different teams and you want Open/Closed compliance.

3. Walk through the class hierarchy the Factory Method pattern requires (Creator, ConcreteCreator, Product, ConcreteProduct) and show how Java's abstract classes and interfaces map onto each role for a DocumentExporter example.

Product is usually an interface (Document), ConcreteProduct implementations satisfy it (PdfDocument, WordDocument). Creator is an abstract class holding the shared export workflow and declaring the abstract factory method; ConcreteCreator subclasses (PdfExporter, WordExporter) override that method to produce the matching ConcreteProduct.

public interface Document {
    byte[] render();
}
public class PdfDocument implements Document {
    public byte[] render() { return PdfEngine.build(); }
}

public abstract class DocumentExporter {
    protected abstract Document createDocument();

    public final byte[] export() {
        Document doc = createDocument();
        return doc.render();
    }
}

public class PdfExporter extends DocumentExporter {
    protected Document createDocument() { return new PdfDocument(); }
}

Keeping export() final in the Creator enforces that only object creation varies between subclasses, while the surrounding workflow stays consistent everywhere.

4. What problem does Factory Method solve compared to calling new directly inside client code? Provide a before-and-after refactor of a ReportGenerator class that hardcodes new PdfReport().

Calling new PdfReport() directly hardwires the client to one concrete class, so every future format requires editing that client and recompiling everything that depends on it. Factory Method moves the decision behind an overridable method so the client only depends on the abstract Report type.

// Before: tightly coupled
public class ReportGenerator {
    public Report generate() {
        Report report = new PdfReport(); // hardcoded
        report.build();
        return report;
    }
}

// After: Factory Method
public abstract class ReportGenerator {
    protected abstract Report createReport();

    public Report generate() {
        Report report = createReport();
        report.build();
        return report;
    }
}
public class PdfReportGenerator extends ReportGenerator {
    protected Report createReport() { return new PdfReport(); }
}
Why it matters The refactor lets you add CsvReportGenerator or ExcelReportGenerator without touching ReportGenerator or any code that already depends on it.

5. Describe how Factory Method enables the Open/Closed Principle. Show how adding a new Shape type (e.g., Hexagon) to an existing shape-drawing framework requires no changes to existing classes.

Open/Closed means existing, tested code should not need modification to support new behavior. Because each ConcreteCreator owns its own factory method override, adding Hexagon means writing Hexagon implements Shape and HexagonCreator extends ShapeCreator; nothing in the base ShapeCreator or the rendering client changes.

public interface Shape { void draw(Canvas c); }

public abstract class ShapeCreator {
    public abstract Shape createShape();
    public void renderOnCanvas(Canvas c) { createShape().draw(c); }
}

// Adding a new shape touches only new files
public class Hexagon implements Shape {
    public void draw(Canvas c) { c.drawPolygon(6); }
}
public class HexagonCreator extends ShapeCreator {
    public Shape createShape() { return new Hexagon(); }
}
Open for extensionClosed for modification

6. What is the difference between Factory Method and Abstract Factory? Give a concrete Java example showing a UI toolkit where Abstract Factory produces families of related widgets while Factory Method produces a single product.

Factory Method creates one product via one overridable method, typically as part of a larger class's behavior. Abstract Factory creates a family of related products (a button, a checkbox, a scrollbar that must visually match) through several factory methods grouped on one factory interface, ensuring the family stays consistent.

// Abstract Factory: a family of related widgets
public interface UiFactory {
    Button createButton();
    Checkbox createCheckbox();
}
public class DarkUiFactory implements UiFactory {
    public Button createButton() { return new DarkButton(); }
    public Checkbox createCheckbox() { return new DarkCheckbox(); }
}

// Factory Method: one product, tied to one Creator's own logic
public abstract class DialogFactory {
    protected abstract Button createConfirmButton();
    public void showConfirmDialog() { createConfirmButton().render(); }
}

A useful heuristic: Abstract Factory is often implemented internally using several Factory Methods, one per product in the family.

7. Compare Factory Method to the Simple Factory (static factory) idiom in terms of extensibility, testability, and adherence to SOLID principles, using a LoggerFactory example.

Simple Factory centralizes creation logic in one static method, which is easy to call but violates Open/Closed because every new logger type means editing that method. Factory Method spreads creation across ConcreteCreator subclasses, which is more extensible and easier to unit test in isolation because each Creator can be mocked or substituted independently.

// Simple Factory
public class LoggerFactory {
    public static Logger create(String kind) {
        if ("console".equals(kind)) return new ConsoleLogger();
        if ("file".equals(kind)) return new FileLogger();
        throw new IllegalArgumentException(kind);
    }
}

// Factory Method
public abstract class LoggerCreator {
    public abstract Logger createLogger();
}
public class FileLoggerCreator extends LoggerCreator {
    public Logger createLogger() { return new FileLogger(); }
}

For a small, framework-internal utility, Simple Factory is often fine; Factory Method pays off once external teams need to plug in their own logger types without editing your factory.

8. How would you implement Factory Method using Java generics to avoid unchecked casts, and what are the limitations of that approach when the product hierarchy grows?

Parameterize the Creator with a generic type bound to the Product interface so the factory method's return type is checked at compile time instead of relying on casts. This works cleanly for a single product hierarchy, but it gets awkward once a Creator needs to produce multiple unrelated product types, since a class can only bind one generic parameter per hierarchy.

public abstract class Repository {
    protected abstract T createEntity();

    public T newDefault() {
        T entity = createEntity();
        return entity;
    }
}

public class UserRepository extends Repository {
    protected User createEntity() { return new User(); }
}
Limitation If a Creator later needs to also produce an Audit object alongside User, generics alone won't express two independent product families cleanly; you typically split it into two Creator hierarchies or move to Abstract Factory.

9. Discuss how parameterized Factory Method (passing a type token or enum into the factory method) can violate the Open/Closed Principle, and show a refactor that restores it using polymorphic creators.

A "parameterized factory method" that takes an enum or class token and internally branches is really Simple Factory wearing Factory Method's name. Every new type requires editing the branching method, which breaks Open/Closed. Restoring the pattern means giving each type its own ConcreteCreator so the branch disappears entirely.

// Violates Open/Closed: must edit this method for every new type
public Shape createShape(ShapeType type) {
    switch (type) {
        case CIRCLE: return new Circle();
        case SQUARE: return new Square();
        default: throw new IllegalArgumentException();
    }
}

// Restored: polymorphic creators, no shared branch to edit
public abstract class ShapeCreator { public abstract Shape createShape(); }
public class CircleCreator extends ShapeCreator { public Shape createShape() { return new Circle(); } }
public class SquareCreator extends ShapeCreator { public Shape createShape() { return new Square(); } }

10. In a microservices order-processing system, how would you use Factory Method to select the correct ShippingCalculator implementation based on the destination country, and what would go wrong if you used a single giant if-else chain instead?

Register one ConcreteCreator per country or region (DomesticShippingCreator, InternationalShippingCreator, FreightShippingCreator) behind a small lookup, and let each creator's factory method build the calculator with whatever data it needs. A single if-else chain over country codes tends to grow unbounded, mixes unrelated tax and customs logic together, and becomes a merge-conflict magnet as multiple teams add countries in the same method.

public abstract class ShippingCalculatorCreator {
    public abstract ShippingCalculator createCalculator();
}
public class InternationalShippingCreator extends ShippingCalculatorCreator {
    public ShippingCalculator createCalculator() {
        return new InternationalShippingCalculator(CustomsRatesTable.load());
    }
}
Per-region creatorsAvoids monolithic if-elseIndependent deploys per team

11. Explain how the Factory Method pattern interacts with dependency injection frameworks like Spring. Is Factory Method still needed when you have an IoC container, and if so, why?

Spring's @Bean factory methods and FactoryBean interface are essentially Factory Method applied at the container level: a method decides which concrete implementation to wire up based on profile, configuration, or conditionals. You still write application-level Factory Method hierarchies when the decision needs to happen per call rather than once at startup, such as picking a strategy per request rather than per bean.

@Configuration
public class PaymentConfig {
    @Bean
    @ConditionalOnProperty(name = "payments.provider", havingValue = "stripe")
    public PaymentProcessor stripeProcessor() {
        return new StripeProcessor();
    }
}

Here Spring's container plays the role of Creator, and the @Bean method is the factory method, resolved once during application startup rather than per invocation.

12. Show how to implement Factory Method using Java's Supplier<T> functional interface instead of subclassing, and discuss the trade-offs of this lambda-based approach versus the classic GoF structure.

Instead of a subclass hierarchy, you can pass a Supplier<Product> lambda into the Creator's constructor or method. This is lighter weight and avoids a class-per-type explosion, but it loses the ability for the "creator" to override other behavior alongside creation, since there is no real subclass to hook into.

public class ReportGenerator {
    private final Supplier reportSupplier;

    public ReportGenerator(Supplier reportSupplier) {
        this.reportSupplier = reportSupplier;
    }

    public Report generate() {
        Report report = reportSupplier.get();
        report.build();
        return report;
    }
}

ReportGenerator pdfGenerator = new ReportGenerator(PdfReport::new);

Use lambdas when creation is the only thing that varies; use classic subclassing when the Creator's other template-method steps also need to vary per concrete type.

13. What are the performance implications of using Factory Method versus object pooling when creating expensive-to-construct objects like database connections?

Factory Method controls which class gets instantiated, but it says nothing about lifecycle cost; if the factory method calls new on every invocation for an expensive object like a raw JDBC connection, you pay full construction cost every time. Pooling amortizes that cost by reusing pre-built instances, so a well-designed factory typically returns a handle from a pool rather than constructing from scratch.

public class PooledConnectionFactory extends ConnectionFactory {
    private final HikariDataSource pool;

    protected Connection createConnection() throws SQLException {
        return pool.getConnection(); // borrowed, not newly constructed
    }
}
Watch out Factory Method and pooling solve different problems — selecting a type versus managing lifecycle — and combining them means the "product" returned is really a pooled lease, not a fresh object.

14. Describe a real production bug caused by a Factory Method implementation that cached a single instance of a product across concurrent requests. How would you detect and fix this issue?

A common bug: someone "optimizes" a factory method by memoizing the created product in an instance field on the Creator, intending to save allocations. Under concurrent requests, if the product is stateful (holds a request-scoped buffer or user context), different threads end up sharing and corrupting that state, producing intermittent, hard-to-reproduce data mix-ups in logs.

// Buggy: shared mutable product across threads
public class RequestContextFactory extends ContextFactory {
    private RequestContext cached; // BUG: shared across threads
    protected RequestContext createContext() {
        if (cached == null) cached = new RequestContext();
        return cached;
    }
}

The fix is to create a fresh instance per call (or per thread using ThreadLocal if reuse is required), and to add a concurrency test that runs the factory method from multiple threads simultaneously and asserts each result is independent.

15. How would you unit test a class that depends on an abstract Creator without instantiating any concrete product implementations? Show an example using a test-double ConcreteCreator.

Because clients depend only on the Creator's public API, you can subclass the Creator in a test with a factory method that returns a hand-built test double, letting you verify the Creator's shared workflow (the template method) in isolation from any real product.

class TestReportGenerator extends ReportGenerator {
    Report fakeReport = mock(Report.class);
    protected Report createReport() { return fakeReport; }
}

@Test
void generateCallsBuildOnTheCreatedReport() {
    TestReportGenerator generator = new TestReportGenerator();
    generator.generate();
    verify(generator.fakeReport).build();
}
Test-double ConcreteCreatorIsolates the template logic

16. What is the template-method-calls-factory-method idiom, and how does it let a base class define an algorithm skeleton while deferring object creation to subclasses? Illustrate with a DocumentProcessor example.

This idiom nests Factory Method inside the Template Method pattern: the base class defines a fixed sequence of steps (open, process, close), and one step in that sequence calls an abstract factory method to obtain the object it needs, letting subclasses customize just that one step without duplicating the surrounding algorithm.

public abstract class DocumentProcessor {
    protected abstract Parser createParser();

    public final void process(InputStream input) {
        Parser parser = createParser();   // factory method step
        Document doc = parser.parse(input);
        validate(doc);
        persist(doc);
    }
}

public class XmlDocumentProcessor extends DocumentProcessor {
    protected Parser createParser() { return new XmlParser(); }
}

17. Explain how Factory Method can lead to class explosion in large codebases. Show a scenario with 15+ concrete creator/product pairs and discuss mitigation strategies.

Because each product variant requires its own ConcreteCreator plus ConcreteProduct, a system supporting 15 file formats, 15 payment gateways, or 15 chart types ends up with 30+ small classes, most of which contain only a single overridden method. Navigation, code review, and onboarding all get harder as the package fills with near-identical boilerplate classes.

// 15 pairs like this add up fast
public class ExcelExporterCreator extends ExporterCreator {
    protected Exporter createExporter() { return new ExcelExporter(); }
}

Mitigations include collapsing trivial creators into a registry of Supplier<Product> lambdas keyed by type, or generating the boilerplate creators from an enum, reserving full subclassing for the variants that genuinely need extra template-method behavior.

18. What common mistake do developers make when they put business logic unrelated to object creation inside a factory method? Show a bad example and refactor it to separate concerns.

A factory method should decide which class to instantiate, not perform validation, persistence, or notification side effects. When those concerns creep in, the method becomes hard to test, hard to reuse, and surprising to callers who expect creation to be side-effect free.

// Bad: side effects hidden inside "creation"
protected PaymentProcessor createProcessor() {
    PaymentProcessor p = new StripeProcessor();
    auditLog.record("processor created"); // unrelated side effect
    emailService.notifyAdmin();           // unrelated side effect
    return p;
}

// Better: factory method only creates; caller handles the rest
protected PaymentProcessor createProcessor() {
    return new StripeProcessor();
}
public void processPayment(BigDecimal amount) {
    PaymentProcessor p = createProcessor();
    auditLog.record("processor created");
    p.charge(amount);
}

19. How do you handle Factory Method when the concrete product requires constructor arguments that vary per subtype, such as a VehicleFactory where Truck needs a payload capacity and Motorcycle needs an engine size?

Each ConcreteCreator knows its own product's constructor requirements, so it can hold or receive whatever configuration it needs and pass it internally; the abstract factory method's signature stays uniform (no arguments, or only arguments common to all products) while subtype-specific configuration lives inside the ConcreteCreator.

public abstract class VehicleFactory {
    protected abstract Vehicle createVehicle();
}
public class TruckFactory extends VehicleFactory {
    private final double payloadCapacityTons;
    public TruckFactory(double payloadCapacityTons) { this.payloadCapacityTons = payloadCapacityTons; }
    protected Vehicle createVehicle() { return new Truck(payloadCapacityTons); }
}
public class MotorcycleFactory extends VehicleFactory {
    private final int engineCc;
    public MotorcycleFactory(int engineCc) { this.engineCc = engineCc; }
    protected Vehicle createVehicle() { return new Motorcycle(engineCc); }
}

20. Discuss the trade-off between Factory Method (compile-time subclass selection) and a registry-based factory (runtime lookup by string key) for a plugin architecture that loads DataSourceConnector implementations.

Classic Factory Method requires the client to choose which ConcreteCreator subclass to instantiate, which is resolved at compile time. A registry-based factory maps string keys (like "postgres" or "mongo") to creators at runtime, which is essential for plugin architectures where connector types are only known from configuration files loaded after the JAR is built.

public class ConnectorRegistry {
    private final Map> creators = new HashMap<>();

    public void register(String key, Supplier creator) {
        creators.put(key, creator);
    }

    public DataSourceConnector create(String key) {
        Supplier creator = creators.get(key);
        if (creator == null) throw new IllegalArgumentException("Unknown connector: " + key);
        return creator.get();
    }
}

The registry sacrifices some compile-time safety for the runtime flexibility a plugin system requires.

21. How would you implement Factory Method to support returning null or an Optional<T> when no suitable product can be created, and what are the pitfalls of each approach?

Returning null from a factory method forces every caller to remember a null check, and a missed check produces a NullPointerException far from the real cause. Returning Optional<Product> makes the possibility of absence visible in the type signature and forces callers to handle it explicitly.

public abstract class CodecFactory {
    protected abstract Optional createCodec(String format);
}
public class ImageCodecFactory extends CodecFactory {
    protected Optional createCodec(String format) {
        return switch (format) {
            case "png" -> Optional.of(new PngCodec());
            case "jpg" -> Optional.of(new JpgCodec());
            default -> Optional.empty();
        };
    }
}
Pitfall Overusing Optional as a parameter type or storing it in fields is discouraged; it is best kept as a factory method's return type only.

22. Explain why Factory Method is sometimes called a virtual constructor and demonstrate a Java example where the factory method is overridden polymorphically to change which subclass gets constructed.

Java has no true "virtual constructors" — constructors are not polymorphic — but a factory method achieves the same effect indirectly: calling the same method name on different subclass instances produces different concrete types, exactly the way virtual dispatch changes behavior polymorphically.

public abstract class ButtonCreator {
    public abstract Button createButton(); // acts like a virtual constructor
}
public class WindowsButtonCreator extends ButtonCreator {
    public Button createButton() { return new WindowsButton(); }
}
public class MacButtonCreator extends ButtonCreator {
    public Button createButton() { return new MacButton(); }
}

ButtonCreator creator = isWindows ? new WindowsButtonCreator() : new MacButtonCreator();
Button button = creator.createButton(); // "virtual" construction

23. What issues arise when you try to combine Factory Method with the Singleton pattern for the concrete products, and how would this affect testability in a unit test suite?

If a ConcreteProduct is itself a Singleton, every call to the factory method returns the exact same shared instance, which quietly reintroduces global mutable state into a design that was supposed to keep creation flexible. Tests that run in parallel or in sequence can leak state between test cases through that shared Singleton product, causing order-dependent failures.

// Risky combination: Singleton Product hidden behind Factory Method
protected Cache createCache() {
    return CacheSingleton.getInstance(); // same instance every call
}
Testability impact Mocking the product becomes harder because the Singleton's private constructor and static access point resist substitution; prefer letting the factory method construct a fresh, injectable instance instead.

24. Show how Factory Method can be used to implement the Strategy pattern's strategy selection, using an example of a CompressionStrategyFactory that picks between GZIP, ZIP, and LZ4 based on file size.

Factory Method and Strategy compose naturally: Strategy defines interchangeable algorithms, and Factory Method decides which strategy instance to hand back based on runtime conditions like file size, letting the calling code stay unaware of the selection logic.

public abstract class CompressionStrategyFactory {
    public abstract CompressionStrategy createStrategy(long fileSizeBytes);
}
public class SizeBasedCompressionFactory extends CompressionStrategyFactory {
    public CompressionStrategy createStrategy(long fileSizeBytes) {
        if (fileSizeBytes < 1_000_000) return new Lz4Strategy();
        if (fileSizeBytes < 100_000_000) return new GzipStrategy();
        return new ZipStrategy();
    }
}

25. How does Factory Method help decouple a framework's core logic from application-specific extensions? Use the example of a TestCaseFactory in a testing framework like JUnit.

Testing frameworks like JUnit define the core running algorithm (set up, run, tear down) inside the framework, while relying on Factory Method-like extension points (such as JUnit 5's TestInstanceFactory) so application code supplies how test instances get constructed, including dependency injection or custom lifecycles, without the framework knowing any application-specific class.

public interface TestInstanceFactory {
    Object createTestInstance(TestClass testClass, ExtensionContext context);
}

public class SpringAwareTestInstanceFactory implements TestInstanceFactory {
    public Object createTestInstance(TestClass testClass, ExtensionContext context) {
        return springContext.getAutowireCapableBeanFactory()
            .createBean(testClass.getJavaClass());
    }
}
Framework/extension boundaryInversion of control
No comments
Leave a Comment