Chaturmind
LearnDSASystem DesignDevOpsEngineering GrowthBlog
Start learning
Chaturmind

Structured learning paths for engineers who want to go deep. Written by practitioners.

Learn

  • Java
  • DSA
  • System Design
  • Spring Boot
  • AI / ML
  • DevOps
  • Engineering Growth

Company

  • Blog
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Chaturmind. All rights reserved.

Built for engineers who want to go deep.


← Low-Level Design & Design Patterns

OOP Fundamentals & SOLID Principles

  • Object-Oriented Design Refresher
  • Single Responsibility & Open/Closed
  • Liskov Substitution & Interface Segregation
  • Dependency Inversion

Creational Design Patterns

  • Singleton Pattern
  • Factory & Abstract Factory
  • Builder Pattern
  • Prototype Pattern

Structural Design Patterns

  • Adapter & Facade
  • Decorator Pattern
  • Composite & Proxy
  • Bridge & Flyweight

Behavioral Design Patterns

  • Strategy Pattern
  • Observer Pattern
  • Command Pattern
  • Template Method
  • State Pattern
  • Chain of Responsibility
  • Iterator & Mediator
  • Visitor Pattern

Machine Coding Exercises

  • Parking Lot — Requirements & Class Design
  • Parking Lot — Implementation
  • Elevator System — Requirements & Class Design
  • Elevator System — Implementation & Scheduling
  • Library Management System
  • Tic-Tac-Toe / Board Game Design
  • Chess Engine Design
  • Vending Machine Design
  • ATM System Design
  • In-Memory Rate Limiter
  • Thread-Safe LRU Cache
  • Connection Pool Design
  • Producer-Consumer Class Design
  • Movie Ticket Booking System
  • Hotel Reservation System
  • Airline Booking / Seat Selection System
  • Splitwise / Expense Sharing System
  • Design a Logging Framework
  • Design a Notification/Observer-Based Pub-Sub
  • Design a Cache with Pluggable Eviction Policy
  • Design Snake and Ladder
  • Design a Card Game Framework
  • Design an Inventory Management System
  • Restaurant Management System
  • Food Delivery Order Matching
  • Design a Shopping Cart & Checkout Flow
  • Design a Recommendation Engine
  • Design a Job Scheduler
  • Design a Circuit Breaker
  • Design a Distributed ID Generator
  • Design a Pub-Sub Message Broker
  • Design Twitter/X
  • Multi-Pattern Problem: Design a Ride Booking Class Model
  • Multi-Pattern Problem: Design a Food Ordering Class Model
  • Design an Authentication/Authorization Module
  • Design a Form/Survey Builder
  • Design a Meeting Room / Calendar Booking System
  • Design a Search/Filter Engine for an E-Commerce Catalog
  • Design a Voting/Polling System
  • Design a Retry Mechanism with Backoff
  • Design a Health Check Aggregator
  • Design a Feature Flag System
  • Design a Config Management Client
  • Design a Leader Election Algorithm
  • Design a Distributed Counter
  • Design a Bloom Filter
  • Design a Consistent Hashing Ring
  • Design a Distributed Tracing Library
  • Design a Metrics Collection Library
  • Design an Audit Logging Framework
  • Design a Plugin/Extension System
  • Design a Workflow Engine
  • Multi-Pattern Capstone: Design a Food Delivery App
Chaturmind
← Low-Level Design & Design Patterns

OOP Fundamentals & SOLID Principles

  • Object-Oriented Design Refresher
  • Single Responsibility & Open/Closed
  • Liskov Substitution & Interface Segregation
  • Dependency Inversion

Creational Design Patterns

  • Singleton Pattern
  • Factory & Abstract Factory
  • Builder Pattern
  • Prototype Pattern

Structural Design Patterns

  • Adapter & Facade
  • Decorator Pattern
  • Composite & Proxy
  • Bridge & Flyweight

Behavioral Design Patterns

  • Strategy Pattern
  • Observer Pattern
  • Command Pattern
  • Template Method
  • State Pattern
  • Chain of Responsibility
  • Iterator & Mediator
  • Visitor Pattern

Machine Coding Exercises

  • Parking Lot — Requirements & Class Design
  • Parking Lot — Implementation
  • Elevator System — Requirements & Class Design
  • Elevator System — Implementation & Scheduling
  • Library Management System
  • Tic-Tac-Toe / Board Game Design
  • Chess Engine Design
  • Vending Machine Design
  • ATM System Design
  • In-Memory Rate Limiter
  • Thread-Safe LRU Cache
  • Connection Pool Design
  • Producer-Consumer Class Design
  • Movie Ticket Booking System
  • Hotel Reservation System
  • Airline Booking / Seat Selection System
  • Splitwise / Expense Sharing System
  • Design a Logging Framework
  • Design a Notification/Observer-Based Pub-Sub
  • Design a Cache with Pluggable Eviction Policy
  • Design Snake and Ladder
  • Design a Card Game Framework
  • Design an Inventory Management System
  • Restaurant Management System
  • Food Delivery Order Matching
  • Design a Shopping Cart & Checkout Flow
  • Design a Recommendation Engine
  • Design a Job Scheduler
  • Design a Circuit Breaker
  • Design a Distributed ID Generator
  • Design a Pub-Sub Message Broker
  • Design Twitter/X
  • Multi-Pattern Problem: Design a Ride Booking Class Model
  • Multi-Pattern Problem: Design a Food Ordering Class Model
  • Design an Authentication/Authorization Module
  • Design a Form/Survey Builder
  • Design a Meeting Room / Calendar Booking System
  • Design a Search/Filter Engine for an E-Commerce Catalog
  • Design a Voting/Polling System
  • Design a Retry Mechanism with Backoff
  • Design a Health Check Aggregator
  • Design a Feature Flag System
  • Design a Config Management Client
  • Design a Leader Election Algorithm
  • Design a Distributed Counter
  • Design a Bloom Filter
  • Design a Consistent Hashing Ring
  • Design a Distributed Tracing Library
  • Design a Metrics Collection Library
  • Design an Audit Logging Framework
  • Design a Plugin/Extension System
  • Design a Workflow Engine
  • Multi-Pattern Capstone: Design a Food Delivery App
