Built a Text Editor using Low-Level Design & Design Patterns in Python
I worked on implementing a Text Editor (LLD) in Python—focusing not on the UI, but on how real editors efficiently store and manage text behind the scenes.
To make the editor scalable and memory-efficient, I implemented the Flyweight Design Pattern and structured the project into clean, modular classes. This exercise helped me understand how large-scale editors (like VS Code, Sublime, etc.) avoid storing duplicate styling data and handle operations like insert, delete, and read efficiently.
🧱 Design Patterns Used
🔹 Flyweight Pattern
I used the Flyweight pattern to avoid storing duplicate style objects. For example, if the user types many characters with the same font settings, the editor reuses the same style object instead of creating new ones.
Where it's used: CharFlyweight + CharFlyweightFactory
🧩 Class Breakdown
1️⃣ CharFlyweight
Represents a character along with its styling (font, size, bold, italic). Instead of storing styling for every character separately, we share these objects.
@dataclass
class CharFlyweight:
ch: str
font_name: str
font_size: int
is_bold: bool
is_italic: bool
2️⃣ CharFlyweightFactory
The heart of the Flyweight pattern. It stores previously created styles and reuses them when the same combination appears again.
This significantly reduces memory usage when thousands of characters share the same style.
3️⃣ TextRow
Represents a single row in the editor. Handles:
This keeps row-level logic clean and modular.
4️⃣ Solution (Main Editor Controller)
This is the main class that:
Supports features like:
Recommended by LinkedIn
Full code:
from dataclasses import dataclass, field
from typing import List, Dict, Optional
class Solution:
def __init__(self):
self.factory = CharFlyweightFactory()
self.rows: List[TextRow] = []
def init(self, helper):
self.helper = helper
def add_character(self, row: int, column: int, ch: str, font_name: str,
font_size: int, is_bold: bool, is_italic: bool):
"""
Add a character at a specific row and column.
"""
while row >= len(self.rows):
self.rows.append(TextRow())
flyweight = self.factory.create_style(ch, font_name, font_size, is_bold, is_italic)
self.rows[row].add_character(flyweight, column)
def get_style(self, row: int, col: int) -> str:
"""
Retrieve the style of a character at a specific row and column.
"""
if row < 0 or row >= len(self.rows):
return ""
flyweight = self.rows[row].get_flyweight(col)
return flyweight.get_char_and_style() if flyweight else ""
def read_line(self, row: int) -> str:
"""
Read a full line at a specific row.
"""
if row < 0 or row >= len(self.rows):
return ""
flyweights = self.rows[row].read_line()
return ''.join(flyweight.get_char() for flyweight in flyweights)
def delete_character(self, row: int, col: int) -> bool:
"""
Delete a character at a specific row and column.
"""
if row < 0 or row >= len(self.rows):
return False
return self.rows[row].delete_character(col)
@dataclass
class CharFlyweight:
"""
Represents a character with its font style attributes.
"""
ch: str
font_name: str
font_size: int
is_bold: bool
is_italic: bool
def get_char(self) -> str:
"""Returns the character."""
return self.ch
def get_char_and_style(self) -> str:
"""Returns a formatted string with character and its style."""
style = f"{self.ch}-{self.font_name}-{self.font_size}"
if self.is_bold:
style += "-b"
if self.is_italic:
style += "-i"
return style
class CharFlyweightFactory:
"""
Factory for creating and reusing CharFlyweight instances.
"""
def __init__(self):
self.map: Dict[str, CharFlyweight] = {}
def create_style(self, ch: str, font_name: str, font_size: int,
is_bold: bool, is_italic: bool) -> 'CharFlyweight':
key = f"{ch}-{font_name}-{font_size}"
if is_bold:
key += "-b"
if is_italic:
key += "-i"
if key not in self.map:
self.map[key] = CharFlyweight(ch, font_name, font_size, is_bold, is_italic)
return self.map[key]
class TextRow:
"""
Represents a single row in the text editor.
"""
def __init__(self):
self.data: List[CharFlyweight] = []
def add_character(self, ch: CharFlyweight, column: int):
"""
Add a character at a specific column.
"""
# keep behavior: if column > length, append; else insert at column
if column >= len(self.data):
self.data.append(ch)
else:
self.data.insert(column, ch)
def get_flyweight(self, column: int) -> Optional['CharFlyweight']:
"""
Retrieve a CharFlyweight at the given column.
"""
if 0 <= column < len(self.data):
return self.data[column]
return None
def read_line(self) -> List['CharFlyweight']:
"""
Return the entire row as a list of CharFlyweights.
"""
return self.data
def delete_character(self, col: int) -> bool:
"""
Delete a character at a specific column.
"""
if 0 <= col < len(self.data):
self.data.pop(col)
return True
return False
🏗️ UML Class Diagram Analysis
The diagram illustrates how the classes are structured to manage character data and styles efficiently using the Flyweight pattern.
1. CharFlyweight (Flyweight)
Relationships:
2. CharFlyweightFactory (Flyweight Factory)
3. TextRow
4. Solution (Client/Context)
🧠 Key Learnings
✔ How real text editors avoid memory overhead
✔ Importance of separating logic into small, meaningful classes
✔ How the Flyweight pattern improves performance in high-data applications
✔ Clean LLD structuring to simulate real-world systems
#Python #DesignPatterns #SoftwareDesign #FlyWeightPattern #FactoryPattern #LLD #BackendDevelopment #CleanCode #OOP