Bird
Raised Fist0

Abstract Factory Pattern - Creating Families of Related Objects

LLDeasyInterview freq: common
Choose your preparation mode4 modes available
๐ŸŽฏ
Abstract Factory Pattern - Creating Families of Related Objects
40 min ยท medium
medium

Design a UI component factory system that can create families of related UI elements (Buttons, Checkboxes) for different operating systems (Windows, MacOS, Linux). The system must allow adding new OS families without modifying existing code and ensure thread-safe singleton factory instances.

  • There are at least three OS families: Windows, MacOS, Linux.
  • Each family produces related UI components: Button and Checkbox.
  • Factories must be singletons and thread-safe.
  • Clients use abstract interfaces to create UI components without knowing concrete classes.
  • Adding a new OS family should not require modifying existing factories or client code.
๐Ÿ“‹
Guided Steps
1/5
Step 1
Define Abstract Product Interfaces
Define Java interfaces for the abstract products Button and Checkbox that all concrete products will implement.
๐Ÿ’ก Hint
Use Java interfaces because products only define behavior without shared state; interfaces cannot hold instance fields.
Use Java interfaces Button and Checkbox to define product behavior contracts; interfaces cannot hold instance state, enabling multiple inheritance and flexible implementation.
Mid: Names Abstract Factory pattern and product interfaces. Senior: Explains why interfaces are used for product contracts due to lack of instance state and multiple inheritance support in Java.
โš ๏ธCommon Wrong Answer
Using abstract classes for Button and Checkbox leads to unnecessary inheritance constraints and inability to implement multiple product interfaces.
Step 2
Define Abstract Factory Interface
Create an abstract factory interface UIComponentFactory with methods to create Button and Checkbox.
๐Ÿ’ก Hint
Use interface for factory to allow multiple concrete factories implementing it; no shared state needed at this level.
UIComponentFactory as a Java interface with methods createButton() and createCheckbox() returning abstract product interfaces; interface chosen because no instance state is needed.
Mid: Identifies Abstract Factory interface role. Senior: Explains interface choice due to stateless factory contract and Java's single inheritance limitation.
โš ๏ธCommon Wrong Answer
Using abstract class for UIComponentFactory unnecessarily restricts factory implementations and complicates adding new factories.
Step 3
Implement Concrete Factories as Thread-safe Singletons
Implement concrete factories WindowsFactory, MacOSFactory, LinuxFactory as thread-safe singletons to produce concrete products.
๐Ÿ’ก Hint
Use volatile instance field with double-checked locking in getInstance() to ensure safe lazy initialization in Java.
Concrete factories implement UIComponentFactory and use private volatile static instance with double-checked locking in getInstance() method to ensure thread-safe singleton initialization.
Mid: Recognizes singleton pattern for factories. Senior: Explains volatile keyword prevents instruction reordering and ensures visibility, preventing partially constructed instances.
โš ๏ธCommon Wrong Answer
Using simple lazy initialization without volatile leads to possible visibility issues and returning partially constructed factory instances in multithreaded environments.
Step 4
Implement Concrete Products
Implement concrete product classes WindowsButton, MacOSButton, LinuxButton, WindowsCheckbox, MacOSCheckbox, LinuxCheckbox implementing respective interfaces.
๐Ÿ’ก Hint
Use concrete classes extending product interfaces; no shared state needed at abstract level, but concrete classes can have instance fields.
Concrete product classes implement Button or Checkbox interfaces; instance fields like label or state can be added as needed; no inheritance from abstract classes to keep flexibility.
Mid: Understands concrete product implementation. Senior: Explains why concrete classes implement interfaces and can have instance state, while abstract interfaces cannot.
โš ๏ธCommon Wrong Answer
Using abstract classes for concrete products limits flexibility and complicates adding new product types.
Step 5
Client Usage and Extensibility
Design client code to use UIComponentFactory interface to create UI components without depending on concrete classes, enabling easy addition of new OS families.
๐Ÿ’ก Hint
Client holds reference to UIComponentFactory interface; new factories can be added without modifying client code, following Open/Closed Principle.
Client depends only on UIComponentFactory interface and product interfaces; new concrete factories can be added without client modification, enforcing Open/Closed Principle in Java.
Mid: Describes Abstract Factory usage in client. Senior: Explains how programming to interfaces and factory abstraction enables extensibility without recompilation.
โš ๏ธCommon Wrong Answer
Client depending on concrete factories breaks Open/Closed Principle and requires code changes when adding new OS families.
๐Ÿ”Œ
API Design Challenge
Design an API for the UIComponentFactory interface and its concrete implementations that allows clients to create Buttons and Checkboxes for different OS families without depending on concrete classes. The API should support adding new OS families without changing client code and ensure thread-safe singleton factory instances.
  • Use Java interfaces for UIComponentFactory and product types to decouple clients from implementations.
  • Implement concrete factories as thread-safe singletons using volatile instance fields and double-checked locking.
  • Design factory methods to return abstract product interfaces to enable extensibility and Open/Closed Principle.