HomeLearnSystem DesignLow-Level Design & Design PatternsMachine Coding Exercises
✓ FreeIntermediate· 6 min read

Design a Form/Survey Builder

Form/Question/TextQuestion/ChoiceQuestion/Response classes, per-question-type validation via polymorphism, and Composite pattern for nested/conditional sections.

Published September 23, 2026


Design a Form/Survey Builder

Core classes

abstract class Question {
    String id;
    String prompt;
    boolean required;
    abstract ValidationResult validate(Answer answer); // polymorphism — no question-type switch anywhere else
}

class TextQuestion extends Question {
    Integer maxLength;
    ValidationResult validate(Answer answer) {
        String text = answer.getTextValue();
        if (required && (text == null || text.isBlank())) return ValidationResult.invalid("Required field");
        if (maxLength != null && text.length() > maxLength) return ValidationResult.invalid("Exceeds max length");
        return ValidationResult.valid();
    }
}

class ChoiceQuestion extends Question {
    List<String> options;
    boolean allowMultiple;
    ValidationResult validate(Answer answer) {
        List<String> selected = answer.getSelectedOptions();
        if (required && selected.isEmpty()) return ValidationResult.invalid("Required field");
        if (!allowMultiple && selected.size() > 1) return ValidationResult.invalid("Only one option allowed");
        if (!options.containsAll(selected)) return ValidationResult.invalid("Invalid option selected");
        return ValidationResult.valid();
    }
}

class Form { List<Question> questions; }
class Response { Map<String, Answer> answersByQuestionId; }

Validation via polymorphism, not a type switch

Same OCP argument made throughout this course (Chess Engine Design's piece-movement polymorphism, Strategy Pattern's if/else-chain comparison): Form.validate(Response) never branches on question type — it iterates questions and calls each one's own validate(). Adding a new question type (a DateQuestion, a NumericRangeQuestion) means writing one new Question subclass with its own validate() implementation, with zero changes to Form or any existing question type's code.

Composite pattern for nested/conditional sections

abstract class FormElement { // the Composite abstraction — both Question and Section implement it
    abstract ValidationResult validate(Response response);
    abstract boolean isVisible(Response response); // supports conditional logic
}

class Section extends FormElement {
    List<FormElement> children; // can hold Questions OR nested Sections
    String showIfQuestionId; // conditional visibility: only show this section if a specific prior answer matches
    String showIfValue;

    boolean isVisible(Response response) {
        if (showIfQuestionId == null) return true; // always visible
        Answer trigger = response.getAnswer(showIfQuestionId);
        return trigger != null && trigger.matches(showIfValue);
    }

    ValidationResult validate(Response response) {
        if (!isVisible(response)) return ValidationResult.valid(); // hidden sections skip validation entirely
        return children.stream()
            .map(child -> child.validate(response))
            .filter(ValidationResult::isInvalid)
            .findFirst()
            .orElse(ValidationResult.valid());
    }
}

Modeling Question and Section as both implementing a shared FormElement abstraction is Composite (see Composite & Proxy) applied directly — a Section can contain a mix of individual Questions and further nested Sections, and validate() recurses through this tree uniformly, exactly like Directory.size() recursing through nested directories in the Composite & Proxy file-system example. The conditional-visibility check (showIfQuestionId/showIfValue) is what makes this genuinely useful for a real survey builder — an entire section of follow-up questions that only applies based on an earlier answer, with hidden sections correctly skipped during validation rather than incorrectly required.

Follow-up questions this topic invites — and their answers

Q: Why does Section.validate() short-circuit on the first invalid child (findFirst()) rather than collecting every validation error? A: A real form-builder UI typically wants ALL validation errors at once (so a user sees every problem, not just the first, before resubmitting) — this simplified version is a reasonable LLD-scope shortcut; a production version would likely collect a List<ValidationResult> across all children rather than stopping at the first failure, worth naming as a deliberate simplification rather than an oversight.

Q: How would conditional visibility handle a chain of dependencies (Section C only shows if Section B is visible AND a specific answer in B matches)? A: The current showIfQuestionId design only supports a single flat condition — a more general design might need isVisible() to accept an arbitrary predicate/expression over the full Response, or explicitly check the triggering section's own visibility first before evaluating its own condition, since a hidden section's 'answer' shouldn't be able to trigger a dependent section's visibility.

Q: Is Question really the right thing to make abstract, or should validate() logic live in a separate QuestionValidator class per type instead? A: Both are defensible — putting validate() directly on Question (as shown) keeps a question type's data and its validation rule colocated; a separate QuestionValidator hierarchy would more closely mirror Strategy pattern's separation-of-algorithm-from-data philosophy, useful specifically if the SAME question type needed different validation rules in different contexts (a length limit that varies per form, not per question type).

Q: How would you extend ChoiceQuestion validation to support 'exactly N selections required' rather than just required/optional? A: A straightforward extension — add a minSelections/maxSelections range on ChoiceQuestion (generalizing the current binary allowMultiple flag) and check selected.size() against that range in validate(), no structural change needed, just additional fields and a slightly richer check within the same method.

Previous

Design an Authentication/Authorization Module

Next

Design a Meeting Room / Calendar Booking System

AI Tutor

Lesson: Design a Form/Survey Builder

Quick actions

AI responses can be inaccurate. Verify critical information.