Build Google Docs | A Real-World LLD Project | Document Editor LLD |
Introduction to Low-Level Design (LLD)
Overview of the Session
- The session welcomes viewers back to a series on low-level design, emphasizing practical application of previously learned theories such as SOLID principles and object-oriented design.
- The focus will be on approaching a problem in an LLD interview context, demonstrating how to create a low-level design through example.
Problem Statement: Document Editor
- The primary task is to develop a document editor, outlining its features and implementation strategies.
- Users can input text and images into the document editor, which should support scalability for future features like tables or videos.
Approaching the Design Problem
Design Methodologies
- Two main approaches exist for designing applications: top-down and bottom-up.
- Top-down focuses on creating high-level objects first before detailing smaller components.
- Bottom-up starts with small components and builds up to larger objects.
- Preference is given to the bottom-up approach in most LLD interviews due to its effectiveness in handling complex designs.
Initial Class Structure
Creating the Document Editor Class
- A basic class structure for the document editor is proposed, where all functionalities are initially housed within one class.
- The class will maintain lists for both text and images, allowing users to add elements sequentially while determining their order.
Methods Within the Class
- Key methods include:
addText: Adds text strings to the document.
addImage: Accepts image paths as strings for inclusion in the document.
renderDocument: Loops through elements and generates output based on user inputs.
Identifying Bad Design
Issues with Current Architecture
- The current design violates several principles:
- Single Responsibility Principle: One class handles multiple responsibilities (adding content, rendering, saving).
- Open/Closed Principle: Adding new features requires modifying existing code rather than extending it.
Improving Design Principles
Refactoring Strategy
- To adhere better to design principles:
- Separate concerns by extracting element management from the main editor class into distinct classes (e.g., DocumentElement).
- Implement polymorphism by creating subclasses for different types of elements (text, image).
New Class Structures
- An abstract base class (
DocumentElement) will define common behavior while allowing specific implementations in derived classes likeTextElementandImageElement.
Finalizing Document Management
Document Class Responsibilities
- A new
Documentclass will manage collections of document elements. It will handle adding/removing elements and delegating rendering tasks back to individual element classes.
Persistence Layer Implementation
- Introduce a persistence layer via an abstract class that defines save operations. This allows flexibility in saving documents either locally or remotely without altering core functionality.
This structured approach ensures clarity in understanding how low-level designs evolve from initial concepts through iterative improvements while adhering closely to established software engineering principles.
Rendering Documents: Methodology and Architecture
Overview of Document Rendering
- The discussion begins with the creation of a
renderDocumentmethod, which will handle the rendering process for documents.
- This method will loop through elements in the document, calling their respective render functions whether they are text or image elements.
Saving Mechanism
- A new method called
saveis introduced to replacesaveToFile, which will invoke the database's save method.
- The saving mechanism depends on the reference to the database variable within the document editor, ensuring that it knows how to save files correctly.
Document Editor Responsibilities
- The document editor retains methods like
addText,addImage,renderDocument, andsave, but delegates all requests to appropriate handlers instead of managing them directly.
- It acts as an intermediary between clients and document handling processes, simplifying client interactions by abstracting complexity.
Code Structure and Class Design
Abstract Class Implementation
- An abstract class named
DocumentElementis created with a single render method that is extended by specific element types likeTextElement.
- Each element type overrides its render function; for instance, text returns plain text while images return HTML tags with paths.
Extensibility of Elements
- New elements can be added easily due to adherence to open/closed principles; examples include newline and tab space elements that inherit from the base class.
- Future extensions could include formatting options such as bold or italicized text by creating new classes that implement their own rendering logic.
Document Class Functionality
Collection Management
- The Document class manages a collection of document elements using a vector structure for storage.
- It includes methods for adding elements and rendering them without handling specifics itself; it loops through stored elements calling their individual render methods.
Persistence Layer Integration
- A separate Persistence class handles data storage operations, supporting both file-based and database storage mechanisms.
- Database storage involves overriding save methods to accommodate SQL queries for data persistence.
Client Interaction with Document Editor
Constructor Logic
- The DocumentEditor class constructor initializes references to documents and persistence layers, allowing polymorphic behavior based on provided storage types (file or DB).
Method Delegation
- Methods like
addTextdelegate requests back to the document object, maintaining simplicity in client interactions while ensuring functionality remains intact.
Finalizing Architecture: SOLID Principles
Adherence to SOLID Principles
- The architecture follows Single Responsibility Principle (SRP), where each class has distinct responsibilities—document management, rendering, persistence—without overlap.
Open/Closed Principle Compliance
- New element types can be added without modifying existing code structures.
- This allows flexibility in extending functionalities while keeping core implementations stable.
Liskov Substitution Principle
- Any subclass can replace its parent type without affecting program correctness.
- For example, both TextElement and ImageElement can be used interchangeably within collections managed by Document.
Interface Segregation Principle
- Interfaces are kept minimalistic so that implementing classes only need relevant methods.
Dependency Inversion Principle
- High-level modules do not depend on low-level modules directly but interact through abstractions (interfaces).
Addressing Potential Issues
Counter Questions in Design Review
- Concerns arise regarding whether having multiple responsibilities within classes violates SRP despite delegation practices being employed.
Knowledge Coupling Concerns
- Classes may still possess knowledge about other components' functionalities leading to potential coupling issues.
Proposed Solutions for Improvement
- Suggestions include separating rendering logic into a dedicated renderer class while keeping core operations focused solely on data manipulation tasks within documents.