๐Ÿ“Š
Scoring Rubric
โ–ผ Show
Must Have - 60%
  • Product interfaces as Java interfaces without instance state
  • UIComponentFactory interface defining factory methods
  • Concrete factories implemented as thread-safe singletons with volatile and double-checked locking
  • Concrete product classes implementing product interfaces
  • Client depends only on abstract factory and product interfaces
Good to Have - 30%
  • Use of enum for OS family identifiers
  • Explanation of SOLID principles applied
  • Clear separation of concerns between factories and products
  • Use of Java-specific concurrency mechanisms in singleton implementation
Bonus - 10%
  • Extensibility to add new product types beyond Button and Checkbox
  • Demonstration of factory registration or lookup mechanism
  • Use of Java 8+ features like default methods in interfaces if applicable
Using abstract classes for product interfaces restricts multiple inheritance and flexibility, limiting the ability to implement multiple product interfaces and complicating extensibility.
Singleton without volatile leads to visibility issues and possible returning of partially constructed factory instances in multithreaded environments, causing unpredictable behavior.
โœ…
Model Answer
โ–ผ Reveal

This design implements the Abstract Factory pattern in Java to create families of related UI components (Buttons and Checkboxes) for multiple OS families (Windows, MacOS, Linux). It uses Java interfaces for product and factory abstractions, concrete factories as thread-safe singletons with volatile and double-checked locking, and concrete product classes implementing product interfaces. The client depends only on abstract interfaces, enabling extensibility and adherence to SOLID principles.

  • Use Java interfaces for product types (Button, Checkbox) to define behavior contracts without instance state, enabling multiple inheritance and flexibility.
  • Define UIComponentFactory as a Java interface with factory methods returning abstract product interfaces to decouple clients from concrete implementations.
  • Implement concrete factories as thread-safe singletons using private volatile static instance fields and double-checked locking in getInstance() methods to ensure safe lazy initialization and visibility.
  • Concrete product classes implement product interfaces and can have instance fields for state, avoiding abstract classes to maintain flexibility.
  • Client depends only on UIComponentFactory and product interfaces, enabling addition of new OS families without modifying client code, following Open/Closed Principle.
  • Use of volatile keyword in singleton factories prevents instruction reordering and visibility issues in multithreaded environments.
  • Avoid abstract classes for product interfaces to allow multiple inheritance and flexible implementation.
  • Factory methods return abstract product interfaces to decouple client from concrete implementations and enable extensibility.
  • Follow SOLID principles: Single Responsibility for factories and products, Open/Closed for extensibility, Dependency Inversion for client abstraction.
  • Define product interfaces Button and Checkbox as Java interfaces to specify behavior without instance state.
  • Define UIComponentFactory as a Java interface with factory methods returning abstract product interfaces.
  • Implement concrete factories as thread-safe singletons using private volatile static instance and double-checked locking in getInstance().
  • Concrete product classes implement product interfaces and can have instance fields for state.
  • Client code depends only on UIComponentFactory and product interfaces, enabling extensibility and adherence to Open/Closed Principle.
  • Use enum or constants for OS family identifiers if needed to avoid string-based errors.
  • Avoid abstract classes for product interfaces to allow multiple inheritance and flexibility.
  • Use volatile keyword in singleton factories to prevent instruction reordering and visibility issues.
  • Ensure factory methods return abstract product interfaces to decouple client from concrete implementations.
  • Follow SOLID principles: Single Responsibility for factories and products, Open/Closed for extensibility, Dependency Inversion for client abstraction.

Practice

(1/5)
1. In designing LinkedIn's Profile component, should Profile be an abstract class or an interface, considering it has both state (e.g., name, headline) and behavior (e.g., updateStatus())? Why?
easy
A. Abstract class, because it can hold shared state and provide default behavior implementations.
B. Interface, because state can be stored externally and behavior defined purely abstractly.
C. Interface, because it allows multiple inheritance and defines behavior without state.
D. Abstract class, because it enforces implementation of all methods in subclasses.

