Abstract Factory in Interviews: Questions and Machine Coding
Abstract Factory is a popular interview topic because it tests your understanding of object-oriented design principles, abstraction layers, and the ability to balance complexity with maintainability.
Common Interview Questions
Question 1: How does Abstract Factory differ from Factory Method?
Answer: Factory Method creates one product type through inheritance (subclasses decide which concrete class to instantiate), while Abstract Factory creates families of related products through composition (a factory object creates multiple related products). Factory Method uses one method, Abstract Factory uses multiple methods (one per product type). Use Factory Method when you have a single product hierarchy. Use Abstract Factory when you have multiple related products that must be created together consistently.
Question 2: How do you handle adding a new product type to all families?
Answer: Adding a new product type requires modifying the abstract factory interface and all concrete factory implementations, which violates the Open-Closed Principle for the factory dimension. This is an inherent trade-off of the pattern. To mitigate this, you can use default methods (in languages that support them) that throw UnsupportedOperationException, allowing factories to opt-in to new products. However, if you frequently add product types rather than product families, Abstract Factory may be the wrong pattern. Consider Builder or a more flexible creation mechanism instead.
Question 3: When should you use Abstract Factory versus Dependency Injection?
Answer: Modern frameworks with dependency injection (DI) containers handle object creation and wiring automatically, often eliminating the need for manual factory patterns. Use DI when your framework supports it and objects have complex dependency graphs. Use Abstract Factory when you need explicit control over family consistency, when working without a DI framework, or when building a library that consumers will extend. In practice, DI and Abstract Factory can coexist with the DI container injecting the appropriate factory based on configuration.
Machine Coding Scenario: Notification System
Design a notification system that sends messages through multiple channels (Email, SMS, Push). Each channel requires two components: a message formatter and a delivery service. Different notification profiles (Marketing, Transactional, Alert) format and deliver messages differently.
Step 1: Identify Product Families
Step 2: Define Abstract Products
Step 3: Create Abstract Factory
Step 4: Implement Client
Key Design Decisions to Discuss
Why Abstract Factory is appropriate here: First, you have two product types (formatter and delivery service) that must work together consistently. Second, mixing a marketing formatter with transactional delivery would create inconsistent user experiences (flashy format but immediate priority doesn't match marketing intent). Third, adding new profiles (like SecurityAlert) requires only adding a new factory without modifying existing code.
Handling channel variations: The factory takes Channel as a parameter to createFormatter because formatting varies by channel even within the same profile. However, delivery service behavior is consistent across channels within a profile (marketing always batches, transactional always sends immediately), so it doesn't need the channel parameter. If this assumption changes later, you can add the parameter without breaking the interface contract.
Alternative considered: You could use Strategy pattern where NotificationService composes separate strategy objects for formatting and delivery. However, this requires the client to ensure strategies are compatible (not mixing marketing formatter with transactional delivery). Abstract Factory enforces this consistency automatically, which is why it's preferred when consistency is critical.
Follow-up Questions Interviewers Ask
How would you add a new channel like WhatsApp? Add WhatsApp to the Channel enum. Update each concrete factory's createFormatter method to handle the WhatsApp case. Delivery services likely don't change since they're channel-agnostic. This modification is localized to factory implementations and doesn't affect client code.
What if a new profile only needs custom formatting but uses standard delivery? Create the new factory (like CustomerServiceFactory) and implement createDeliveryService to return the same delivery service as transactional profile. Alternatively, use composition where factories can delegate to shared service creators for common components. However, this adds complexity, so evaluate whether the profile truly needs to be a separate family.
How do you test this design? First, unit test each concrete factory to verify it creates the correct product types. Second, use mock factories in NotificationService tests to verify it correctly uses formatter and delivery service. Third, integration tests verify that products from the same family work together correctly (marketing formatter produces output that marketing delivery service handles properly). Dependency injection makes testing easier by allowing test doubles to be injected.