Solution

  1. Step 1: Analyze state and behavior requirements

    The Profile has shared state fields like name and headline, which need storage and default behavior like updateStatus().
  2. Step 2: Choose between abstract class and interface

    Abstract classes can hold state and provide default method implementations, while interfaces cannot hold instance state (prior to Java 8 default methods, but still no fields).
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Abstract class supports shared state and behavior [check]
Hint: State + behavior -> abstract class, not interface [check]
Common Mistakes:
2. A class has a private constructor and a static synchronized getInstance() method returning a single instance. Which pattern is applied and why is this the CORRECT use case?
easy
A. Factory Method pattern, because it controls object creation with a static method.
B. Prototype pattern, to clone existing instances instead of creating new ones.
C. Builder pattern, to construct complex objects step-by-step.
D. Singleton pattern, ensuring only one instance exists and is thread-safe.

Solution

  1. Step 1: Identify the pattern from constructor and method

    The private constructor prevents external instantiation, and the static synchronized getInstance() method ensures a single shared instance with thread safety.
  2. Step 2: Eliminate other creational patterns

    Factory Method creates objects but doesn't enforce single instance; Builder constructs complex objects but not singleton; Prototype clones instances, not restricts to one.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Private constructor + synchronized static getter -> Singleton pattern [check]
Hint: Private constructor + static synchronized getter -> Singleton [check]
Common Mistakes:
3. In the playback queue, the method addTrack() is not synchronized. Two threads concurrently check if the queue has space and then add tracks. Which exact sequence causes a race condition leading to queue overflow?
medium
A. Thread 1 checks queue size -> space available, context switch, Thread 2 checks queue size -> space available, both add tracks exceeding capacity.
B. Thread 1 adds a track, Thread 2 waits until Thread 1 finishes, then adds another track safely.
C. Thread 1 locks queue, Thread 2 spins waiting for lock, no race occurs.
D. Thread 1 removes a track while Thread 2 adds, causing inconsistent queue state.

Solution

  1. Step 1: Identify the check-then-act pattern in addTrack()

    Both threads check queue size before adding, but without synchronization, the state can change between check and add.
  2. Step 2: Trace the race condition sequence

    Thread 1 checks size -> space available, context switch before adding. Thread 2 checks size -> space available, both add tracks, causing overflow.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Unsynchronized check-then-act -> double add -> overflow [check]
Hint: Check-then-act without sync -> race condition [check]
Common Mistakes:
4. In a multi-threaded notification system, the sendNotification() method checks if a channel is available before sending. Without synchronizing this method, what exact sequence of thread interleaving causes duplicate notifications to be sent?
medium
A. Thread 1 updates channel status after sending; Thread 2 reads stale status and retries sending.
B. Thread 1 sends notification; Thread 2 waits until Thread 1 finishes sending.
C. Thread 1 and Thread 2 both block on a lock and then send sequentially.
D. Thread 1 checks isAvailable() -> true, context switch; Thread 2 checks isAvailable() -> true; both send notifications concurrently.

Solution

  1. Step 1: Identify the check-then-act race condition

    Without synchronization, both threads can see the channel as available before either marks it busy.
  2. Step 2: Trace thread interleaving

    Thread 1 checks availability -> true, context switch; Thread 2 checks availability -> true; both proceed to send, causing duplicate notifications.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Unsynchronized check-then-act allows concurrent sends [check]
Hint: Check-then-act without lock -> race, double send [check]
Common Mistakes:
5. In the Airline Reservation System, if the reserveSeat() method is not synchronized, which exact sequence of thread interleaving causes double-booking of the same seat?
medium
A. Thread 1 and Thread 2 both fail to read seat status due to visibility issues, causing booking failure.
B. Thread 1 reads seat as available, Thread 2 reads seat as unavailable, both proceed to book.
C. Thread 1 locks seat, Thread 2 waits; Thread 1 releases lock after booking.
D. Thread 1 checks seat availability -> true, context switch before booking; Thread 2 checks availability -> true, both book seat.

Solution

  1. Step 1: Identify the check-then-act race condition

    Without synchronization, Thread 1 reads seat as available but is preempted before booking; Thread 2 also reads available and proceeds.
  2. Step 2: Both threads book the same seat concurrently

    Because no lock or atomic operation protects the check-and-book sequence, double-booking occurs.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Check-then-act gap without synchronization -> double-booking [check]
Hint: Check seat available -> context switch -> both book [check]
Common Mistakes:
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard