<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Shift Elevate]]></title><description><![CDATA[Shift Elevate]]></description><link>https://newsletter.shiftelevate.dev</link><image><url>https://newsletter.shiftelevate.dev/img/substack.png</url><title>Shift Elevate</title><link>https://newsletter.shiftelevate.dev</link></image><generator>Substack</generator><lastBuildDate>Thu, 02 Jul 2026 17:29:10 GMT</lastBuildDate><atom:link href="https://newsletter.shiftelevate.dev/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Shift Elevate]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[shiftelevate@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[shiftelevate@substack.com]]></itunes:email><itunes:name><![CDATA[Shift Elevate]]></itunes:name></itunes:owner><itunes:author><![CDATA[Shift Elevate]]></itunes:author><googleplay:owner><![CDATA[shiftelevate@substack.com]]></googleplay:owner><googleplay:email><![CDATA[shiftelevate@substack.com]]></googleplay:email><googleplay:author><![CDATA[Shift Elevate]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Visitor Pattern: Add New Operations to Object Structures Without Changing Them]]></title><description><![CDATA[Master the Visitor design pattern in Java with step-by-step implementation, clinic appointment management examples, and practical code for adding new operations to object structures without modifying existing classes.]]></description><link>https://newsletter.shiftelevate.dev/p/visitor-pattern-add-new-operations-to-object-structures-without-changing-them</link><guid isPermaLink="false">https://newsletter.shiftelevate.dev/p/visitor-pattern-add-new-operations-to-object-structures-without-changing-them</guid><dc:creator><![CDATA[Shift Elevate]]></dc:creator><pubDate>Mon, 11 May 2026 12:26:23 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/33c62e38-4b24-466c-a4e2-a1cc565c6489_1200x630.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>The Pain of Scattered Business Logic</h2><p><strong>Picture this:</strong> You're building a clinic management system. You have three appointment types: general consultations, lab tests, and surgeries. Each is modelled as a clean class. Your first feature request is straightforward: calculate the billing fee for each appointment. You add a <em><mark data-color="#cfe2f3" style="background-color: rgb(207, 226, 243); color: rgb(0, 0, 0);">calculateFee()</mark></em> method to each class. Done.</p><p>Then the insurance team needs claim codes per appointment type. You add <em><mark data-color="#cfe2f3" style="background-color: rgb(207, 226, 243); color: rgb(0, 0, 0);">generateClaimCode()</mark></em> to each class. Then admin wants appointment summaries for records. You add <em><mark data-color="#cfe2f3" style="background-color: rgb(207, 226, 243); color: rgb(0, 0, 0);">generateSummary()</mark></em>. The compliance team needs discharge notes. Another method on every class.</p><p>Six months in, your clean appointment classes look like this:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;03f55541-b600-41b9-969f-b54cb0a5055c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">public class Surgery {
    private String patientName;
    private String procedureName;
    private int durationMinutes;

    public double calculateFee() { /* billing logic */ }
    public String generateClaimCode() { /* insurance code */ }
    public String generateSummary() { /* appointment summary */ }
    public String generateDischargeNote() { /* discharge logic */ }
    // ...more unrelated methods keep piling up
}</code></pre></div><p>Every new operation means modifying every appointment class. Your classes have stopped being models of clinical appointments. They've become dumping grounds for every feature the business ever asked for. Adding a new operation is risky: you touch every class, risk introducing bugs, and trigger a full regression cycle.</p><p>The <strong>Visitor</strong> pattern solves this by separating operations from the object structure they work on. New operations become new visitor classes, and you never touch the appointment classes again.</p><h2>Understanding the Visitor Pattern</h2><div class="callout-block" data-callout="true"><p>The Visitor pattern lets you define a new operation without changing the classes of the elements it operates on. You create a visitor object that encapsulates the operation, and each element in the structure "accepts" the visitor and delegates the operation back to it.</p></div><p>Think of it like a clinic administrator processing different appointment types: the administrator <em>(visitor)</em> knows how to handle a general consultation, a lab test, and a surgery differently. Each appointment doesn't need to know <em>how</em> it gets processed; it just hands itself to the administrator. When the insurance team sends their own claims processor, the appointments don't change, only the processor does.</p><p>This pattern promotes <strong>Open/Closed Principle</strong>, <strong>Single Responsibility</strong>, and <strong>Separation of Concerns</strong> by keeping operations in dedicated visitor classes rather than scattered across the element hierarchy.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!EvoI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F780e5d53-55aa-4de5-bfea-17cd85af9bf5_840x440.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!EvoI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F780e5d53-55aa-4de5-bfea-17cd85af9bf5_840x440.png 424w, https://substackcdn.com/image/fetch/$s_!EvoI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F780e5d53-55aa-4de5-bfea-17cd85af9bf5_840x440.png 848w, https://substackcdn.com/image/fetch/$s_!EvoI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F780e5d53-55aa-4de5-bfea-17cd85af9bf5_840x440.png 1272w, https://substackcdn.com/image/fetch/$s_!EvoI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F780e5d53-55aa-4de5-bfea-17cd85af9bf5_840x440.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!EvoI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F780e5d53-55aa-4de5-bfea-17cd85af9bf5_840x440.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/780e5d53-55aa-4de5-bfea-17cd85af9bf5_840x440.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!EvoI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F780e5d53-55aa-4de5-bfea-17cd85af9bf5_840x440.png 424w, https://substackcdn.com/image/fetch/$s_!EvoI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F780e5d53-55aa-4de5-bfea-17cd85af9bf5_840x440.png 848w, https://substackcdn.com/image/fetch/$s_!EvoI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F780e5d53-55aa-4de5-bfea-17cd85af9bf5_840x440.png 1272w, https://substackcdn.com/image/fetch/$s_!EvoI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F780e5d53-55aa-4de5-bfea-17cd85af9bf5_840x440.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p style="text-align: center;">Visitor Pattern Components</p><h3>Core Components</h3><ul><li><p><strong>Element Interface</strong><a href="#element-interface">:</a> Declares an <em><mark data-color="#cfe2f3" style="background-color: rgb(207, 226, 243); color: rgb(0, 0, 0);">accept(visitor)</mark></em> method that each concrete element must implement</p></li><li><p><strong>Visitor Interface</strong><a href="#visitor-interface">:</a> Declares a <em><mark data-color="#cfe2f3" style="background-color: rgb(207, 226, 243); color: rgb(0, 0, 0);">visit()</mark></em> method overload for each concrete element type</p></li><li><p><strong>Concrete Elements</strong><a href="#concrete-elements">:</a> The object structure <em>(GeneralConsultation, LabTest, Surgery)</em>. Each calls <em><mark data-color="#cfe2f3" style="background-color: rgb(207, 226, 243); color: rgb(0, 0, 0);">visitor.visit(this)</mark></em> in its <em><mark data-color="#cfe2f3" style="background-color: rgb(207, 226, 243); color: rgb(0, 0, 0);">accept</mark></em> method</p></li><li><p><strong>Concrete Visitors</strong><a href="#concrete-visitors">:</a> Implement the operation for every element type <em>(BillingVisitor, InsuranceClaimVisitor)</em></p></li></ul><h2>Complete Java Implementation</h2><p>Let's build a clinic management system that demonstrates the Visitor pattern's power in adding new operations without touching appointment classes.</p><h3>Element Interface</h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;b66f6d90-1d3a-414f-baa9-345b0623237d&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">public interface Appointment {
    void accept(AppointmentVisitor visitor);
    String getPatientName();
}</code></pre></div><h3>Visitor Interface</h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;e4d1b07f-def9-4122-b4c2-fdd64d7ee71f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">public interface AppointmentVisitor {
    void visit(GeneralConsultation consultation);
    void visit(LabTest labTest);
    void visit(Surgery surgery);
}</code></pre></div><h3>Concrete Elements</h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;43569102-d115-4c35-8523-2b8a6dd14a8b&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">public class GeneralConsultation implements Appointment {
    private String patientName;
    private String doctorName;

    public GeneralConsultation(String patientName, String doctorName) {
        this.patientName = patientName;
        this.doctorName = doctorName;
    }

    public String getDoctorName() { return doctorName; }

    @Override public String getPatientName() { return patientName; }
    @Override public void accept(AppointmentVisitor visitor) { visitor.visit(this); }
}

public class LabTest implements Appointment {
    private String patientName;
    private String testType;

    public LabTest(String patientName, String testType) {
        this.patientName = patientName;
        this.testType = testType;
    }

    public String getTestType() { return testType; }

    @Override public String getPatientName() { return patientName; }
    @Override public void accept(AppointmentVisitor visitor) { visitor.visit(this); }
}

public class Surgery implements Appointment {
    private String patientName;
    private String procedureName;

    public Surgery(String patientName, String procedureName) {
        this.patientName = patientName;
        this.procedureName = procedureName;
    }

    public String getProcedureName() { return procedureName; }

    @Override public String getPatientName() { return patientName; }
    @Override public void accept(AppointmentVisitor visitor) { visitor.visit(this); }
}</code></pre></div><h3>Concrete Visitors</h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;e1bcf26d-85d4-42c9-adab-a276a28c291e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">public class BillingVisitor implements AppointmentVisitor {
    private double totalBill;

    @Override
    public void visit(GeneralConsultation consultation) {
        double fee = 80.00;
        totalBill += fee;
        System.out.println(consultation.getPatientName() + " [Consultation] $" + fee + " (Dr. " + consultation.getDoctorName() + ")");
    }

    @Override
    public void visit(LabTest labTest) {
        double fee = 45.00;
        totalBill += fee;
        System.out.println(labTest.getPatientName() + " [Lab Test] $" + fee + " (" + labTest.getTestType() + ")");
    }

    @Override
    public void visit(Surgery surgery) {
        double fee = 500.00;
        totalBill += fee;
        System.out.println(surgery.getPatientName() + " [Surgery] $" + fee + " (" + surgery.getProcedureName() + ")");
    }

    public double getTotalBill() { return totalBill; }
}</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;ac8662ab-46a2-4a42-bc3d-331af93aca82&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">public class InsuranceClaimVisitor implements AppointmentVisitor {

    @Override
    public void visit(GeneralConsultation consultation) {
        System.out.println(consultation.getPatientName() + " Code: GP-001 Type: General Practice");
    }

    @Override
    public void visit(LabTest labTest) {
        System.out.println(labTest.getPatientName() + " Code: LT-042 Type: Diagnostic Lab");
    }

    @Override
    public void visit(Surgery surgery) {
        System.out.println(surgery.getPatientName() + " Code: SG-301 Type: Surgical Procedure");
    }
}</code></pre></div><h3>Client</h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;695fe685-4446-4586-8f7c-3cfe7f5b58a7&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">public class ClinicBillingDemo {
    public static void main(String[] args) {
        System.out.println("=== Clinic Management System ===\n");

        List&lt;Appointment&gt; appointments = Arrays.asList(
            new GeneralConsultation("Alice Johnson", "Smith"),
            new LabTest("Bob Williams", "Blood Panel"),
            new Surgery("Carol Davis", "Appendectomy"),
            new GeneralConsultation("David Brown", "Patel")
        );

        System.out.println("--- Insurance Claims ---");
        InsuranceClaimVisitor claims = new InsuranceClaimVisitor();
        for (Appointment appointment : appointments) {
            appointment.accept(claims);
        }

        System.out.println("\n--- Patient Bills ---");
        BillingVisitor billing = new BillingVisitor();
        for (Appointment appointment : appointments) {
            appointment.accept(billing);
        }
        System.out.println("Total: $" + billing.getTotalBill());
    }
}</code></pre></div><h4>Expected Output:</h4><pre><code>=== Clinic Management System ===

--- Insurance Claims ---
Alice Johnson Code: GP-001 Type: General Practice
Bob Williams Code: LT-042 Type: Diagnostic Lab
Carol Davis Code: SG-301 Type: Surgical Procedure
David Brown Code: GP-001 Type: General Practice

--- Patient Bills ---
Alice Johnson [Consultation] $80.0 (Dr. Smith)
Bob Williams [Lab Test] $45.0 (Blood Panel)
Carol Davis [Surgery] $500.0 (Appendectomy)
David Brown [Consultation] $80.0 (Dr. Patel)
Total: $705.0</code></pre><h3>&#128640; Get the Complete Implementation</h3><p>The full code with additional visitors (risk analysis, compliance audit) is available in our <a href="https://github.com/shift-elevate/design-patterns">Design Patterns Repository</a>.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;47c1fffe-4eb1-4147-9704-f8309a9a8e6e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash"># Clone and run the complete demo
git clone https://github.com/shift-elevate/design-patterns.git
cd design-patterns
mvn test -Dtest=VisitorPatternTest</code></pre></div><h2>Real World Examples</h2><p>The Visitor pattern is extensively used in real-world systems where stable structures need evolving operations:</p><h3>1. Compiler Abstract Syntax Trees</h3><p>Compilers build an AST <em>(Abstract Syntax Tree)</em> representing the parsed source code. Different compiler phases <em>(type checking, optimisation, code generation, linting)</em> are implemented as separate visitors that traverse the same tree. The AST node classes remain untouched while each new compiler phase is a new visitor, making compiler toolchains highly extensible.</p><h3>2. Document Export</h3><p>Applications that model documents as a tree of elements <em>(paragraphs, headings, tables, images)</em> use the Visitor pattern for export operations. Each export format <em>(PDF, HTML, plain text)</em> is implemented as a separate visitor that knows how to render every element type, without any element class needing to know about export formats.</p><h3>3. Static Code Analysis</h3><p>Static analysis tools analyse source code by visiting AST nodes. Each analysis rule <em>(detect null dereference, find unused variables, enforce naming conventions)</em> is an independent visitor. Adding a new rule means adding a new visitor, and the AST structure and existing rules are never modified.</p><h2>When to Use the Visitor Pattern</h2><p>Understanding when to apply the Visitor pattern is crucial for making the right architectural decisions. Here's when it shines and when alternatives might be better:</p><h3>&#9989;<strong> Ideal Scenarios:</strong></h3><ul><li><p>You have a stable object structure but need to frequently add new operations.</p></li><li><p>You want to keep unrelated operations out of the element classes.</p></li><li><p>A class hierarchy contains many distinct types and you need type-specific behaviour across multiple operations.</p></li><li><p>You need to accumulate state across elements during traversal <em>(like totalling tax across all assets)</em>.</p></li></ul><h3>&#10060;<strong> Skip It When:</strong></h3><ul><li><p>The element hierarchy changes frequently, as adding a new element type requires updating every visitor.</p></li><li><p>The object structure is simple and has only a handful of operations that rarely change.</p></li><li><p>Encapsulation of element internals is critical, as visitors often need access to internal fields.</p></li><li><p>The double-dispatch mechanism adds more complexity than the problem warrants.</p></li></ul><h2>Next Steps: Apply the Visitor Pattern in Your Project</h2><p>Ready to implement the Visitor pattern in your own projects? Here's a structured approach to get you started:</p><ol><li><p><strong>Identify Stable Structures</strong>: Find class hierarchies that rarely gain new types but frequently gain new operations.</p></li><li><p><strong>Define the Visitor Interface</strong>: Declare one method per concrete element type so each visitor knows how to handle every element.</p></li><li><p><strong>Add Accept to Elements</strong>: Each element implements a method that hands itself to the visitor, keeping all operation logic out of the element.</p></li><li><p><strong>Create Concrete Visitors</strong>: Implement each operation as a dedicated visitor class with its own state.</p></li><li><p><strong>Drive from the Client</strong>: Iterate the element collection and let each element accept the visitor in turn.</p></li></ol><p>The Visitor pattern transforms bloated element classes into clean, focused models by relocating operations to dedicated visitor classes that can evolve independently. Your element classes model your domain; your visitor classes model what you do with it.</p>]]></content:encoded></item><item><title><![CDATA[Queue]]></title><description><![CDATA[Manage Tasks in Order with FIFO]]></description><link>https://newsletter.shiftelevate.dev/p/queue-7021</link><guid isPermaLink="false">https://newsletter.shiftelevate.dev/p/queue-7021</guid><dc:creator><![CDATA[Shift Elevate]]></dc:creator><pubDate>Wed, 06 May 2026 09:43:19 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/dc5b70a8-ea8c-4a56-8197-922333de046a_1200x630.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>Introduction</h2><p>A queue is a linear data structure that follows the First-In-First-Out (FIFO) principle. Think of it like a line at a coffee shop: the first person to join the line is the first person to be served. Queues are essential for managing tasks in order, from handling print jobs to processing requests in web servers.</p><div class="pullquote"><p><span data-color="#990000" style="color: rgb(153, 0, 0);">The code examples in this article use JavaScript's built-in array API (push, shift, etc.). The underlying concepts apply to queues in any language, but method names may differ.</span></p></div><h2>Explanation</h2><p>A queue operates on two main principles:</p><p><strong>FIFO (First-In-First-Out)</strong>: Elements are removed in the same order they were added. The first element you add is the first one you remove.</p><p><strong>Core Operations:</strong></p><ul><li><p><strong>Enqueue</strong>: Add an element to the back of the queue</p></li><li><p><strong>Dequeue</strong>: Remove and return the element from the front of the queue</p></li><li><p><strong>Peek</strong>: View the front element without removing it</p></li><li><p><strong>isEmpty</strong>: Check if the queue has no elements</p></li></ul><p>Let's see FIFO in action with a simple example:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;javascript&quot;,&quot;nodeId&quot;:&quot;0db38e97-de50-42c9-af96-7af50c4f6b7f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-javascript">const queue = [];

function enqueue(element) {
  queue.push(element);
}

function dequeue() {
  return queue.shift();
}

enqueue('First');
enqueue('Second');
enqueue('Third');
console.log('Queue:', queue);

const item1 = dequeue();
console.log('Removed:', item1);

const item2 = dequeue();
console.log('Removed:', item2);

console.log('Remaining:', queue);</code></pre></div><blockquote><p><strong><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=queue%2Fqueue_a.mp4&amp;label=FIFO+Concept&amp;utm_campaign=queue&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);">Watch the visualization:</span></a></strong><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=queue%2Fqueue_a.mp4&amp;label=FIFO+Concept&amp;utm_campaign=queue&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);"> Notice how &#8220;</span></a><em><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=queue%2Fqueue_a.mp4&amp;label=FIFO+Concept&amp;utm_campaign=queue&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);">First&#8221;</span></a></em><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=queue%2Fqueue_a.mp4&amp;label=FIFO+Concept&amp;utm_campaign=queue&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);"> goes in first and comes out first, maintaining the exact order elements were added.</span></a></p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!DG2i!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe3c41ec-d9ca-4e84-9628-3bff967d9dae_960x525.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!DG2i!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe3c41ec-d9ca-4e84-9628-3bff967d9dae_960x525.png 424w, https://substackcdn.com/image/fetch/$s_!DG2i!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe3c41ec-d9ca-4e84-9628-3bff967d9dae_960x525.png 848w, https://substackcdn.com/image/fetch/$s_!DG2i!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe3c41ec-d9ca-4e84-9628-3bff967d9dae_960x525.png 1272w, https://substackcdn.com/image/fetch/$s_!DG2i!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe3c41ec-d9ca-4e84-9628-3bff967d9dae_960x525.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!DG2i!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe3c41ec-d9ca-4e84-9628-3bff967d9dae_960x525.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fe3c41ec-d9ca-4e84-9628-3bff967d9dae_960x525.png&quot;,&quot;srcNoWatermark&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/61d5cb9a-4517-494e-834c-44bb0748e0b9_960x525.gif&quot;,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:728,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!DG2i!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe3c41ec-d9ca-4e84-9628-3bff967d9dae_960x525.png 424w, https://substackcdn.com/image/fetch/$s_!DG2i!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe3c41ec-d9ca-4e84-9628-3bff967d9dae_960x525.png 848w, https://substackcdn.com/image/fetch/$s_!DG2i!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe3c41ec-d9ca-4e84-9628-3bff967d9dae_960x525.png 1272w, https://substackcdn.com/image/fetch/$s_!DG2i!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe3c41ec-d9ca-4e84-9628-3bff967d9dae_960x525.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>The key insight is that queues maintain order naturally. Unlike stacks where you work with the most recent item, queues ensure fair processing where older items get handled first.</p><h2>Implementation</h2><p>We'll implement a queue using an array with methods that maintain the FIFO principle. While arrays provide flexibility, we'll restrict operations to queue-specific behaviours.</p><h3>Queue Class</h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;javascript&quot;,&quot;nodeId&quot;:&quot;90736f53-3da5-4f32-a45e-9439fdeed190&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-javascript">class Queue {
  constructor() {
    this.items = [];
  }

  // Add element to the back of the queue
  enqueue(element) {
    this.items.push(element);
    console.log(`Enqueued: ${element}`);
  }

  // Remove and return element from the front
  dequeue() {
    if (this.isEmpty()) {
      console.log('Queue is empty');
      return null;
    }
    const removed = this.items.shift();
    console.log(`Dequeued: ${removed}`);
    return removed;
  }

  // View the front element without removing it
  peek() {
    if (this.isEmpty()) {
      return null;
    }
    return this.items[0];
  }

  // Check if queue is empty
  isEmpty() {
    return this.items.length === 0;
  }

  // Get the size of the queue
  size() {
    return this.items.length;
  }

  // Clear all elements
  clear() {
    this.items = [];
    console.log('Queue cleared');
  }

  // Display queue contents
  print() {
    console.log('Queue:', this.items.join(' &lt;- '));
  }
}</code></pre></div><h3>Enqueue Operation</h3><p>The enqueue operation adds elements to the back of the queue using <em><mark data-color="#cfe2f3" style="background-color: rgb(207, 226, 243); color: rgb(0, 0, 0);">push()</mark></em>. This ensures new elements always go to the end, maintaining FIFO order.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;javascript&quot;,&quot;nodeId&quot;:&quot;adc295cd-e6d7-4f05-a124-00cfb0cff2bf&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-javascript">const queue = [];

function enqueue(element) {
  queue.push(element);
  console.log(`Enqueued: ${element}`);
}

enqueue('Download file');
enqueue('Process data');
enqueue('Send email');

console.log('Queue:', queue);</code></pre></div><blockquote><p><strong><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=queue%2Fqueue_b.mp4&amp;label=Enqueue+Operation&amp;utm_campaign=queue&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);">Watch the visualization:</span></a></strong><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=queue%2Fqueue_b.mp4&amp;label=Enqueue+Operation&amp;utm_campaign=queue&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);"> Each &#8220;</span></a><em><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=queue%2Fqueue_b.mp4&amp;label=Enqueue+Operation&amp;utm_campaign=queue&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);">push()&#8221;</span></a></em><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=queue%2Fqueue_b.mp4&amp;label=Enqueue+Operation&amp;utm_campaign=queue&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);"> adds an element to the end. The queue grows from left to right, with new tasks joining at the back of the line.</span></a></p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!cjS2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25d238ad-e706-4846-859e-639dde8ee3d5_960x525.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!cjS2!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25d238ad-e706-4846-859e-639dde8ee3d5_960x525.gif 424w, https://substackcdn.com/image/fetch/$s_!cjS2!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25d238ad-e706-4846-859e-639dde8ee3d5_960x525.gif 848w, https://substackcdn.com/image/fetch/$s_!cjS2!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25d238ad-e706-4846-859e-639dde8ee3d5_960x525.gif 1272w, https://substackcdn.com/image/fetch/$s_!cjS2!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25d238ad-e706-4846-859e-639dde8ee3d5_960x525.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!cjS2!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25d238ad-e706-4846-859e-639dde8ee3d5_960x525.gif" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/25d238ad-e706-4846-859e-639dde8ee3d5_960x525.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!cjS2!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25d238ad-e706-4846-859e-639dde8ee3d5_960x525.gif 424w, https://substackcdn.com/image/fetch/$s_!cjS2!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25d238ad-e706-4846-859e-639dde8ee3d5_960x525.gif 848w, https://substackcdn.com/image/fetch/$s_!cjS2!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25d238ad-e706-4846-859e-639dde8ee3d5_960x525.gif 1272w, https://substackcdn.com/image/fetch/$s_!cjS2!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25d238ad-e706-4846-859e-639dde8ee3d5_960x525.gif 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Dequeue Operation</h3><p>The dequeue operation removes and returns the element from the front using <em><mark data-color="#cfe2f3" style="background-color: rgb(207, 226, 243); color: rgb(0, 0, 0);">shift()</mark></em>. This maintains FIFO by always serving the oldest element first.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;javascript&quot;,&quot;nodeId&quot;:&quot;4dd417da-e156-4b51-8be4-653830d20588&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-javascript">const queue = ['Download file', 'Process data', 'Send email'];

function dequeue() {
  return queue.shift();
}

console.log('Initial queue:', queue);

const processed1 = dequeue();
const processed2 = dequeue();

console.log('Processed:', processed1, 'and', processed2);
console.log('Remaining:', queue);</code></pre></div><blockquote><p><strong><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=queue%2Fqueue_c.mp4&amp;label=Dequeue+Operation&amp;utm_campaign=queue&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);">Watch the visualization:</span></a></strong><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=queue%2Fqueue_c.mp4&amp;label=Dequeue+Operation&amp;utm_campaign=queue&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);"> Notice how </span></a><em><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=queue%2Fqueue_c.mp4&amp;label=Dequeue+Operation&amp;utm_campaign=queue&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><mark data-color="#cfe2f3" style="background-color: rgb(207, 226, 243); color: rgb(0, 0, 0);"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);">shift()</span></mark></a></em><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=queue%2Fqueue_c.mp4&amp;label=Dequeue+Operation&amp;utm_campaign=queue&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);"> always removes from index 0. The first task in gets processed first, and the remaining tasks shift forward in the queue.</span></a></p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7p2J!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55065568-ddd6-43c0-b29b-7002c69beccd_960x525.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7p2J!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55065568-ddd6-43c0-b29b-7002c69beccd_960x525.gif 424w, https://substackcdn.com/image/fetch/$s_!7p2J!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55065568-ddd6-43c0-b29b-7002c69beccd_960x525.gif 848w, https://substackcdn.com/image/fetch/$s_!7p2J!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55065568-ddd6-43c0-b29b-7002c69beccd_960x525.gif 1272w, https://substackcdn.com/image/fetch/$s_!7p2J!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55065568-ddd6-43c0-b29b-7002c69beccd_960x525.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7p2J!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55065568-ddd6-43c0-b29b-7002c69beccd_960x525.gif" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/55065568-ddd6-43c0-b29b-7002c69beccd_960x525.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!7p2J!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55065568-ddd6-43c0-b29b-7002c69beccd_960x525.gif 424w, https://substackcdn.com/image/fetch/$s_!7p2J!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55065568-ddd6-43c0-b29b-7002c69beccd_960x525.gif 848w, https://substackcdn.com/image/fetch/$s_!7p2J!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55065568-ddd6-43c0-b29b-7002c69beccd_960x525.gif 1272w, https://substackcdn.com/image/fetch/$s_!7p2J!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55065568-ddd6-43c0-b29b-7002c69beccd_960x525.gif 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Peek Operation</h3><p>Peek lets you view the front element without removing it. This is useful when you need to check what's next without actually processing it.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;javascript&quot;,&quot;nodeId&quot;:&quot;ca76a3e1-e9c4-4b18-a92d-a329d148420b&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-javascript">const queue = ['Download file', 'Process data', 'Send email'];

function peek() {
  return queue[0];
}

const nextTask = peek();

console.log('Next task:', nextTask);
console.log('Queue unchanged:', queue);</code></pre></div><blockquote><p><strong><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=queue%2Fqueue_d.mp4&amp;label=Peek+Operation&amp;utm_campaign=queue&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);">Watch the visualization:</span></a></strong><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=queue%2Fqueue_d.mp4&amp;label=Peek+Operation&amp;utm_campaign=queue&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);"> The </span></a><em><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=queue%2Fqueue_d.mp4&amp;label=Peek+Operation&amp;utm_campaign=queue&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><mark data-color="#cfe2f3" style="background-color: rgb(207, 226, 243); color: rgb(0, 0, 0);"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);">nextJob</span></mark></a></em><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=queue%2Fqueue_d.mp4&amp;label=Peek+Operation&amp;utm_campaign=queue&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);"> variable gets the value, but the </span></a><em><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=queue%2Fqueue_d.mp4&amp;label=Peek+Operation&amp;utm_campaign=queue&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><mark data-color="#cfe2f3" style="background-color: rgb(207, 226, 243); color: rgb(0, 0, 0);"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);">jobQueue</span></mark></a></em><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=queue%2Fqueue_d.mp4&amp;label=Peek+Operation&amp;utm_campaign=queue&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);"> array remains unchanged. Peek is a read-only operation.</span></a></p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!G8bb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4172c3e0-92b6-4ce9-a95f-96f086aa5e16_960x525.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!G8bb!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4172c3e0-92b6-4ce9-a95f-96f086aa5e16_960x525.gif 424w, https://substackcdn.com/image/fetch/$s_!G8bb!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4172c3e0-92b6-4ce9-a95f-96f086aa5e16_960x525.gif 848w, https://substackcdn.com/image/fetch/$s_!G8bb!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4172c3e0-92b6-4ce9-a95f-96f086aa5e16_960x525.gif 1272w, https://substackcdn.com/image/fetch/$s_!G8bb!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4172c3e0-92b6-4ce9-a95f-96f086aa5e16_960x525.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!G8bb!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4172c3e0-92b6-4ce9-a95f-96f086aa5e16_960x525.gif" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4172c3e0-92b6-4ce9-a95f-96f086aa5e16_960x525.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!G8bb!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4172c3e0-92b6-4ce9-a95f-96f086aa5e16_960x525.gif 424w, https://substackcdn.com/image/fetch/$s_!G8bb!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4172c3e0-92b6-4ce9-a95f-96f086aa5e16_960x525.gif 848w, https://substackcdn.com/image/fetch/$s_!G8bb!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4172c3e0-92b6-4ce9-a95f-96f086aa5e16_960x525.gif 1272w, https://substackcdn.com/image/fetch/$s_!G8bb!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4172c3e0-92b6-4ce9-a95f-96f086aa5e16_960x525.gif 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div><hr></div><h2>Complexity</h2><p><strong>Time Complexity:</strong></p><ul><li><p>Enqueue: O(1) - Adding to the end of an array is constant time</p></li><li><p>Dequeue: O(n) - Removing from the front requires shifting all remaining elements</p></li><li><p>Peek: O(1) - Accessing the first element is constant time</p></li><li><p>isEmpty/size: O(1) - Checking length is constant time</p></li></ul><p><strong>Space Complexity:</strong> O(n) - Where n is the number of elements stored in the queue</p>]]></content:encoded></item><item><title><![CDATA[Array]]></title><description><![CDATA[The Foundation of Every Data Structure]]></description><link>https://newsletter.shiftelevate.dev/p/array</link><guid isPermaLink="false">https://newsletter.shiftelevate.dev/p/array</guid><dc:creator><![CDATA[Shift Elevate]]></dc:creator><pubDate>Tue, 28 Apr 2026 06:15:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/27c5110c-6f5b-466c-b87e-3c62e6f02386_1200x630.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>Introduction</h2><p>Imagine you need to store a list of your top 5 favorite songs. You could create five separate variables, or you could use a single array. Arrays are the most fundamental data structure in programming, an ordered collection that lets you store and access multiple values under one name.</p><div class="pullquote"><p><em><span data-color="#990000" style="color: rgb(153, 0, 0);">The code examples in this article use JavaScript's built-in array API (push, pop, etc.). The underlying concepts apply to arrays in any language, but method names may differ.</span></em></p></div><h2>Explanation</h2><p>An array stores elements in <strong>contiguous, indexed slots</strong>. Each element has a position called an <strong>index</strong>, starting at 0.</p><p><strong>Core Operations</strong>:</p><ul><li><p><strong>Insert</strong>: Add an element to the array</p></li><li><p><strong>Delete</strong>: Remove an element from the array</p></li><li><p><strong>Seek</strong>: Read an element at a specific index</p></li><li><p><strong>Traverse</strong>: Visit every element in order</p></li></ul><p>Think of an array like a row of numbered lockers. Locker 0 holds the first item, locker 1 holds the second, and so on. You can jump directly to any locker if you know its number.</p><p>Let's see this in action:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;javascript&quot;,&quot;nodeId&quot;:&quot;ed4a6603-6342-4ec3-a200-c153e5e86ebf&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-javascript">const numbers = [10, 20, 30, 40, 50];

console.log('First element:', numbers[0]);
console.log('Second element:', numbers[1]);
console.log('Total elements:', numbers.length);</code></pre></div><blockquote><p><strong><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=array%2Farray_a.mp4&amp;label=Basic+Index+Access&amp;utm_campaign=array&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);">Watch the visualization</span></a></strong><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=array%2Farray_a.mp4&amp;label=Basic+Index+Access&amp;utm_campaign=array&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);">: Notice how index </span></a><em><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=array%2Farray_a.mp4&amp;label=Basic+Index+Access&amp;utm_campaign=array&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);">&#8220;0&#8221;</span></a></em><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=array%2Farray_a.mp4&amp;label=Basic+Index+Access&amp;utm_campaign=array&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);"> maps to the first element. The </span></a><em><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=array%2Farray_a.mp4&amp;label=Basic+Index+Access&amp;utm_campaign=array&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><mark data-color="#cfe2f3" style="background-color: rgb(207, 226, 243); color: rgb(0, 0, 0);"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);">length</span></mark></a></em><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=array%2Farray_a.mp4&amp;label=Basic+Index+Access&amp;utm_campaign=array&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);"> property always equals the highest index + 1. Arrays are zero-indexed, so a 5-element array has indices 0 through 4.</span></a></p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!vQJe!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3f894aa-f657-4f4f-b65c-cd13ea2f7ce2_2174x1188.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!vQJe!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3f894aa-f657-4f4f-b65c-cd13ea2f7ce2_2174x1188.gif 424w, https://substackcdn.com/image/fetch/$s_!vQJe!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3f894aa-f657-4f4f-b65c-cd13ea2f7ce2_2174x1188.gif 848w, https://substackcdn.com/image/fetch/$s_!vQJe!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3f894aa-f657-4f4f-b65c-cd13ea2f7ce2_2174x1188.gif 1272w, https://substackcdn.com/image/fetch/$s_!vQJe!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3f894aa-f657-4f4f-b65c-cd13ea2f7ce2_2174x1188.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!vQJe!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3f894aa-f657-4f4f-b65c-cd13ea2f7ce2_2174x1188.gif" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b3f894aa-f657-4f4f-b65c-cd13ea2f7ce2_2174x1188.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!vQJe!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3f894aa-f657-4f4f-b65c-cd13ea2f7ce2_2174x1188.gif 424w, https://substackcdn.com/image/fetch/$s_!vQJe!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3f894aa-f657-4f4f-b65c-cd13ea2f7ce2_2174x1188.gif 848w, https://substackcdn.com/image/fetch/$s_!vQJe!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3f894aa-f657-4f4f-b65c-cd13ea2f7ce2_2174x1188.gif 1272w, https://substackcdn.com/image/fetch/$s_!vQJe!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3f894aa-f657-4f4f-b65c-cd13ea2f7ce2_2174x1188.gif 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>The key insight: arrays give you <strong>O(1) random access</strong>. No matter how large the array, reading any element by index takes the same amount of time.</p><h2>Implementation</h2><h3>Creating Arrays</h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;javascript&quot;,&quot;nodeId&quot;:&quot;5b2e1f00-e3f9-4eac-9b36-be7bdddd172f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-javascript">// Empty array
const empty = [];

// Array with initial values
const numbers = [10, 20, 30, 40, 50];

// Access by index
console.log('Element at index 2:', numbers[2]);

// Last element using length
console.log('Last element:', numbers[numbers.length - 1]);</code></pre></div><blockquote><p><strong><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=array%2Farray_b.mp4&amp;label=Creating+Arrays&amp;utm_campaign=array&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);">Watch the visualization</span></a></strong><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=array%2Farray_b.mp4&amp;label=Creating+Arrays&amp;utm_campaign=array&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);">: Track how </span></a><em><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=array%2Farray_b.mp4&amp;label=Creating+Arrays&amp;utm_campaign=array&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><mark data-color="#cfe2f3" style="background-color: rgb(207, 226, 243); color: rgb(0, 0, 0);"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);">numbers[2]</span></mark></a></em><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=array%2Farray_b.mp4&amp;label=Creating+Arrays&amp;utm_campaign=array&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);"> directly jumps to index 2 without scanning through 0 and 1. That's direct access in action.</span></a></p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!p6Ba!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6068d352-0b66-42a8-ba1b-709e7ede0302_2174x1188.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!p6Ba!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6068d352-0b66-42a8-ba1b-709e7ede0302_2174x1188.gif 424w, https://substackcdn.com/image/fetch/$s_!p6Ba!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6068d352-0b66-42a8-ba1b-709e7ede0302_2174x1188.gif 848w, https://substackcdn.com/image/fetch/$s_!p6Ba!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6068d352-0b66-42a8-ba1b-709e7ede0302_2174x1188.gif 1272w, https://substackcdn.com/image/fetch/$s_!p6Ba!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6068d352-0b66-42a8-ba1b-709e7ede0302_2174x1188.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!p6Ba!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6068d352-0b66-42a8-ba1b-709e7ede0302_2174x1188.gif" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6068d352-0b66-42a8-ba1b-709e7ede0302_2174x1188.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!p6Ba!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6068d352-0b66-42a8-ba1b-709e7ede0302_2174x1188.gif 424w, https://substackcdn.com/image/fetch/$s_!p6Ba!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6068d352-0b66-42a8-ba1b-709e7ede0302_2174x1188.gif 848w, https://substackcdn.com/image/fetch/$s_!p6Ba!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6068d352-0b66-42a8-ba1b-709e7ede0302_2174x1188.gif 1272w, https://substackcdn.com/image/fetch/$s_!p6Ba!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6068d352-0b66-42a8-ba1b-709e7ede0302_2174x1188.gif 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Insert Operation</h3><p><em><mark data-color="#cfe2f3" style="background-color: rgb(207, 226, 243); color: rgb(0, 0, 0);">push()</mark></em> adds an element to the end of the array in O(1). No existing elements need to move.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;javascript&quot;,&quot;nodeId&quot;:&quot;7177037e-a555-4946-a1e1-25a9b24b08f3&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-javascript">const numbers = [10, 20];

numbers.push(30);
console.log('After push:', numbers);

numbers.push(40);
console.log('After another push:', numbers);

console.log('Length:', numbers.length);</code></pre></div><blockquote><p><strong><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=array%2Farray_c.mp4&amp;label=Insert+Operation&amp;utm_campaign=array&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);">Watch the visualization</span></a></strong><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=array%2Farray_c.mp4&amp;label=Insert+Operation&amp;utm_campaign=array&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);">: Each </span></a><em><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=array%2Farray_c.mp4&amp;label=Insert+Operation&amp;utm_campaign=array&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><mark data-color="#cfe2f3" style="background-color: rgb(207, 226, 243); color: rgb(0, 0, 0);"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);">push()</span></mark></a></em><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=array%2Farray_c.mp4&amp;label=Insert+Operation&amp;utm_campaign=array&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);"> places the new element at the next available slot at the end. The length grows by 1 each time, but nothing else shifts.</span></a></p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!eFGg!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01188df7-6f0f-4cd3-b643-ade212f15713_2174x1188.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!eFGg!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01188df7-6f0f-4cd3-b643-ade212f15713_2174x1188.gif 424w, https://substackcdn.com/image/fetch/$s_!eFGg!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01188df7-6f0f-4cd3-b643-ade212f15713_2174x1188.gif 848w, https://substackcdn.com/image/fetch/$s_!eFGg!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01188df7-6f0f-4cd3-b643-ade212f15713_2174x1188.gif 1272w, https://substackcdn.com/image/fetch/$s_!eFGg!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01188df7-6f0f-4cd3-b643-ade212f15713_2174x1188.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!eFGg!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01188df7-6f0f-4cd3-b643-ade212f15713_2174x1188.gif" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/01188df7-6f0f-4cd3-b643-ade212f15713_2174x1188.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!eFGg!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01188df7-6f0f-4cd3-b643-ade212f15713_2174x1188.gif 424w, https://substackcdn.com/image/fetch/$s_!eFGg!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01188df7-6f0f-4cd3-b643-ade212f15713_2174x1188.gif 848w, https://substackcdn.com/image/fetch/$s_!eFGg!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01188df7-6f0f-4cd3-b643-ade212f15713_2174x1188.gif 1272w, https://substackcdn.com/image/fetch/$s_!eFGg!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01188df7-6f0f-4cd3-b643-ade212f15713_2174x1188.gif 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Delete Operation</h3><p><em><mark data-color="#cfe2f3" style="background-color: rgb(207, 226, 243); color: rgb(0, 0, 0);">pop()</mark></em><mark data-color="#cfe2f3" style="background-color: rgb(207, 226, 243); color: rgb(0, 0, 0);"> </mark>removes and returns the last element in O(1). Like push, no other elements are affected.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;javascript&quot;,&quot;nodeId&quot;:&quot;3945753d-a416-468f-93e0-47b15178e552&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-javascript">const numbers = [10, 20, 30, 40];

const last = numbers.pop();
console.log('Removed:', last);
console.log('After pop:', numbers);

const second = numbers.pop();
console.log('Removed:', second);
console.log('After pop:', numbers);</code></pre></div><blockquote><p><strong><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=array%2Farray_d.mp4&amp;label=Delete+Operation&amp;utm_campaign=array&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);">Watch the visualization</span></a></strong><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=array%2Farray_d.mp4&amp;label=Delete+Operation&amp;utm_campaign=array&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);">: Each </span></a><em><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=array%2Farray_d.mp4&amp;label=Delete+Operation&amp;utm_campaign=array&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><mark data-color="#cfe2f3" style="background-color: rgb(207, 226, 243); color: rgb(0, 0, 0);"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);">pop()</span></mark></a></em><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=array%2Farray_d.mp4&amp;label=Delete+Operation&amp;utm_campaign=array&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);"> removes from the end and the length shrinks by 1. The remaining elements stay exactly where they are, so no shifting occurs.</span></a></p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5UzE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff43a113-f0de-4c23-8404-85e8498d6856_2174x1188.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5UzE!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff43a113-f0de-4c23-8404-85e8498d6856_2174x1188.gif 424w, https://substackcdn.com/image/fetch/$s_!5UzE!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff43a113-f0de-4c23-8404-85e8498d6856_2174x1188.gif 848w, https://substackcdn.com/image/fetch/$s_!5UzE!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff43a113-f0de-4c23-8404-85e8498d6856_2174x1188.gif 1272w, https://substackcdn.com/image/fetch/$s_!5UzE!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff43a113-f0de-4c23-8404-85e8498d6856_2174x1188.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5UzE!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff43a113-f0de-4c23-8404-85e8498d6856_2174x1188.gif" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ff43a113-f0de-4c23-8404-85e8498d6856_2174x1188.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!5UzE!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff43a113-f0de-4c23-8404-85e8498d6856_2174x1188.gif 424w, https://substackcdn.com/image/fetch/$s_!5UzE!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff43a113-f0de-4c23-8404-85e8498d6856_2174x1188.gif 848w, https://substackcdn.com/image/fetch/$s_!5UzE!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff43a113-f0de-4c23-8404-85e8498d6856_2174x1188.gif 1272w, https://substackcdn.com/image/fetch/$s_!5UzE!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff43a113-f0de-4c23-8404-85e8498d6856_2174x1188.gif 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Seek Operation</h3><p>Seeking retrieves an element directly by its index in O(1). No scanning required because the array computes the exact memory location instantly.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;javascript&quot;,&quot;nodeId&quot;:&quot;16332b3b-2aa1-40d1-9a87-e7669e3b1bf5&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-javascript">const numbers = [10, 20, 30, 40, 50];

console.log('Element at index 0:', numbers[0]);
console.log('Element at index 3:', numbers[3]);
console.log('Last element:', numbers[numbers.length - 1]);</code></pre></div><blockquote><p><strong><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=array%2Farray_e.mp4&amp;label=Seek+Operation&amp;utm_campaign=array&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);">Watch the visualization</span></a></strong><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=array%2Farray_e.mp4&amp;label=Seek+Operation&amp;utm_campaign=array&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);">: Whether you seek index 0 or index 4, the operation takes the same amount of time. That's the power of index-based access.</span></a></p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_kOd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8939cb15-082b-4d3d-9cdd-160a0ff1c34b_2174x1188.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_kOd!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8939cb15-082b-4d3d-9cdd-160a0ff1c34b_2174x1188.gif 424w, https://substackcdn.com/image/fetch/$s_!_kOd!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8939cb15-082b-4d3d-9cdd-160a0ff1c34b_2174x1188.gif 848w, https://substackcdn.com/image/fetch/$s_!_kOd!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8939cb15-082b-4d3d-9cdd-160a0ff1c34b_2174x1188.gif 1272w, https://substackcdn.com/image/fetch/$s_!_kOd!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8939cb15-082b-4d3d-9cdd-160a0ff1c34b_2174x1188.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_kOd!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8939cb15-082b-4d3d-9cdd-160a0ff1c34b_2174x1188.gif" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8939cb15-082b-4d3d-9cdd-160a0ff1c34b_2174x1188.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!_kOd!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8939cb15-082b-4d3d-9cdd-160a0ff1c34b_2174x1188.gif 424w, https://substackcdn.com/image/fetch/$s_!_kOd!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8939cb15-082b-4d3d-9cdd-160a0ff1c34b_2174x1188.gif 848w, https://substackcdn.com/image/fetch/$s_!_kOd!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8939cb15-082b-4d3d-9cdd-160a0ff1c34b_2174x1188.gif 1272w, https://substackcdn.com/image/fetch/$s_!_kOd!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8939cb15-082b-4d3d-9cdd-160a0ff1c34b_2174x1188.gif 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Traverse Operation</h3><p>Traversal visits every element in the array from index 0 to the last. Unlike seek, you don't know which index you want, so you need to process all of them. This costs O(n) since you touch every element once.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;javascript&quot;,&quot;nodeId&quot;:&quot;36754ee2-33c7-4a7a-9689-7b21d3a89048&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-javascript">const numbers = [10, 20, 30];

let total = 0;
for (let i = 0; i &lt; numbers.length; i++) {
  console.log(`Index ${i}: ${numbers[i]}`);
  total += numbers[i];
}

console.log('Total:', total);</code></pre></div><blockquote><p><strong><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=array%2Farray_f.mp4&amp;label=Traverse+Operation&amp;utm_campaign=array&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);">Watch the visualization:</span></a></strong><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=array%2Farray_f.mp4&amp;label=Traverse+Operation&amp;utm_campaign=array&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);"> The index &#8220;</span></a><em><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=array%2Farray_f.mp4&amp;label=Traverse+Operation&amp;utm_campaign=array&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);">i&#8221;</span></a></em><a href="https://shift-elevate.github.io/dsa/blog-previews/player.html?src=array%2Farray_f.mp4&amp;label=Traverse+Operation&amp;utm_campaign=array&amp;utm_medium=referral&amp;utm_source=newsletter.shiftelevate.dev"><span data-color="rgb(35, 61, 77)" style="color: rgb(35, 61, 77);"> steps through 0, 1, 2 in order, and every element is visited exactly once. The more elements in the array, the longer a traversal takes.</span></a></p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!uCpZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3651b0c-00b6-452a-b47d-ca05d1a54ec9_2174x1188.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!uCpZ!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3651b0c-00b6-452a-b47d-ca05d1a54ec9_2174x1188.gif 424w, https://substackcdn.com/image/fetch/$s_!uCpZ!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3651b0c-00b6-452a-b47d-ca05d1a54ec9_2174x1188.gif 848w, https://substackcdn.com/image/fetch/$s_!uCpZ!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3651b0c-00b6-452a-b47d-ca05d1a54ec9_2174x1188.gif 1272w, https://substackcdn.com/image/fetch/$s_!uCpZ!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3651b0c-00b6-452a-b47d-ca05d1a54ec9_2174x1188.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!uCpZ!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3651b0c-00b6-452a-b47d-ca05d1a54ec9_2174x1188.gif" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f3651b0c-00b6-452a-b47d-ca05d1a54ec9_2174x1188.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!uCpZ!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3651b0c-00b6-452a-b47d-ca05d1a54ec9_2174x1188.gif 424w, https://substackcdn.com/image/fetch/$s_!uCpZ!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3651b0c-00b6-452a-b47d-ca05d1a54ec9_2174x1188.gif 848w, https://substackcdn.com/image/fetch/$s_!uCpZ!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3651b0c-00b6-452a-b47d-ca05d1a54ec9_2174x1188.gif 1272w, https://substackcdn.com/image/fetch/$s_!uCpZ!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3651b0c-00b6-452a-b47d-ca05d1a54ec9_2174x1188.gif 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div><hr></div><h2>Complexity</h2><h3>Time Complexity</h3><ul><li><p>Seek <em>(access by index)</em>: O(1), direct jump to memory location</p></li><li><p>Insert <em>(push to end)</em>: O(1), no shifting needed</p></li><li><p>Delete <em>(pop from end)</em>: O(1), no shifting needed</p></li><li><p>Traverse: O(n), every element is visited once</p></li></ul><h3>Space Complexity</h3><p>O(n), where n is the number of elements stored in the array</p>]]></content:encoded></item><item><title><![CDATA[Singleton Pattern: Ensure Single Instance with Global Access]]></title><description><![CDATA[Master the Singleton design pattern with step by step implementation, database connection examples, and practical code for ensuring single instance with global access.]]></description><link>https://newsletter.shiftelevate.dev/p/singleton-pattern-ensure-single-instance-with-global-access</link><guid isPermaLink="false">https://newsletter.shiftelevate.dev/p/singleton-pattern-ensure-single-instance-with-global-access</guid><dc:creator><![CDATA[Shift Elevate]]></dc:creator><pubDate>Sat, 25 Apr 2026 12:55:03 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/efeb3da4-3daf-42e2-92c3-a7a46078f77e_1200x630.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>The Pain of Uncontrolled Object Creation</h2><p><strong>Picture this:</strong> You're building a database connection manager for your application. Your initial approach seems logical: create a new connection object whenever you need to access the database.</p><p>But requirements evolve quickly. You realize you need to limit database connections to prevent resource exhaustion, maintain connection pooling, and ensure consistent configuration across your application. Your code becomes scattered with connection creation logic, leading to resource leaks, inconsistent configurations, and performance issues.</p><p>Suddenly, you're dealing with multiple database connections consuming memory, different configurations causing data inconsistencies, and a system that's hard to manage and debug. Sound familiar?</p><p>The <strong>Singleton</strong> pattern solves this challenge elegantly. Let's explore how.</p><h2>Understanding the Singleton Pattern</h2><p><em>The Singleton pattern ensures that a class has only one instance and provides a global point of access to that instance. It guarantees that no other instance can be created and provides a way to access the single instance from anywhere in the application.</em></p><p>Think of it like your computer's clipboard: there's only one clipboard, and every application reads from and writes to the same one. When you copy text in your browser and paste it in your editor, both apps are accessing the same shared instance. You wouldn't want multiple clipboards causing confusion about what was last copied.</p><p>This pattern promotes <strong>Resource Management, Global Access</strong>, and <strong>Instance Control</strong> while ensuring consistent behaviour across the application.</p><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!E5jq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda0003a4-0cc0-4938-8622-05d96e81e624_620x440.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!E5jq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda0003a4-0cc0-4938-8622-05d96e81e624_620x440.png 424w, https://substackcdn.com/image/fetch/$s_!E5jq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda0003a4-0cc0-4938-8622-05d96e81e624_620x440.png 848w, https://substackcdn.com/image/fetch/$s_!E5jq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda0003a4-0cc0-4938-8622-05d96e81e624_620x440.png 1272w, https://substackcdn.com/image/fetch/$s_!E5jq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda0003a4-0cc0-4938-8622-05d96e81e624_620x440.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!E5jq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda0003a4-0cc0-4938-8622-05d96e81e624_620x440.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/da0003a4-0cc0-4938-8622-05d96e81e624_620x440.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!E5jq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda0003a4-0cc0-4938-8622-05d96e81e624_620x440.png 424w, https://substackcdn.com/image/fetch/$s_!E5jq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda0003a4-0cc0-4938-8622-05d96e81e624_620x440.png 848w, https://substackcdn.com/image/fetch/$s_!E5jq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda0003a4-0cc0-4938-8622-05d96e81e624_620x440.png 1272w, https://substackcdn.com/image/fetch/$s_!E5jq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda0003a4-0cc0-4938-8622-05d96e81e624_620x440.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><p>Singleton Pattern Components</p><h3>Core Components</h3><ul><li><p><strong><a href="#singleton-database-connection-manag">Singleton</a></strong><a href="#singleton-database-connection-manag">:</a> The class that ensures only one instance exists, with a private constructor, static instance variable, and static getter method</p></li><li><p><strong><a href="#singleton-eager-initialization-conf">Singleton - Eager Initialization</a></strong><a href="#singleton-eager-initialization-conf">:</a> A variant that creates the instance at class loading time, trading lazy creation for simplicity and inherent thread safety</p></li></ul><h2>Complete Java Implementation</h2><p>Let's build a database connection manager that demonstrates the Singleton pattern's power in managing single instances with global access.</p><h3>Singleton (DatabaseConnectionManager)</h3><pre><code>public class DatabaseConnectionManager {
    private static volatile DatabaseConnectionManager instance;
    private String connectionString;
    private boolean isConnected;
    private int connectionCount;
    
    private DatabaseConnectionManager() {
        this.connectionString = "jdbc:mysql://localhost:3306/mydb";
        this.isConnected = false;
        this.connectionCount = 0;
        System.out.println("DatabaseConnectionManager instance created");
    }
    
    public static DatabaseConnectionManager getInstance() {
        if (instance == null) {
            synchronized (DatabaseConnectionManager.class) {
                if (instance == null) {
                    instance = new DatabaseConnectionManager();
                }
            }
        }
        return instance;
    }
    
    public void connect() {
        if (!isConnected) {
            isConnected = true;
            connectionCount++;
            System.out.println("Connected to database: " + connectionString);
        } else {
            System.out.println("Already connected to database");
        }
    }
    
    public void disconnect() {
        if (isConnected) {
            isConnected = false;
            System.out.println("Disconnected from database");
        } else {
            System.out.println("Not connected to database");
        }
    }
    
    public void executeQuery(String query) {
        if (isConnected) {
            System.out.println("Executing query: " + query);
        } else {
            System.out.println("Cannot execute query - not connected to database");
        }
    }
    
    public String getConnectionString() { return connectionString; }
    public boolean isConnected() { return isConnected; }
    public int getConnectionCount() { return connectionCount; }
    
    public void setConnectionString(String connectionString) {
        this.connectionString = connectionString;
        System.out.println("Connection string updated to: " + connectionString);
    }
}</code></pre><h3>Singleton - Eager Initialization (ConfigurationManager)</h3><pre><code>public class ConfigurationManager {
    private static final ConfigurationManager instance = new ConfigurationManager();
    private String appName;
    private String version;
    private Map&lt;String, String&gt; settings;
    
    private ConfigurationManager() {
        this.appName = "My Application";
        this.version = "1.0.0";
        this.settings = new HashMap&lt;&gt;();
        loadDefaultSettings();
        System.out.println("ConfigurationManager instance created (eager initialization)");
    }
    
    public static ConfigurationManager getInstance() {
        return instance;
    }
    
    private void loadDefaultSettings() {
        settings.put("theme", "dark");
        settings.put("language", "en");
        settings.put("timeout", "30");
    }
    
    public String getSetting(String key) {
        return settings.get(key);
    }
    
    public void setSetting(String key, String value) {
        settings.put(key, value);
        System.out.println("Setting updated: " + key + " = " + value);
    }
    
    public String getAppName() { return appName; }
    public String getVersion() { return version; }
    public Map&lt;String, String&gt; getAllSettings() { return new HashMap&lt;&gt;(settings); }
}
</code></pre><h3>Client</h3><pre><code>public class SingletonDemo {
    public static void main(String[] args) {
        System.out.println("=== Singleton Pattern Demo ===\n");
        
        System.out.println("=== Database Connection Manager ===");
        DatabaseConnectionManager dbManager1 = DatabaseConnectionManager.getInstance();
        DatabaseConnectionManager dbManager2 = DatabaseConnectionManager.getInstance();
        
        System.out.println("Are both instances the same? " + (dbManager1 == dbManager2));
        
        dbManager1.connect();
        dbManager2.executeQuery("SELECT * FROM users");
        dbManager1.disconnect();
        
        System.out.println("\n=== Configuration Manager ===");
        ConfigurationManager config1 = ConfigurationManager.getInstance();
        ConfigurationManager config2 = ConfigurationManager.getInstance();
        
        System.out.println("Are both instances the same? " + (config1 == config2));
        
        System.out.println("App Name: " + config1.getAppName());
        System.out.println("Version: " + config1.getVersion());
        config1.setSetting("theme", "light");
        System.out.println("Theme setting: " + config2.getSetting("theme"));
        
        System.out.println("\n=== Thread Safety Test ===");
        testThreadSafety();
    }
    
    private static void testThreadSafety() {
        System.out.println("Testing thread safety with multiple threads...");
        
        Thread[] threads = new Thread[3];
        for (int i = 0; i &lt; 3; i++) {
            final int threadId = i;
            threads[i] = new Thread(() -&gt; {
                DatabaseConnectionManager instance = DatabaseConnectionManager.getInstance();
                System.out.println("Thread " + threadId + " got instance: " + instance.hashCode());
            });
        }
        
        for (Thread thread : threads) {
            thread.start();
        }
        
        for (Thread thread : threads) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
</code></pre><h4>Expected Output:</h4><pre><code>=== Singleton Pattern Demo ===

=== Database Connection Manager ===
DatabaseConnectionManager instance created
Are both instances the same? true
Connected to database: jdbc:mysql://localhost:3306/mydb
Executing query: SELECT * FROM users
Disconnected from database

=== Configuration Manager ===
ConfigurationManager instance created (eager initialization)
Are both instances the same? true
App Name: My Application
Version: 1.0.0
Setting updated: theme = light
Theme setting: light

=== Thread Safety Test ===
Testing thread safety with multiple threads...
Thread 0 got instance: 1234567890
Thread 1 got instance: 1234567890
Thread 2 got instance: 1234567890</code></pre><h3>&#128640; Get the Complete Implementation</h3><p>The full code with advanced singleton implementations and thread safety is available in our <a href="https://github.com/shift-elevate/design-patterns">Design Patterns Repository</a>.</p><pre><code># Clone and run the complete demo
git clone https://github.com/shift-elevate/design-patterns.git
cd design-patterns
mvn test -Dtest=SingletonPatternTest</code></pre><h2>Real World Examples</h2><p>The Singleton pattern is extensively used in real-world applications:</p><h3>1. Database Connection Pools</h3><p>Database connection managers use Singleton patterns to ensure only one connection pool exists, preventing resource exhaustion and maintaining consistent connection management.</p><h3>2. Logging Systems</h3><p>Logging frameworks use Singleton patterns to provide a single, global logging instance that can be accessed from anywhere in the application.</p><h3>3. Configuration Management</h3><p>Configuration managers use Singleton patterns to ensure consistent application settings across all components.</p><h3>4. Cache Systems</h3><p>Cache managers use Singleton patterns to provide a single, shared cache instance that can be accessed by multiple components.</p><h2>When to Use Singleton Pattern</h2><p>Understanding when to apply the Singleton pattern is crucial for making the right architectural decisions. Here's when it shines and when alternatives might be better:</p><h3>&#9989; Ideal Scenarios:</h3><ul><li><p>You need exactly one instance of a class throughout the application.</p></li><li><p>You want to control access to shared resources (database connections, file systems).</p></li><li><p>You need a global point of access for configuration or logging.</p></li><li><p>You want to ensure consistent behavior across the application.</p></li></ul><h3>&#10060; Skip It When:</h3><ul><li><p>You need multiple instances with different configurations.</p></li><li><p>The class doesn't maintain state or has no shared resources.</p></li><li><p>You're building a library that might be used in different contexts.</p></li><li><p>You need to subclass the singleton class.</p></li></ul><h2>Next Steps: Apply Singleton Pattern in Your Project</h2><p>Ready to implement the Singleton pattern in your own projects? Here's a structured approach to get you started:</p><ol><li><p><strong>Identify Single Instance Needs</strong>: Look for classes that should have only one instance <em>(loggers, configuration managers, connection pools)</em>.</p></li><li><p><strong>Make Constructor Private</strong>: Prevent external instantiation of the class.</p></li><li><p><strong>Create Static Getter Method</strong>: Provide a way to access the single instance.</p></li><li><p><strong>Ensure Thread Safety</strong>: Use double-checked locking or enum singleton for thread safety.</p></li><li><p><strong>Test Singleton Behavior</strong>: Verify that only one instance is created and accessed.</p></li></ol><p>The Singleton pattern transforms uncontrolled object creation into managed, single-instance systems. By ensuring only one instance exists, you build applications with consistent behaviour and efficient resource management.</p><p><em>Found this helpful? Share it with a colleague who's struggling with uncontrolled object creation. Got questions? We'd love to hear from you at <a href="/cdn-cgi/l/email-protection#bfdcd0d1cbdedccbffccd7d6d9cbdad3dac9decbda91dbdac9">[email&nbsp;protected]</a></em></p>]]></content:encoded></item><item><title><![CDATA[Throttling Pattern: Controlling Request Rates for System Protection]]></title><description><![CDATA[Master the Throttling pattern with rate limiting algorithms, configuration strategies, with Java implementations for protecting systems from overload in distributed environments.]]></description><link>https://newsletter.shiftelevate.dev/p/throttling-pattern-controlling-request-rates-for-system-protection</link><guid isPermaLink="false">https://newsletter.shiftelevate.dev/p/throttling-pattern-controlling-request-rates-for-system-protection</guid><dc:creator><![CDATA[Shift Elevate]]></dc:creator><pubDate>Fri, 10 Apr 2026 07:44:41 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/83d0bfc3-1fa5-4c07-b22f-88893672b555_1200x630.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>In distributed systems, uncontrolled request rates can overwhelm services and cause failures. The Throttling pattern protects systems by limiting how many requests a client can make within a given time window, ensuring fair resource allocation and system stability.</em></p><p>This pattern is essential for any service exposed to external clients such as APIs, microservices, or cloud functions, where you cannot fully control how callers behave. By implementing intelligent rate limiting, you prevent abuse, protect downstream dependencies, and maintain consistent performance for all users.</p><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7Rrd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F762dab2f-10d6-4299-a4ab-7abd7da4f332_1376x684.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7Rrd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F762dab2f-10d6-4299-a4ab-7abd7da4f332_1376x684.png 424w, https://substackcdn.com/image/fetch/$s_!7Rrd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F762dab2f-10d6-4299-a4ab-7abd7da4f332_1376x684.png 848w, https://substackcdn.com/image/fetch/$s_!7Rrd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F762dab2f-10d6-4299-a4ab-7abd7da4f332_1376x684.png 1272w, https://substackcdn.com/image/fetch/$s_!7Rrd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F762dab2f-10d6-4299-a4ab-7abd7da4f332_1376x684.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7Rrd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F762dab2f-10d6-4299-a4ab-7abd7da4f332_1376x684.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/762dab2f-10d6-4299-a4ab-7abd7da4f332_1376x684.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!7Rrd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F762dab2f-10d6-4299-a4ab-7abd7da4f332_1376x684.png 424w, https://substackcdn.com/image/fetch/$s_!7Rrd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F762dab2f-10d6-4299-a4ab-7abd7da4f332_1376x684.png 848w, https://substackcdn.com/image/fetch/$s_!7Rrd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F762dab2f-10d6-4299-a4ab-7abd7da4f332_1376x684.png 1272w, https://substackcdn.com/image/fetch/$s_!7Rrd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F762dab2f-10d6-4299-a4ab-7abd7da4f332_1376x684.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a><p>This guide walks you through the Throttling pattern from concept to practical implementation, covering the three core rate limiting algorithms, how to apply them in a real API service, and how to choose the right approach for your use case.</p><ul><li><p><a href="#understanding-the-throttling-patter">Understanding the Throttling Pattern</a></p></li><li><p><a href="#implementing-the-throttling-pattern">Implementing the Throttling Pattern</a></p></li><li><p><a href="#when-to-use-throttling-pattern">When to Use Throttling Pattern</a></p></li><li><p><a href="#best-practices">Best Practices</a></p></li></ul><h2>Understanding the Throttling Pattern</h2><p>The Throttling pattern sits in front of your service and intercepts each incoming request. Before passing the request through, it checks whether the client has exceeded their allowed rate. If they have, the request is rejected with a rate limit error <em>(typically HTTP 429)</em> rather than forwarded to the service.</p><h3>Core Throttling Strategies</h3><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lS8H!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1f4b584-b37b-4256-a1fa-875671ca9f05_1095x814.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lS8H!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1f4b584-b37b-4256-a1fa-875671ca9f05_1095x814.png 424w, https://substackcdn.com/image/fetch/$s_!lS8H!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1f4b584-b37b-4256-a1fa-875671ca9f05_1095x814.png 848w, https://substackcdn.com/image/fetch/$s_!lS8H!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1f4b584-b37b-4256-a1fa-875671ca9f05_1095x814.png 1272w, https://substackcdn.com/image/fetch/$s_!lS8H!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1f4b584-b37b-4256-a1fa-875671ca9f05_1095x814.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!lS8H!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1f4b584-b37b-4256-a1fa-875671ca9f05_1095x814.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b1f4b584-b37b-4256-a1fa-875671ca9f05_1095x814.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!lS8H!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1f4b584-b37b-4256-a1fa-875671ca9f05_1095x814.png 424w, https://substackcdn.com/image/fetch/$s_!lS8H!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1f4b584-b37b-4256-a1fa-875671ca9f05_1095x814.png 848w, https://substackcdn.com/image/fetch/$s_!lS8H!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1f4b584-b37b-4256-a1fa-875671ca9f05_1095x814.png 1272w, https://substackcdn.com/image/fetch/$s_!lS8H!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1f4b584-b37b-4256-a1fa-875671ca9f05_1095x814.png 1456w" sizes="100vw"></picture><div></div></div></a><p>Throttling Pattern Strategies</p><h3>Key Benefits</h3><ul><li><p><strong>System Protection</strong>: Prevents services from being overwhelmed by excessive requests.</p></li><li><p><strong>Resource Management</strong>: Ensures fair allocation of system resources.</p></li><li><p><strong>Cost Control</strong>: Prevents runaway costs from unexpected traffic spikes.</p></li><li><p><strong>Quality of Service</strong>: Maintains consistent performance for legitimate users.</p></li><li><p><strong>Abuse Prevention</strong>: Protects against malicious or accidental abuse.</p></li></ul><h2>Implementing the Throttling Pattern</h2><p>Let's build a throttling system that implements three classic rate limiting algorithms, then applies one of them in a practical API service.</p><h3>Implementation Overview</h3><ul><li><p><strong>TokenBucketRateLimiter</strong>: The most flexible algorithm. Clients accumulate tokens over time and spend one per request, allowing short bursts while enforcing a long-term average rate.</p></li><li><p><strong>SlidingWindowRateLimiter</strong>: The most accurate algorithm. Tracks the timestamp of every request and counts how many fall within the last N milliseconds, giving a precise rolling rate.</p></li><li><p><strong>FixedWindowRateLimiter</strong>: The simplest algorithm. Counts requests per fixed time slot and resets the counter when the slot changes.</p></li><li><p><strong>RateLimitedApiService</strong>: A practical API service that applies Token Bucket throttling per client.</p></li><li><p><strong>Main</strong>: A runnable demo showing normal usage and burst traffic hitting the rate limit.</p></li></ul><p>This implementation uses plain Java with no external dependencies. For production systems, consider using a distributed cache like Redis to share rate limit state across multiple service instances. An in-memory limiter only enforces limits on a single node.</p><h3>Rate Limiting Algorithms</h3><pre><code>/**
 * Token Bucket: imagine a bucket that fills with tokens over time.
 * Each request consumes one token. If the bucket is empty, the request is rejected.
 * Allows short bursts (up to capacity) while enforcing a steady long-term rate.
 */
public class TokenBucketRateLimiter {
    private final int capacity;       // Max tokens the bucket can hold (burst limit)
    private final double refillPerMs; // Tokens added per millisecond
    private final Map&lt;String, Bucket&gt; buckets = new HashMap&lt;&gt;();

    public TokenBucketRateLimiter(int capacity, int refillPerSecond) {
        this.capacity = capacity;
        this.refillPerMs = refillPerSecond / 1000.0;
    }

    /** Returns true if the request is allowed, false if rate-limited */
    public boolean isAllowed(String clientId) {
        Bucket bucket = buckets.computeIfAbsent(clientId, k -&gt; new Bucket(capacity));
        bucket.refill();
        return bucket.tryConsume();
    }

    private class Bucket {
        private double tokens;
        private long lastRefillTime = System.currentTimeMillis();

        Bucket(int initialTokens) { this.tokens = initialTokens; }

        /** Add tokens based on how much time has passed since the last refill */
        void refill() {
            long now = System.currentTimeMillis();
            double tokensToAdd = (now - lastRefillTime) * refillPerMs;
            tokens = Math.min(capacity, tokens + tokensToAdd);
            lastRefillTime = now;
        }

        boolean tryConsume() {
            if (tokens &gt;= 1.0) {
                tokens -= 1.0;
                return true;
            }
            return false;
        }
    }
}

/**
 * Sliding Window: tracks the timestamp of every recent request.
 * Only counts requests that fall within the last windowMs milliseconds.
 * Most accurate approach with no boundary artifacts, but uses more memory.
 */
public class SlidingWindowRateLimiter {
    private final int maxRequests;
    private final long windowMs;
    private final Map&lt;String, Queue&lt;Long&gt;&gt; requestLogs = new HashMap&lt;&gt;();

    public SlidingWindowRateLimiter(int maxRequests, long windowMs) {
        this.maxRequests = maxRequests;
        this.windowMs = windowMs;
    }

    public synchronized boolean isAllowed(String clientId) {
        long now = System.currentTimeMillis();
        Queue&lt;Long&gt; timestamps = requestLogs.computeIfAbsent(clientId, k -&gt; new LinkedList&lt;&gt;());

        // Drop timestamps that have slid outside the current window
        while (!timestamps.isEmpty() &amp;&amp; now - timestamps.peek() &gt; windowMs) {
            timestamps.poll();
        }

        if (timestamps.size() &lt; maxRequests) {
            timestamps.offer(now);
            return true;
        }
        return false;
    }
}

/**
 * Fixed Window: counts requests within a fixed time slot (e.g., the current minute).
 * Simplest to implement. Trade-off: a burst at a window boundary can send
 * up to 2&#215; the limit in a short period.
 */
public class FixedWindowRateLimiter {
    private final int maxRequests;
    private final long windowMs;
    private final Map&lt;String, Window&gt; windows = new HashMap&lt;&gt;();

    public FixedWindowRateLimiter(int maxRequests, long windowMs) {
        this.maxRequests = maxRequests;
        this.windowMs = windowMs;
    }

    public synchronized boolean isAllowed(String clientId) {
        long currentSlot = System.currentTimeMillis() / windowMs;
        Window window = windows.computeIfAbsent(clientId, k -&gt; new Window(currentSlot));

        // New time slot, reset the counter
        if (window.slot != currentSlot) {
            window.slot = currentSlot;
            window.count = 0;
        }

        if (window.count &lt; maxRequests) {
            window.count++;
            return true;
        }
        return false;
    }

    private static class Window {
        long slot;
        int count = 0;
        Window(long slot) { this.slot = slot; }
    }
}</code></pre><h3>Practical Implementation: Rate-Limited API Service</h3><pre><code>/** An API service that enforces per-client rate limits using Token Bucket */
public class RateLimitedApiService {
    private final TokenBucketRateLimiter rateLimiter;

    public RateLimitedApiService(int requestsPerSecond) {
        // Allow bursts up to 5 requests, then refill at the specified rate
        this.rateLimiter = new TokenBucketRateLimiter(5, requestsPerSecond);
    }

    public ApiResponse handleRequest(String clientId, String operation) {
        if (!rateLimiter.isAllowed(clientId)) {
            System.out.println("  [" + clientId + "] THROTTLED: " + operation);
            return new ApiResponse(429, "Rate limit exceeded. Slow down your requests.");
        }
        System.out.println("  [" + clientId + "] OK: " + operation);
        return new ApiResponse(200, "Success");
    }
}

// Simple data class for the API response
public record ApiResponse(int status, String message) {}</code></pre><h3>Putting It All Together</h3><pre><code>public class Main {
    public static void main(String[] args) throws InterruptedException {
        // 2 requests/sec with a burst capacity of 5
        RateLimitedApiService service = new RateLimitedApiService(2);

        // Happy path: spread-out requests stay within the rate limit
        System.out.println("=== Normal usage ===");
        service.handleRequest("client-A", "GET /products");
        Thread.sleep(600);
        service.handleRequest("client-A", "GET /orders");
        Thread.sleep(600);
        service.handleRequest("client-A", "GET /profile");

        System.out.println();

        // Burst: 8 requests in quick succession exhaust the bucket and trigger throttling
        System.out.println("=== Burst traffic ===");
        for (int i = 1; i &lt;= 8; i++) {
            service.handleRequest("client-B", "GET /products (request " + i + ")");
        }
    }
}</code></pre><h3>Example Output</h3><pre><code>=== Normal usage ===
  [client-A] OK: GET /products
  [client-A] OK: GET /orders
  [client-A] OK: GET /profile

=== Burst traffic ===
  [client-B] OK: GET /products (request 1)
  [client-B] OK: GET /products (request 2)
  [client-B] OK: GET /products (request 3)
  [client-B] OK: GET /products (request 4)
  [client-B] OK: GET /products (request 5)
  [client-B] THROTTLED: GET /products (request 6)
  [client-B] THROTTLED: GET /products (request 7)
  [client-B] THROTTLED: GET /products (request 8)</code></pre><h3>Choosing the Right Algorithm</h3><p>All three algorithms limit request rates, but they behave differently at the edges:</p><ul><li><p><strong>Token Bucket</strong> is best when you want to allow short bursts. A client who hasn't sent requests in a while accumulates tokens they can spend all at once, then must wait for the bucket to refill. Use this when occasional spikes are acceptable and you want to reward clients who space out their requests.</p></li><li><p><strong>Sliding Window</strong> is the most accurate and fairest. It counts exactly how many requests arrived in the last N seconds at any point in time, with no boundary effects. The trade-off is memory: it stores one timestamp per recent request. Use this when precision matters most.</p></li><li><p><strong>Fixed Window</strong> is the simplest to understand and implement. The risk is the boundary burst: a client can fire <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">maxRequests</mark></em> at 11:59:59 and another <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">maxRequests</mark></em> at 12:00:00, that's 2&#215; the limit in two seconds. Use this for non-critical limits where simplicity is preferred.</p></li></ul><h2>When to Use Throttling Pattern</h2><h3>&#9989;<strong> Ideal Scenarios:</strong></h3><ul><li><p>Your API or service is called by external clients whose request behaviour you cannot fully control.</p></li><li><p>You need to protect downstream services from being flooded by a single misbehaving client.</p></li><li><p>You want fair resource allocation, preventing one client from monopolizing shared capacity.</p></li><li><p>You are enforcing business rules around tiered limits (free vs. paid plans).</p></li><li><p>You need to cap usage of an expensive downstream service or API to control costs.</p></li><li><p>You're building a public-facing API where abuse protection is required.</p></li></ul><h3>&#10060;<strong> Skip It When:</strong></h3><ul><li><p>Your service is only called by internal, trusted systems with predictable and controlled request rates.</p></li><li><p>You need to guarantee processing of every request. Consider the Queue-based Load Leveling pattern instead.</p></li><li><p>The cost of dropped requests exceeds the cost of the overload you're trying to prevent.</p></li><li><p>All clients are coordinated and can implement cooperative rate limiting at the source.</p></li><li><p>You're in an early-stage prototype where operational complexity isn't yet warranted.</p></li></ul><h2>Best Practices</h2><ul><li><p><strong>Return meaningful responses</strong>: Respond with HTTP 429 and include a <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">Retry-After</mark></em> header so clients know when to try again. This turns a confusing failure into an actionable signal.</p></li><li><p><strong>Key limiters per client, not globally</strong>: A single misbehaving client shouldn't degrade service for others. Use a per-client identifier such as user ID, API key, or IP address as the rate limit key.</p></li><li><p><strong>Use multiple granularities</strong>: Per-second limits protect against bursts; per-minute or per-hour limits enforce usage quotas. Many production APIs enforce both simultaneously.</p></li><li><p><strong>Log throttled requests</strong>: Don't silently discard them. Throttling events are valuable signals for capacity planning and for diagnosing client integration problems.</p></li><li><p><strong>Plan for distributed deployments</strong>: An in-memory rate limiter only works on a single instance. For multi-node deployments, synchronize counters using a shared store like Redis so limits are enforced consistently across all nodes.</p></li><li><p><strong>Start permissive, tighten gradually</strong>: It is much easier to lower limits after clients are integrated than to raise them once they've built assumptions around the current values. Start generous and adjust based on observed behaviour.</p></li></ul><p><em>Found this helpful? Share it with a colleague who's struggling with system overload and performance issues. Got questions? We'd love to hear from you at <a href="/cdn-cgi/l/email-protection#bdded2d3c9dcdec9fdced5d4dbc9d8d1d8cbdcc9d893d9d8cb">[email&nbsp;protected]</a></em></p>]]></content:encoded></item><item><title><![CDATA[Temporary Field: Extract Class Refactoring | Clean Code]]></title><description><![CDATA[Refactor the Temporary Field code smell with Extract Class technique, improve class interfaces, and enhance code organization.]]></description><link>https://newsletter.shiftelevate.dev/p/temporary-field-extract-class-refactoring-clean-code</link><guid isPermaLink="false">https://newsletter.shiftelevate.dev/p/temporary-field-extract-class-refactoring-clean-code</guid><dc:creator><![CDATA[Shift Elevate]]></dc:creator><pubDate>Sun, 29 Mar 2026 11:25:19 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/cf5512e1-b684-4a3b-bf29-d552eff4431f_1200x630.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Temporary Fields are instance variables that are only used in certain circumstances, making the class interface confusing.</em></p><p>We will see how we can refactor using the Extract Class technique to organize temporary fields into dedicated classes.</p><h3>Clean Code Reference</h3><p>&#10077;</p><p><strong>&#9888;&#65039; Code Smell:</strong><span data-color="rgb(204, 204, 204)" style="color: rgb(204, 204, 204);"> </span>Temporary Field<br>&#9989;<strong> Refactoring:</strong><span data-color="rgb(204, 204, 204)" style="color: rgb(204, 204, 204);"> </span>Extract Class<br>&#127919;<strong> Goal:</strong><span data-color="rgb(204, 204, 204)" style="color: rgb(204, 204, 204);"> </span>Clear class interfaces and better field organization</p><p>The Temporary Field code smell occurs when instance variables are only used in certain circumstances, making the class interface confusing and the object's state unclear. The <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">Extract Class</mark></em> refactoring technique moves these temporary fields into dedicated classes that better represent their purpose.</p><h2>The Code Smell: Temporary Field</h2><p>Temporary Fields are instance variables that are only used in specific methods or under certain conditions, making the class interface confusing. These fields often represent intermediate calculations, temporary state, or data that's only relevant during specific processing stages. This makes the object's state unclear and can lead to bugs when fields contain stale or irrelevant values.</p><p>Symptoms</p><p>Impact</p><p><em>Fields only used in certain methods</em></p><p><em>Confusing class interface</em></p><p><em>Unclear object state</em></p><p><em>Difficult to understand</em></p><p><em>Fields initialized to null or zero</em></p><p><em>Higher bug risk</em></p><h4>Here's a typical example of Temporary Field:</h4><pre><code>public class OrderProcessor {
    // Core order fields
    private String orderId;
    private Customer customer;
    private List&lt;OrderItem&gt; items;
    private double totalAmount;

    // Temporary fields - only used during discount calculation
    private String appliedDiscountCode;
    private double discountAmount;

    // Temporary fields - only used during payment
    private boolean paymentProcessed;
    private String transactionId;

    // Temporary fields - only used during shipment
    private String shippingAddress;
    private String trackingNumber;

    public OrderProcessor(String orderId, Customer customer, List&lt;OrderItem&gt; items) {
        this.orderId = orderId;
        this.customer = customer;
        this.items = items;
        this.totalAmount = calculateBaseTotal();

        // Initialize temporary fields to default values
        this.appliedDiscountCode = null;
        this.discountAmount = 0.0;
        this.paymentProcessed = false;
        this.transactionId = null;
        this.shippingAddress = null;
        this.trackingNumber = null;
    }

    public double applyDiscount(String discountCode) {
        this.appliedDiscountCode = discountCode;
        this.discountAmount = calculateDiscount(discountCode);
        return this.totalAmount - this.discountAmount;
    }

    public boolean processPayment(String paymentMethod) {
        PaymentResult result = paymentGateway.charge(paymentMethod, totalAmount);
        this.paymentProcessed = result.isSuccess();
        this.transactionId = result.getTransactionId();
        return this.paymentProcessed;
    }

    public void prepareShipment(String shippingAddress) {
        this.shippingAddress = shippingAddress;
        this.trackingNumber = "TRK" + orderId;
    }

    private double calculateDiscount(String discountCode) {
        if (discountCode == null) return 0.0;
        switch (discountCode) {
            case "SAVE10": return totalAmount * 0.10;
            case "SAVE20": return totalAmount * 0.20;
            default: return 0.0;
        }
    }

    private double calculateBaseTotal() {
        return items.stream()
                .mapToDouble(item -&gt; item.getPrice() * item.getQuantity())
                .sum();
    }

}</code></pre><p>In this example, <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">OrderProcessor</mark></em> has three groups of temporary fields: <mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">discount fields </mark><em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">(appliedDiscountCode, discountAmount)</mark></em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">, payment fields </mark><em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">(paymentProcessed, transactionId)</mark></em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">, and shipment fields </mark><em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">(shippingAddress, trackingNumber)</mark></em>. These fields are only relevant during specific processing stages, yet they clutter the class interface throughout the object's lifetime.</p><h2>The Refactoring: Extract Class</h2><p>The Extract Class refactoring technique moves temporary fields into dedicated classes that better represent their purpose and usage context. This makes the main class interface cleaner and the object's state more predictable.</p><p>By creating dedicated classes for discount information, payment details, and shipment data, we transform temporary state into well-defined objects that only exist when relevant.</p><h4>Step by Step Refactoring Process:</h4><ol><li><p><strong>Identify temporary fields</strong> that are only used in specific contexts.</p></li><li><p><strong>Group related temporary fields</strong> that are used together.</p></li><li><p><strong>Create new classes</strong> to represent these contexts.</p></li><li><p><strong>Move temporary fields</strong> into the appropriate classes.</p></li><li><p><strong>Update methods</strong> to work with the new classes.</p></li></ol><h4>Here's the refactored version:</h4><p>The temporary fields have been organized into dedicated classes:</p><ol><li><p><a href="#order-processor-refactored-main-cla">OrderProcessor</a> - Refactored main class</p></li><li><p><a href="#discount-info-extracted-discount-da">DiscountInfo</a> - Extracted discount data</p></li><li><p><a href="#payment-info-extracted-payment-data">PaymentInfo</a> - Extracted payment data</p></li><li><p><a href="#shipment-info-extracted-shipment-da">ShipmentInfo</a> - Extracted shipment data</p></li></ol><h3>OrderProcessor - Refactored Main Class</h3><pre><code>public class OrderProcessor {
    private final String orderId;
    private final Customer customer;
    private final List&lt;OrderItem&gt; items;
    private final double totalAmount;

    private DiscountInfo discountInfo;
    private PaymentInfo paymentInfo;
    private ShipmentInfo shipmentInfo;

    public OrderProcessor(String orderId, Customer customer, List&lt;OrderItem&gt; items) {
        this.orderId = orderId;
        this.customer = customer;
        this.items = items;
        this.totalAmount = calculateBaseTotal();
    }

    public double applyDiscount(String discountCode) {
        this.discountInfo = new DiscountInfo(discountCode, totalAmount);
        return totalAmount - discountInfo.getAmount();
    }

    public boolean processPayment(String paymentMethod) {
        PaymentResult result = paymentGateway.charge(paymentMethod, totalAmount);
        this.paymentInfo = new PaymentInfo(paymentMethod, result);
        return paymentInfo.isProcessed();
    }

    public void prepareShipment(String shippingAddress) {
        this.shipmentInfo = new ShipmentInfo(orderId, shippingAddress);
    }

    private double calculateBaseTotal() {
        return items.stream()
                .mapToDouble(item -&gt; item.getPrice() * item.getQuantity())
                .sum();
    }

}
</code></pre><h3>DiscountInfo - Extracted Discount Data</h3><pre><code>public class DiscountInfo {
    private final String code;
    private final double amount;

    public DiscountInfo(String code, double orderTotal) {
        this.code = code;
        this.amount = calculateDiscount(code, orderTotal);
    }

    private double calculateDiscount(String code, double orderTotal) {
        if (code == null) return 0.0;
        switch (code) {
            case "SAVE10": return orderTotal * 0.10;
            case "SAVE20": return orderTotal * 0.20;
            default: return 0.0;
        }
    }

    @Override
    public String toString() {
        return String.format("Discount{code='%s', amount=%.2f}", code, amount);
    }
}
</code></pre><h3>PaymentInfo - Extracted Payment Data</h3><pre><code>public class PaymentInfo {
    private final String method;
    private final boolean processed;
    private final String transactionId;

    public PaymentInfo(String method, PaymentResult result) {
        this.method = method;
        this.processed = result.isSuccess();
        this.transactionId = result.getTransactionId();
    }

    @Override
    public String toString() {
        return String.format("Payment{method='%s', processed=%b, txId='%s'}", method, processed, transactionId);
    }
}
</code></pre><h3>ShipmentInfo - Extracted Shipment Data</h3><pre><code>public class ShipmentInfo {
    private final String address;
    private final String trackingNumber;

    public ShipmentInfo(String orderId, String address) {
        this.address = address;
        this.trackingNumber = "TRK" + orderId;
    }

    @Override
    public String toString() {
        return String.format("Shipment{address='%s', tracking='%s'}", address, trackingNumber);
    }
}
</code></pre><h3>Usage Example - Putting It All Together</h3><p>Notice how the refactored version transforms 6 scattered temporary fields into 3 focused objects. The <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">OrderProcessor</mark></em> constructor no longer initializes fields to null or zero, each object is created only when its processing stage begins.</p><p>When you access <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">paymentInfo.getTransactionId()</mark></em> or <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">shipmentInfo.getTrackingNumber()</mark></em>, it's immediately clear what processing stage produced that data. Compare this to the original where every field existed at all times, even when irrelevant.</p><pre><code>public class OrderProcessingDemo {
    public static void main(String[] args) {
        System.out.println("=== Order Processing System ===\n");

        Customer customer = new Customer("johndoe");
        List&lt;OrderItem&gt; items = List.of(
            new OrderItem("Laptop", 999.99, 1),
            new OrderItem("Mouse", 29.99, 2)
        );

        OrderProcessor processor = new OrderProcessor("ORD-001", customer, items);
        System.out.println("Base total: $" + processor.getTotalAmount());

        double discountedTotal = processor.applyDiscount("SAVE10");
        System.out.println("After discount: $" + discountedTotal);
        System.out.println("Discount applied: " + processor.getDiscountInfo());

        boolean paid = processor.processPayment("CREDIT_CARD");
        System.out.println("\nPayment processed: " + paid);
        System.out.println("Payment details: " + processor.getPaymentInfo());

        processor.prepareShipment("123 Main St, Springfield");
        System.out.println("\nShipment prepared: " + processor.getShipmentInfo());
    }
}
</code></pre><h4>Expected Output:</h4><pre><code>=== Order Processing System ===

Base total: $1059.97
After discount: $953.97
Discount applied: Discount{code='SAVE10', amount=106.00}

Payment processed: true
Payment details: Payment{method='CREDIT_CARD', processed=true, txId='TXN-789'}

Shipment prepared: Shipment{address='123 Main St, Springfield', tracking='TRKORD-001'}
</code></pre><h2>Benefits of Extract Class for Temporary Fields</h2><p>Benefit</p><p>Description</p><p><em>Clearer Class Interface</em></p><p><em>The main class interface is cleaner and more focused on its core responsibilities.</em></p><p><em>Better State Management</em></p><p><em>Temporary fields are organized into logical groups with clear purposes.</em></p><p><em>Improved Maintainability</em></p><p><em>Related functionality is grouped together, making it easier to understand and modify.</em></p><h2>When to Apply Extract Class for Temporary Fields</h2><ul><li><p>Instance variables that are only used in specific methods or contexts.</p></li><li><p>Fields that are set but not always used, making the object's state unclear.</p></li><li><p>Classes with many temporary fields that represent different aspects of processing.</p></li><li><p>When the class interface becomes confusing due to too many fields.</p></li><li><p>When you find yourself initializing many fields to default values that might not be used.</p></li></ul><p>Apply Extract Class refactoring to one class with temporary fields in your current project today. Start with the class that has the most confusing interface due to temporary fields.</p><h2>Repository &amp; Resources</h2><p><strong>Complete Code Examples:</strong> <a href="https://github.com/shift-elevate/clean-code">Clean Code Repository</a></p><p>Find the complete implementation of Temporary Field refactoring and other clean code techniques in our dedicated repository. Each example includes:</p><ul><li><p>Before and after code comparisons</p></li><li><p>Unit tests demonstrating the improvements</p></li></ul><p><em>Found this helpful? Share it with a colleague who's struggling with Temporary Fields. Got questions? We'd love to hear from you at <a href="/cdn-cgi/l/email-protection#10737f7e64717364506378797664757c75667164753e747566">[email&nbsp;protected]</a></em></p>]]></content:encoded></item><item><title><![CDATA[Compensating Transaction Pattern: Managing Distributed Transactions with Compensation]]></title><description><![CDATA[Master the Compensating Transaction pattern with compensation strategies, rollback techniques, and production ready Java implementations for managing distributed transactions in microservices.]]></description><link>https://newsletter.shiftelevate.dev/p/compensating-transaction-pattern-managing-distributed-transactions-with-compensation</link><guid isPermaLink="false">https://newsletter.shiftelevate.dev/p/compensating-transaction-pattern-managing-distributed-transactions-with-compensation</guid><dc:creator><![CDATA[Shift Elevate]]></dc:creator><pubDate>Mon, 23 Mar 2026 14:32:51 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/25aa3e5f-5b72-48a9-9f2d-d6e1b38ff0e8_1200x630.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>In distributed systems, traditional ACID transactions are often not feasible across service boundaries. The Compensating Transaction pattern provides an alternative approach by implementing compensation logic that can undo or correct the effects of completed operations when a distributed transaction fails, ensuring data consistency across services.</em></p><p>This pattern is essential for maintaining data integrity in microservices architectures where services operate independently and cannot participate in traditional distributed transactions. By implementing intelligent compensation mechanisms, you can achieve eventual consistency and handle partial failures gracefully.</p><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!H7Kh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F892c1018-2898-46e6-b9d2-6359f3312173_1920x927.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!H7Kh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F892c1018-2898-46e6-b9d2-6359f3312173_1920x927.png 424w, https://substackcdn.com/image/fetch/$s_!H7Kh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F892c1018-2898-46e6-b9d2-6359f3312173_1920x927.png 848w, https://substackcdn.com/image/fetch/$s_!H7Kh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F892c1018-2898-46e6-b9d2-6359f3312173_1920x927.png 1272w, https://substackcdn.com/image/fetch/$s_!H7Kh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F892c1018-2898-46e6-b9d2-6359f3312173_1920x927.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!H7Kh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F892c1018-2898-46e6-b9d2-6359f3312173_1920x927.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/892c1018-2898-46e6-b9d2-6359f3312173_1920x927.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!H7Kh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F892c1018-2898-46e6-b9d2-6359f3312173_1920x927.png 424w, https://substackcdn.com/image/fetch/$s_!H7Kh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F892c1018-2898-46e6-b9d2-6359f3312173_1920x927.png 848w, https://substackcdn.com/image/fetch/$s_!H7Kh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F892c1018-2898-46e6-b9d2-6359f3312173_1920x927.png 1272w, https://substackcdn.com/image/fetch/$s_!H7Kh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F892c1018-2898-46e6-b9d2-6359f3312173_1920x927.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a><p>This guide walks you through the Compensating Transaction pattern from concept to practical implementation, covering how to structure compensatable steps, manage transaction state, and roll back gracefully when distributed operations fail.</p><ul><li><p><a href="#understanding-the-compensating-tran">Understanding the Compensating Transaction Pattern</a></p></li><li><p><a href="#implementing-the-compensating-trans">Implementing the Compensating Transaction Pattern</a></p></li><li><p><a href="#when-to-use-compensating-transactio">When to Use Compensating Transaction Pattern</a></p></li><li><p><a href="#best-practices">Best Practices</a></p></li></ul><h2>Understanding the Compensating Transaction Pattern</h2><p>The Compensating Transaction pattern works by pairing each operation in a distributed transaction with a corresponding compensation operation that can reverse its effects. When a transaction fails partway through, the system executes compensation operations for all completed steps.</p><h3>Core Architecture</h3><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!TFa2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ede9fb-36ac-41d7-8801-edb866214224_808x1361.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!TFa2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ede9fb-36ac-41d7-8801-edb866214224_808x1361.png 424w, https://substackcdn.com/image/fetch/$s_!TFa2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ede9fb-36ac-41d7-8801-edb866214224_808x1361.png 848w, https://substackcdn.com/image/fetch/$s_!TFa2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ede9fb-36ac-41d7-8801-edb866214224_808x1361.png 1272w, https://substackcdn.com/image/fetch/$s_!TFa2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ede9fb-36ac-41d7-8801-edb866214224_808x1361.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!TFa2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ede9fb-36ac-41d7-8801-edb866214224_808x1361.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/83ede9fb-36ac-41d7-8801-edb866214224_808x1361.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!TFa2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ede9fb-36ac-41d7-8801-edb866214224_808x1361.png 424w, https://substackcdn.com/image/fetch/$s_!TFa2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ede9fb-36ac-41d7-8801-edb866214224_808x1361.png 848w, https://substackcdn.com/image/fetch/$s_!TFa2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ede9fb-36ac-41d7-8801-edb866214224_808x1361.png 1272w, https://substackcdn.com/image/fetch/$s_!TFa2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ede9fb-36ac-41d7-8801-edb866214224_808x1361.png 1456w" sizes="100vw"></picture><div></div></div></a><p>Compensating Transaction Pattern Interactions</p><h3>Key Benefits</h3><ul><li><p><strong>Eventual Consistency</strong>: Ensures data consistency across distributed services over time.</p></li><li><p><strong>Failure Recovery</strong>: Provides mechanisms to handle partial transaction failures.</p></li><li><p><strong>Service Independence</strong>: Allows services to operate independently without tight coupling.</p></li><li><p><strong>Scalability</strong>: Enables horizontal scaling without distributed transaction overhead.</p></li><li><p><strong>Flexibility</strong>: Supports complex business logic and custom compensation strategies.</p></li></ul><h2>Implementing the Compensating Transaction Pattern</h2><p>Let's build a comprehensive compensating transaction system that handles complex distributed operations with proper compensation logic and monitoring.</p><h3>Implementation Overview</h3><ul><li><p><strong>CompensatingTransaction &amp; TransactionStep</strong>: Lightweight data models for tracking transaction state and step status</p></li><li><p><strong>CompensatingTransactionManager</strong>: Core manager handling transaction lifecycle, step execution, and synchronous compensation</p></li><li><p><strong>OrderProcessingService</strong>: Practical e-commerce example wiring all three steps together</p></li><li><p><strong>Service Classes</strong>: Order, Payment, and Inventory service implementations with compensation logic</p></li><li><p><strong>Main</strong>: A runnable demo showing both the happy path and a failure/rollback scenario</p></li></ul><h4>Note on Implementation</h4><p>This implementation keeps things simple by design: plain <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">HashMap</mark></em>, synchronous compensation, and no external dependencies. For production systems, consider using a persistent store <em>(PostgreSQL, Redis)</em> for transaction state durability so compensation can survive restarts, and frameworks like Axon Framework or Apache Camel for complex multi-service workflows.</p><h3>Transaction and Step Classes</h3><pre><code>/** Tracks the state and steps of a single distributed transaction */
public class CompensatingTransaction {
    private final String id;
    private final List&lt;TransactionStep&gt; steps = new ArrayList&lt;&gt;();
    private String status = "ACTIVE"; // ACTIVE &#8594; COMPLETED or COMPENSATED

    public CompensatingTransaction(String id) {
        this.id = id;
    }

    public void addStep(TransactionStep step) {
        steps.add(step);
    }

    /** Returns only the steps that completed successfully &#8212; these need to be undone */
    public List&lt;TransactionStep&gt; getCompletedSteps() {
        List&lt;TransactionStep&gt; completed = new ArrayList&lt;&gt;();
        for (TransactionStep step : steps) {
            if (step.isCompleted()) completed.add(step);
        }
        return completed;
    }

    public void markCompleted()   { status = "COMPLETED"; }
    public void markCompensated() { status = "COMPENSATED"; }

    public String getId()     { return id; }
    public String getStatus() { return status; }
}

/**
 * Represents a single step in a distributed transaction.
 * Each step must define:
 *   execute()    &#8212; what to do when things go right
 *   compensate() &#8212; how to undo it when things go wrong
 */
public abstract class TransactionStep {
    private final String name;
    private boolean completed = false;

    public TransactionStep(String name) {
        this.name = name;
    }

    /** Perform the step. Return true if successful, false otherwise. */
    public abstract boolean execute() throws Exception;

    /** Undo the effects of this step. Called during compensation. */
    public abstract void compensate() throws Exception;

    public void markCompleted()  { this.completed = true; }
    public boolean isCompleted() { return completed; }
    public String getName()      { return name; }
}
</code></pre><h3>Core Transaction Manager</h3><pre><code>public class CompensatingTransactionManager {
    // Stores active transactions keyed by their ID
    private final Map&lt;String, CompensatingTransaction&gt; transactions = new HashMap&lt;&gt;();
    private int nextId = 1;

    /** Start a new transaction and return its unique ID */
    public String startTransaction() {
        String id = "txn-" + nextId++;
        transactions.put(id, new CompensatingTransaction(id));
        System.out.println("Started transaction: " + id);
        return id;
    }

    /**
     * Execute a step within the transaction.
     * If the step fails, all previously completed steps are automatically undone.
     */
    public boolean executeStep(String transactionId, TransactionStep step) {
        CompensatingTransaction txn = findTransaction(transactionId);
        txn.addStep(step);

        try {
            boolean success = step.execute();
            if (success) {
                step.markCompleted();
                System.out.println("  &#10003; " + step.getName());
                return true;
            } else {
                System.out.println("  &#10007; " + step.getName() + " failed &#8212; starting compensation");
                compensate(transactionId);
                return false;
            }
        } catch (Exception e) {
            System.out.println("  &#10007; " + step.getName() + " threw: " + e.getMessage());
            compensate(transactionId);
            return false;
        }
    }

    /** Mark the transaction as successfully completed */
    public void completeTransaction(String transactionId) {
        findTransaction(transactionId).markCompleted();
        transactions.remove(transactionId);
        System.out.println("Transaction completed: " + transactionId);
    }

    /**
     * Undo all completed steps in reverse order.
     * Reverse order is critical &#8212; it ensures each step is undone
     * in a way that respects dependencies between operations.
     */
    public void compensate(String transactionId) {
        CompensatingTransaction txn = findTransaction(transactionId);
        System.out.println("Compensating transaction: " + transactionId);

        List&lt;TransactionStep&gt; toUndo = txn.getCompletedSteps();
        Collections.reverse(toUndo); // Undo last step first

        for (TransactionStep step : toUndo) {
            try {
                step.compensate();
                System.out.println("  &#8617; Undid: " + step.getName());
            } catch (Exception e) {
                // Log and continue &#8212; compensation must be best-effort
                System.err.println("  &#10007; Could not undo " + step.getName() + ": " + e.getMessage());
            }
        }

        txn.markCompensated();
        transactions.remove(transactionId);
        System.out.println("Transaction rolled back: " + transactionId);
    }

    private CompensatingTransaction findTransaction(String id) {
        CompensatingTransaction txn = transactions.get(id);
        if (txn == null) throw new IllegalArgumentException("Transaction not found: " + id);
        return txn;
    }
}
</code></pre><h3>Practical Implementation: E-commerce Order Processing</h3><pre><code>public class OrderProcessingService {
    private final CompensatingTransactionManager txnManager;
    private final OrderService orderService;
    private final PaymentService paymentService;
    private final InventoryService inventoryService;

    public OrderProcessingService(CompensatingTransactionManager txnManager,
                                  OrderService orderService, PaymentService paymentService,
                                  InventoryService inventoryService) {
        this.txnManager = txnManager;
        this.orderService = orderService;
        this.paymentService = paymentService;
        this.inventoryService = inventoryService;
    }

    public String processOrder(OrderRequest request) {
        String txnId = txnManager.startTransaction();

        // Each step is tried in sequence.
        // If any step fails, the manager automatically rolls back all previous steps.
        CreateOrderStep createStep = new CreateOrderStep(orderService, request);
        if (!txnManager.executeStep(txnId, createStep)) {
            return "Order failed &#8212; all changes have been rolled back";
        }

        ProcessPaymentStep paymentStep = new ProcessPaymentStep(paymentService, createStep.getOrderId(), request.getAmount());
        if (!txnManager.executeStep(txnId, paymentStep)) {
            return "Order failed &#8212; all changes have been rolled back";
        }

        ReserveInventoryStep inventoryStep = new ReserveInventoryStep(inventoryService, createStep.getOrderId(), request.getItems());
        if (!txnManager.executeStep(txnId, inventoryStep)) {
            return "Order failed &#8212; all changes have been rolled back";
        }

        txnManager.completeTransaction(txnId);
        return createStep.getOrderId();
    }
}

// Step 1: Create the order record
public class CreateOrderStep extends TransactionStep {
    private final OrderService orderService;
    private final OrderRequest request;
    private String orderId; // Saved so compensation knows what to cancel

    public CreateOrderStep(OrderService orderService, OrderRequest request) {
        super("Create Order");
        this.orderService = orderService;
        this.request = request;
    }

    @Override
    public boolean execute() {
        orderId = orderService.createOrder(request);
        return orderId != null;
    }

    @Override
    public void compensate() {
        // Undo: cancel the order we just created
        if (orderId != null) orderService.cancelOrder(orderId);
    }

    public String getOrderId() { return orderId; }
}

// Step 2: Charge the customer
public class ProcessPaymentStep extends TransactionStep {
    private final PaymentService paymentService;
    private final String orderId;
    private final double amount;
    private String paymentId; // Saved so compensation knows what to refund

    public ProcessPaymentStep(PaymentService paymentService, String orderId, double amount) {
        super("Process Payment");
        this.paymentService = paymentService;
        this.orderId = orderId;
        this.amount = amount;
    }

    @Override
    public boolean execute() {
        paymentId = paymentService.processPayment(orderId, amount);
        return paymentId != null;
    }

    @Override
    public void compensate() {
        // Undo: refund the payment
        if (paymentId != null) paymentService.refundPayment(paymentId);
    }
}

// Step 3: Reserve stock
public class ReserveInventoryStep extends TransactionStep {
    private final InventoryService inventoryService;
    private final String orderId;
    private final List&lt;OrderItem&gt; items;
    private List&lt;String&gt; reservationIds; // Saved so we know what to release

    public ReserveInventoryStep(InventoryService inventoryService, String orderId, List&lt;OrderItem&gt; items) {
        super("Reserve Inventory");
        this.inventoryService = inventoryService;
        this.orderId = orderId;
        this.items = items;
    }

    @Override
    public boolean execute() {
        reservationIds = inventoryService.reserveItems(orderId, items);
        return reservationIds != null &amp;&amp; !reservationIds.isEmpty();
    }

    @Override
    public void compensate() {
        // Undo: release the reserved stock
        if (reservationIds != null) inventoryService.releaseReservations(reservationIds);
    }
}

</code></pre><h3>Supporting Service Classes</h3><pre><code>/** Manages order records */
public class OrderService {
    private final Map&lt;String, String&gt; orders = new HashMap&lt;&gt;(); // orderId &#8594; status

    public String createOrder(OrderRequest request) {
        String orderId = "order-" + System.currentTimeMillis();
        orders.put(orderId, "CREATED");
        System.out.println("    Created order: " + orderId);
        return orderId;
    }

    public void cancelOrder(String orderId) {
        orders.put(orderId, "CANCELLED");
        System.out.println("    Cancelled order: " + orderId);
    }
}

/** Handles payment processing */
public class PaymentService {
    private final Map&lt;String, String&gt; payments = new HashMap&lt;&gt;(); // paymentId &#8594; status

    public String processPayment(String orderId, double amount) {
        String paymentId = "pay-" + System.currentTimeMillis();
        payments.put(paymentId, "CHARGED");
        System.out.println("    Charged $" + amount + " for order: " + orderId);
        return paymentId;
    }

    public void refundPayment(String paymentId) {
        payments.put(paymentId, "REFUNDED");
        System.out.println("    Refunded payment: " + paymentId);
    }
}

/** Manages inventory reservations */
public class InventoryService {
    private final Map&lt;String, String&gt; reservations = new HashMap&lt;&gt;();

    public List&lt;String&gt; reserveItems(String orderId, List&lt;OrderItem&gt; items) {
        List&lt;String&gt; ids = new ArrayList&lt;&gt;();
        for (OrderItem item : items) {
            String resId = "res-" + item.getProductId();
            reservations.put(resId, "RESERVED");
            ids.add(resId);
        }
        System.out.println("    Reserved " + items.size() + " item(s) for order: " + orderId);
        return ids;
    }

    public void releaseReservations(List&lt;String&gt; reservationIds) {
        for (String id : reservationIds) reservations.put(id, "RELEASED");
        System.out.println("    Released reservations: " + reservationIds);
    }
}

</code></pre><h3>Data Classes</h3><pre><code>// Simple data container for an order request
public class OrderRequest {
    private final String customerId;
    private final List&lt;OrderItem&gt; items;
    private final double amount;

    public OrderRequest(String customerId, List&lt;OrderItem&gt; items, double amount) {
        this.customerId = customerId;
        this.items = items;
        this.amount = amount;
    }

    public String getCustomerId()    { return customerId; }
    public List&lt;OrderItem&gt; getItems(){ return items; }
    public double getAmount()        { return amount; }
}

// Represents a single product in an order
public class OrderItem {
    private final String productId;
    private final int quantity;

    public OrderItem(String productId, int quantity) {
        this.productId = productId;
        this.quantity = quantity;
    }

    public String getProductId() { return productId; }
    public int getQuantity()     { return quantity; }
}
</code></pre><h3>Putting It All Together</h3><pre><code>public class Main {
    public static void main(String[] args) {
        CompensatingTransactionManager txnManager = new CompensatingTransactionManager();
        OrderProcessingService service = new OrderProcessingService(
            txnManager,
            new OrderService(),
            new PaymentService(),
            new InventoryService()
        );

        // Happy path &#8212; all steps succeed
        System.out.println("=== Successful order ===");
        OrderRequest request = new OrderRequest(
            "customer-1", List.of(new OrderItem("product-A", 2)), 49.99
        );
        System.out.println("Result: " + service.processOrder(request));

        System.out.println();

        // Failure path &#8212; payment returns null (simulating a failed charge)
        System.out.println("=== Failed payment scenario ===");
        PaymentService failingPayments = new PaymentService() {
            @Override
            public String processPayment(String orderId, double amount) {
                System.out.println("    Payment declined!");
                return null; // Returning null signals failure to the step
            }
        };
        OrderProcessingService serviceWithFailure = new OrderProcessingService(
            new CompensatingTransactionManager(),
            new OrderService(), failingPayments,
            new InventoryService()
        );
        System.out.println("Result: " + serviceWithFailure.processOrder(request));
    }
}</code></pre><h3>Example Output</h3><pre><code>=== Successful order ===
Started transaction: txn-1
  &#10003; Create Order
  &#10003; Process Payment
  &#10003; Reserve Inventory
Transaction completed: txn-1
Result: order-1234567890

=== Failed payment scenario ===
Started transaction: txn-1
  &#10003; Create Order
    Payment declined!
  &#10007; Process Payment failed &#8212; starting compensation
Compensating transaction: txn-1
  &#8617; Undid: Create Order
Transaction rolled back: txn-1
Result: Order failed &#8212; all changes have been rolled back</code></pre><h3>When Compensation Itself Fails</h3><p>Compensation can also fail: a refund API times out, a service is down. When that happens, the system is in an inconsistent state that can't be resolved automatically. Here's how to handle it:</p><ul><li><p><strong>Retry with backoff</strong>: Most failures are transient. Retrying the compensation (with a short delay between attempts) resolves the majority of cases. This is why compensation operations must be idempotent: retrying a refund should never charge twice.</p></li><li><p><strong>Log to a failed-compensation store</strong>: Persist the details of every failed compensation. A background job can pick these up and retry them later, even across restarts.</p></li><li><p><strong>Alert a human</strong>: If retries are exhausted, escalate. At that point it requires manual intervention. Someone reviews the logs and resolves the inconsistency directly.</p></li></ul><p>There is no fully automated solution for every scenario. Compensation is best-effort: the goal is to resolve failures automatically in most cases, with a clear escalation path for the rest.</p><h2>When to Use Compensating Transaction Pattern</h2><p>Understanding when to apply the Compensating Transaction pattern is crucial for making the right architectural decisions. Here's when it shines and when alternatives might be better:</p><h3>&#9989; Ideal Scenarios:</h3><ul><li><p>Your system performs multi-step distributed operations that span multiple independent services.</p></li><li><p>You need eventual consistency across service boundaries without traditional ACID transactions.</p></li><li><p>Business operations are logically reversible <em>(refunds, cancellations, inventory releases).</em></p></li><li><p>You're building event-driven or microservices architectures where tight coupling is undesirable.</p></li><li><p>Partial transaction failures require clean, auditable rollback mechanisms.</p></li><li><p>You need to maintain transaction history for compliance or debugging purposes.</p></li></ul><h3>&#10060; Skip It When:</h3><ul><li><p>Strong ACID consistency is required: use traditional database transactions instead.</p></li><li><p>Operations are not logically reversible or meaningful compensation cannot be defined.</p></li><li><p>Your system is a monolith where local transactions within a single database suffice.</p></li><li><p>The added complexity of compensation logic outweighs the consistency benefits.</p></li><li><p>All operations happen within a single service or database boundary.</p></li><li><p>Real-time consistency is critical and eventual consistency is unacceptable.</p></li></ul><h2>Best Practices</h2><p><strong>Make compensation idempotent</strong>: Retrying a failed compensation must produce the same result.</p><p><strong>Persist transaction state</strong>: In memory storage won't survive a crash during compensation. Use PostgreSQL or Redis.</p><p><strong>Compensate in reverse order</strong>: Undo steps in reverse to respect business logic dependencies during rollback.</p><p><strong>Alert on compensation failures</strong>: Log and surface failed compensations immediately as they require human intervention.</p><p><strong>Set transaction timeouts</strong>: Long running incomplete transactions should automatically trigger compensation.</p><p><strong>Monitor compensation rates</strong>: Rising compensation frequency signals upstream reliability issues worth investigating.</p><p><em>Found this helpful? Share it with a colleague who's struggling with distributed transaction management in their microservices architecture. Got questions? We'd love to hear from you at <a href="/cdn-cgi/l/email-protection#44272b2a3025273004372c2d2230212821322530216a202132">[email&nbsp;protected]</a></em></p>]]></content:encoded></item><item><title><![CDATA[Memento Pattern: Capture and Restore Object State for Effortless Undo/Redo]]></title><description><![CDATA[Master the Memento design pattern in Java with step by step implementation, diagram editor examples, and practical code for building clean undo/redo and snapshot functionality.]]></description><link>https://newsletter.shiftelevate.dev/p/memento-pattern-capture-and-restore-object-state-for-effortless-undo-redo</link><guid isPermaLink="false">https://newsletter.shiftelevate.dev/p/memento-pattern-capture-and-restore-object-state-for-effortless-undo-redo</guid><dc:creator><![CDATA[Shift Elevate]]></dc:creator><pubDate>Tue, 17 Mar 2026 05:36:26 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/11769fff-c8ac-46c6-b5c1-7de5a7eae3a3_1200x630.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>The Pain of Managing State History</h2><p><strong>Picture this:</strong> You're building a diagramming tool. Users can add shapes, reposition them, and rename the canvas. Simple enough. Then they ask for the one feature every editor needs: the ability to undo changes.</p><p>Your first instinct is to store a copy of the previous state directly on the canvas class. Then users want multiple undo steps, so you replace that single reference with a list. Then they want redo. The <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">DiagramCanvas</mark></em>, which should only know about shapes and titles, is now managing its own edit history:</p><pre><code>public class DiagramCanvas {
    private String title;
    private List&lt;String&gt; shapes;

    private DiagramCanvas previousState;

    public DiagramCanvas snapshot() {
        DiagramCanvas copy = new DiagramCanvas(title);
        copy.shapes = new ArrayList&lt;&gt;(this.shapes);
        return copy;
    }

    public void addShape(String shape) {
        this.previousState = this.snapshot();
        shapes.add(shape);
    }

    public void undo() {
        if (previousState == null) return;
        this.shapes = previousState.shapes;
        this.title = previousState.title;
        this.previousState = null;
    }
}
</code></pre><p>Every new canvas property means updating the copy and restore logic. Redo needs yet another reference. The class has become its own historian, violating Single Responsibility and making it fragile to extend.</p><p>The <strong>Memento</strong> pattern solves this by externalizing state snapshots into dedicated objects, keeping the originator clean while giving you unlimited undo/redo history.</p><h2>Understanding the Memento Pattern</h2><p><em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">The Memento pattern captures an object's internal state at a point in time and stores it externally, so the object can be restored to that state later, without exposing its internal structure.</mark></em></p><p>Think of it like a word processor's version history: before you make a big edit, the app quietly snapshots your document. If you don't like the result, you restore the snapshot. The document knows how to create and apply snapshots; the version history manager just holds onto them.</p><p>This pattern promotes <strong>Single Responsibility</strong>, <strong>Encapsulation</strong>, and <strong>Separation of Concerns</strong> by keeping history management out of the originator.</p><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tDb8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc9ecd91-2ee6-4ec9-96e9-b6b96953e89c_570x490.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tDb8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc9ecd91-2ee6-4ec9-96e9-b6b96953e89c_570x490.png 424w, https://substackcdn.com/image/fetch/$s_!tDb8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc9ecd91-2ee6-4ec9-96e9-b6b96953e89c_570x490.png 848w, https://substackcdn.com/image/fetch/$s_!tDb8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc9ecd91-2ee6-4ec9-96e9-b6b96953e89c_570x490.png 1272w, https://substackcdn.com/image/fetch/$s_!tDb8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc9ecd91-2ee6-4ec9-96e9-b6b96953e89c_570x490.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!tDb8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc9ecd91-2ee6-4ec9-96e9-b6b96953e89c_570x490.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dc9ecd91-2ee6-4ec9-96e9-b6b96953e89c_570x490.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!tDb8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc9ecd91-2ee6-4ec9-96e9-b6b96953e89c_570x490.png 424w, https://substackcdn.com/image/fetch/$s_!tDb8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc9ecd91-2ee6-4ec9-96e9-b6b96953e89c_570x490.png 848w, https://substackcdn.com/image/fetch/$s_!tDb8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc9ecd91-2ee6-4ec9-96e9-b6b96953e89c_570x490.png 1272w, https://substackcdn.com/image/fetch/$s_!tDb8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc9ecd91-2ee6-4ec9-96e9-b6b96953e89c_570x490.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><p>Memento Pattern Components</p><h3>Core Components</h3><ul><li><p><strong><a href="#memento">Memento</a></strong>: A snapshot of the originator's state at a specific point in time, immutable and opaque to everyone except the originator.</p></li><li><p><strong><a href="#originator">Originator</a></strong>: The object whose state needs to be saved; creates mementos and uses them to restore its own state.</p></li><li><p><strong><a href="#caretaker">Caretaker</a></strong>: Manages the history of mementos <em>(undo/redo stacks)</em> without inspecting their contents.</p></li></ul><h2>Complete Java Implementation</h2><p>Let's build a diagram editor that demonstrates the Memento pattern's power in managing state history without polluting the core canvas object.</p><h3>Memento</h3><pre><code>class CanvasSnapshot {
    private final String title;
    private final List&lt;String&gt; shapes;

    CanvasSnapshot(String title, List&lt;String&gt; shapes) {
        this.title = title;
        this.shapes = List.copyOf(shapes);
    }

    String getTitle() { return title; }
    List&lt;String&gt; getShapes() { return shapes; }
}
</code></pre><h3>Originator</h3><pre><code>public class DiagramCanvas {
    private String title;
    private List&lt;String&gt; shapes;

    public DiagramCanvas(String title) {
        this.title = title;
        this.shapes = new ArrayList&lt;&gt;();
    }

    public void addShape(String shape) {
        shapes.add(shape);
    }

    public CanvasSnapshot save() {
        return new CanvasSnapshot(title, shapes);
    }

    public void restore(CanvasSnapshot snapshot) {
        this.title = snapshot.getTitle();
        this.shapes = new ArrayList&lt;&gt;(snapshot.getShapes());
    }

    public void print() {
        System.out.println("  Title  : " + title);
        System.out.println("  Shapes : " + (shapes.isEmpty() ? "(none)" : String.join(", ", shapes)));
    }
}
</code></pre><h3>Caretaker</h3><pre><code>public class CanvasHistory {
    private Deque&lt;CanvasSnapshot&gt; undoStack = new ArrayDeque&lt;&gt;();
    private Deque&lt;CanvasSnapshot&gt; redoStack = new ArrayDeque&lt;&gt;();

    public void save(DiagramCanvas canvas) {
        undoStack.push(canvas.save());
        redoStack.clear();
        System.out.println("[History] Saved. Undo steps available: " + undoStack.size());
    }

    public void undo(DiagramCanvas canvas) {
        if (undoStack.isEmpty()) {
            System.out.println("[History] Nothing to undo.");
            return;
        }
        redoStack.push(canvas.save());
        canvas.restore(undoStack.pop());
        System.out.println("[History] Undo applied.");
    }

    public void redo(DiagramCanvas canvas) {
        if (redoStack.isEmpty()) {
            System.out.println("[History] Nothing to redo.");
            return;
        }
        undoStack.push(canvas.save());
        canvas.restore(redoStack.pop());
        System.out.println("[History] Redo applied.");
    }
}
</code></pre><h3>Client</h3><pre><code>public class DiagramEditorDemo {
    public static void main(String[] args) {
        System.out.println("=== Diagram Editor ===\n");

        DiagramCanvas canvas = new DiagramCanvas("Untitled Diagram");
        CanvasHistory history = new CanvasHistory();

        System.out.println("-- Initial canvas --");
        canvas.print();

        System.out.println("\n-- Adding a rectangle --");
        history.save(canvas);
        canvas.addShape("Rectangle: (10, 20, 100x50)");
        canvas.print();

        System.out.println("\n-- Adding a circle --");
        history.save(canvas);
        canvas.addShape("Circle: (150, 30, r=40)");
        canvas.print();

        System.out.println("\n-- Undo --");
        history.undo(canvas);
        canvas.print();

        System.out.println("\n-- Redo --");
        history.redo(canvas);
        canvas.print();
    }
}</code></pre><h4>Expected Output:</h4><pre><code>=== Diagram Editor ===

-- Initial canvas --
  Title  : Untitled Diagram
  Shapes : (none)

-- Adding a rectangle --
[History] Saved. Undo steps available: 1
  Title  : Untitled Diagram
  Shapes : Rectangle: (10, 20, 100x50)

-- Adding a circle --
[History] Saved. Undo steps available: 2
  Title  : Untitled Diagram
  Shapes : Rectangle: (10, 20, 100x50), Circle: (150, 30, r=40)

-- Undo --
[History] Undo applied.
  Title  : Untitled Diagram
  Shapes : Rectangle: (10, 20, 100x50)

-- Redo --
[History] Redo applied.
  Title  : Untitled Diagram
  Shapes : Rectangle: (10, 20, 100x50), Circle: (150, 30, r=40)</code></pre><h3>&#128640; Get the Complete Implementation</h3><p>The full code with incremental saves and versioned snapshot support is available in our <a href="https://github.com/shift-elevate/design-patterns">Design Patterns Repository</a>.</p><pre><code># Clone and run the complete demo
git clone https://github.com/shift-elevate/design-patterns.git
cd design-patterns
mvn test -Dtest=DiagramEditorMementoTest</code></pre><h2>Real World Examples</h2><p>The Memento pattern is used extensively in applications that require state history management:</p><h3>1. Text Editors and IDEs</h3><p>Every modern editor uses the Memento pattern under the hood for undo/redo. Each keystroke or save point creates a memento of the buffer's state. The editor's history manager <em>(caretaker)</em> maintains stacks for undo and redo, and the document <em>(originator)</em> knows how to serialize and restore its own state without the history manager ever needing to inspect it.</p><h3>2. Database Transaction Rollback</h3><p>Database engines use memento-like mechanisms to implement transaction rollback. Before executing a transaction, the engine captures the affected pages or row states as a memento <em>(the transaction log)</em>. If the transaction fails or is explicitly rolled back, the engine restores the pre-transaction state from the log, without the transaction itself knowing how the snapshot is stored.</p><h2>When to Use the Memento Pattern</h2><p>Understanding when to apply the Memento pattern is crucial for making the right architectural decisions. Here's when it shines and when alternatives might be better:</p><h3>&#9989;<strong> Ideal Scenarios:</strong></h3><ul><li><p>You need to implement undo/redo functionality.</p></li><li><p>You want to create snapshots or save points without exposing an object's internals.</p></li><li><p>A direct interface to an object's state would expose implementation details.</p></li><li><p>The cost of state snapshots is acceptable relative to the benefit of history management.</p></li><li><p>You want to keep the originator's class focused on its own responsibilities.</p></li></ul><h3>&#10060;<strong> Skip It When:</strong></h3><ul><li><p>The object's state is extremely large and snapshotting is too memory-intensive.</p></li><li><p>State changes are continuous and fine-grained <em>(e.g., every pixel in a video stream)</em>.</p></li><li><p>The originator's internal state cannot be meaningfully serialized or copied.</p></li><li><p>A simpler command-based history <em>(storing inverse operations)</em> would be more efficient.</p></li></ul><h2>Next Steps: Apply the Memento Pattern in Your Project</h2><p>Ready to implement the Memento pattern in your own projects? Here's a structured approach to get you started:</p><ol><li><p><strong>Identify State to Capture</strong>: Find the fields in your originator that define its meaningful state, the ones users would want to undo.</p></li><li><p><strong>Create the Memento Class</strong>: Snapshot those fields immutably. Defensive copy any mutable collections.</p></li><li><p><strong>Add Save/Restore to the Originator</strong>: Implement <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">save()</mark></em> to produce a memento and <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">restore()</mark></em> to apply one, keeping all other logic out.</p></li><li><p><strong>Build the Caretaker</strong>: Use two stacks <em>(undo/redo)</em>. Save before each action, clear redo on new actions.</p></li><li><p><strong>Wire the Client</strong>: Call <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">history.save(originator)</mark></em> before modifying state, then let users trigger <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">undo</mark></em> and <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">redo</mark></em> through your UI or API.</p></li></ol><p>The Memento pattern cleanly separates <em>what</em> state looks like from <em>who</em> manages its history. Your originator stays focused on its own domain, your caretaker stays focused on history, and your users get the reliable, intuitive undo/redo experience they expect.</p><p><em>Found this helpful? Share it with a colleague who's wrestling with undo/redo or snapshot logic in their application. Got questions? We'd love to hear from you at <a href="/cdn-cgi/l/email-protection#e0838f8e94818394a09388898694858c8596819485ce848596">[email&nbsp;protected]</a></em></p>]]></content:encoded></item><item><title><![CDATA[Queue-based Load Leveling Pattern: Managing Variable Workloads with Message Queues]]></title><description><![CDATA[Master the Queue-based Load Leveling pattern with message queues, auto-scaling, and production-ready Java implementations for handling variable workloads in distributed systems.]]></description><link>https://newsletter.shiftelevate.dev/p/queue-based-load-leveling-pattern-managing-variable-workloads-with-message-queues</link><guid isPermaLink="false">https://newsletter.shiftelevate.dev/p/queue-based-load-leveling-pattern-managing-variable-workloads-with-message-queues</guid><dc:creator><![CDATA[Shift Elevate]]></dc:creator><pubDate>Thu, 05 Feb 2026 13:19:27 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/bd809e88-77ba-4968-9f52-d421c2b415bb_1200x630.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>In distributed systems, workloads often arrive in unpredictable bursts that can overwhelm downstream services. The Queue-based Load Leveling pattern uses message queues as buffers to smooth out these bursts, ensuring consistent performance and preventing service overload while maintaining system responsiveness.</em></p><p>This pattern is essential for handling variable workloads in cloud applications, where traffic spikes can cause performance degradation or service failures. By implementing intelligent queuing mechanisms, you can create more resilient and predictable systems.</p><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wyGm!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fffc872c6-1abc-447d-91ba-914c57b6ab2c_1920x1005.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wyGm!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fffc872c6-1abc-447d-91ba-914c57b6ab2c_1920x1005.png 424w, https://substackcdn.com/image/fetch/$s_!wyGm!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fffc872c6-1abc-447d-91ba-914c57b6ab2c_1920x1005.png 848w, https://substackcdn.com/image/fetch/$s_!wyGm!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fffc872c6-1abc-447d-91ba-914c57b6ab2c_1920x1005.png 1272w, https://substackcdn.com/image/fetch/$s_!wyGm!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fffc872c6-1abc-447d-91ba-914c57b6ab2c_1920x1005.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!wyGm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fffc872c6-1abc-447d-91ba-914c57b6ab2c_1920x1005.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ffc872c6-1abc-447d-91ba-914c57b6ab2c_1920x1005.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!wyGm!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fffc872c6-1abc-447d-91ba-914c57b6ab2c_1920x1005.png 424w, https://substackcdn.com/image/fetch/$s_!wyGm!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fffc872c6-1abc-447d-91ba-914c57b6ab2c_1920x1005.png 848w, https://substackcdn.com/image/fetch/$s_!wyGm!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fffc872c6-1abc-447d-91ba-914c57b6ab2c_1920x1005.png 1272w, https://substackcdn.com/image/fetch/$s_!wyGm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fffc872c6-1abc-447d-91ba-914c57b6ab2c_1920x1005.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a><p>This guide walks you through the Queue-based Load Leveling pattern from concept to practical implementation, covering sample producer and consumer implementations, with best practices and production deployment considerations.</p><ul><li><p><a href="#understanding-the-queuebased-load-l">Understanding the Queue-based Load Leveling Pattern</a></p></li><li><p><a href="#implementing-the-queuebased-load-le">Implementing the Queue-based Load Leveling Pattern</a></p></li><li><p><a href="#when-to-use-queuebased-load-levelin">When to Use Queue-based Load Leveling Pattern</a></p></li><li><p><a href="#best-practices">Best Practices</a></p></li></ul><h2>Understanding the Queue-based Load Leveling Pattern</h2><p>The Queue-based Load Leveling pattern introduces a queue between service producers and consumers to buffer requests and smooth out workload variations. This creates a more predictable processing pattern and prevents downstream services from being overwhelmed.</p><h3>Core Architecture</h3><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!QKDh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8578b0e9-dee6-4b02-8b23-b5623aeffaed_1098x1351.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!QKDh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8578b0e9-dee6-4b02-8b23-b5623aeffaed_1098x1351.png 424w, https://substackcdn.com/image/fetch/$s_!QKDh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8578b0e9-dee6-4b02-8b23-b5623aeffaed_1098x1351.png 848w, https://substackcdn.com/image/fetch/$s_!QKDh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8578b0e9-dee6-4b02-8b23-b5623aeffaed_1098x1351.png 1272w, https://substackcdn.com/image/fetch/$s_!QKDh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8578b0e9-dee6-4b02-8b23-b5623aeffaed_1098x1351.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!QKDh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8578b0e9-dee6-4b02-8b23-b5623aeffaed_1098x1351.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8578b0e9-dee6-4b02-8b23-b5623aeffaed_1098x1351.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!QKDh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8578b0e9-dee6-4b02-8b23-b5623aeffaed_1098x1351.png 424w, https://substackcdn.com/image/fetch/$s_!QKDh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8578b0e9-dee6-4b02-8b23-b5623aeffaed_1098x1351.png 848w, https://substackcdn.com/image/fetch/$s_!QKDh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8578b0e9-dee6-4b02-8b23-b5623aeffaed_1098x1351.png 1272w, https://substackcdn.com/image/fetch/$s_!QKDh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8578b0e9-dee6-4b02-8b23-b5623aeffaed_1098x1351.png 1456w" sizes="100vw"></picture><div></div></div></a><p>Queue Based Leveling Pattern Interactions</p><h3>Key Benefits</h3><ul><li><p><strong>Load Smoothing</strong>: Converts variable workloads into consistent processing patterns.</p></li><li><p><strong>Service Protection</strong>: Prevents downstream services from being overwhelmed.</p></li><li><p><strong>Scalability</strong>: Enables independent scaling of producers and consumers.</p></li><li><p><strong>Resilience</strong>: Provides buffering during service outages or slowdowns.</p></li><li><p><strong>Backpressure Management</strong>: Controls the rate of processing based on consumer capacity.</p></li></ul><h2>Implementing the Queue-based Load Leveling Pattern</h2><p>Let's build a queue-based load leveling system that demonstrates the core pattern with producers, consumers, and proper message handling.</p><h3>Implementation Overview</h3><ul><li><p><strong>LoadLevelingQueue</strong>: Core bounded queue with capacity limits and basic tracking</p></li><li><p><strong>MessageProducer</strong>: Enqueues messages with retry support for handling full queues</p></li><li><p><strong>MessageConsumer</strong>: Processes messages from the queue with configurable threading</p></li><li><p><strong>Order Processing Example</strong>: Practical demonstration of the pattern</p></li></ul><h4>Note on Implementation</h4><p>This implementation uses Java's <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">BlockingQueue</mark></em> for thread-safe message handling. For production systems, consider using dedicated message brokers like RabbitMQ, Apache Kafka, or cloud services like AWS SQS for durability, persistence, and distributed scaling.</p><h3>Core Queue Implementation</h3><pre><code>public class LoadLevelingQueue&lt;T&gt; {
    private final BlockingQueue&lt;T&gt; queue;
    private final String name;
    private final AtomicLong totalMessages = new AtomicLong(0);
    private final AtomicLong rejectedMessages = new AtomicLong(0);

    public LoadLevelingQueue(String name, int capacity) {
        this.name = name;
        this.queue = new ArrayBlockingQueue&lt;&gt;(capacity);
    }

    public boolean enqueue(T message) {
        boolean success = queue.offer(message);
        if (success) {
            totalMessages.incrementAndGet();
        } else {
            rejectedMessages.incrementAndGet();
        }
        return success;
    }

    public T dequeue(long timeout, TimeUnit unit) throws InterruptedException {
        return queue.poll(timeout, unit);
    }

    // Monitoring methods
    public int getCurrentSize() { return queue.size(); }
    public int getRemainingCapacity() { return queue.remainingCapacity(); }
    public long getTotalMessages() { return totalMessages.get(); }
    public long getRejectedMessages() { return rejectedMessages.get(); }
}</code></pre><h3>Message Producer Implementation</h3><pre><code>public class MessageProducer&lt;T&gt; {
    private final LoadLevelingQueue&lt;T&gt; queue;

    public MessageProducer(LoadLevelingQueue&lt;T&gt; queue) {
        this.queue = queue;
    }

    public boolean produce(T message) {
        return queue.enqueue(message);
    }

    public boolean produceWithRetry(T message, int maxRetries, long retryDelayMs) {
        for (int attempt = 0; attempt &lt; maxRetries; attempt++) {
            if (produce(message)) {
                return true;
            }

            if (attempt &lt; maxRetries - 1) {
                try {
                    Thread.sleep(retryDelayMs);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    return false;
                }
            }
        }

        // All retries failed - send to dead letter queue or alert
        System.err.println("Failed to enqueue message after " + maxRetries + " retries");
        return false;
    }
}</code></pre><h3>Message Consumer Implementation</h3><pre><code>public class MessageConsumer&lt;T&gt; {
    private final LoadLevelingQueue&lt;T&gt; queue;
    private final Function&lt;T, Boolean&gt; messageProcessor;
    private final ExecutorService executorService;
    private final AtomicBoolean running = new AtomicBoolean(false);

    public MessageConsumer(String name, LoadLevelingQueue&lt;T&gt; queue, Function&lt;T, Boolean&gt; processor) {
        this.queue = queue;
        this.messageProcessor = processor;
        this.executorService = Executors.newSingleThreadExecutor(r -&gt; {
            Thread t = new Thread(r, name + "-consumer");
            t.setDaemon(false);
            return t;
        });
    }

    public void start() {
        if (running.compareAndSet(false, true)) {
            executorService.submit(this::consumeMessages);
        }
    }

    public void stop() {
        running.set(false);
        executorService.shutdown();
        try {
            if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
                executorService.shutdownNow();
            }
        } catch (InterruptedException e) {
            executorService.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }

    private void consumeMessages() {
        while (running.get()) {
            try {
                T message = queue.dequeue(1, TimeUnit.SECONDS);
                if (message != null) {
                    messageProcessor.apply(message);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            } catch (Exception e) {
                System.err.println("Error processing message: " + e.getMessage());
            }
        }
    }
}</code></pre><h3>Practical Example: Order Processing System</h3><pre><code>public class OrderProcessingSystem {
    private final LoadLevelingQueue&lt;Order&gt; orderQueue;
    private final MessageProducer&lt;Order&gt; orderProducer;
    private final List&lt;MessageConsumer&lt;Order&gt;&gt; orderConsumers;

    public OrderProcessingSystem(int queueCapacity, int consumerCount) {
        this.orderQueue = new LoadLevelingQueue&lt;&gt;("order-queue", queueCapacity);
        this.orderProducer = new MessageProducer&lt;&gt;(orderQueue);
        this.orderConsumers = new ArrayList&lt;&gt;();

        for (int i = 0; i &lt; consumerCount; i++) {
            MessageConsumer&lt;Order&gt; consumer = new MessageConsumer&lt;&gt;(
                "order-consumer-" + i,
                orderQueue,
                this::processOrder
            );
            orderConsumers.add(consumer);
        }
    }

    public void start() {
        for (MessageConsumer&lt;Order&gt; consumer : orderConsumers) {
            consumer.start();
        }
    }

    public void stop() {
        for (MessageConsumer&lt;Order&gt; consumer : orderConsumers) {
            consumer.stop();
        }
    }

    public boolean submitOrder(Order order) {
        return orderProducer.produce(order);
    }

    private boolean processOrder(Order order) {
        try {
            // Simulate order processing (100-300ms)
            Thread.sleep(100 + (long)(Math.random() * 200));
            System.out.println("Processed order " + order.getId() + " for customer " + order.getCustomerId());
            return true;
        } catch (Exception e) {
            System.err.println("Failed to process order: " + e.getMessage());
            return false;
        }
    }
}

// Supporting classes
record Order(String id, String customerId, double totalAmount) {}</code></pre><h3>Putting It All Together</h3><pre><code>public class Main {
    public static void main(String[] args) throws InterruptedException {
        // Create system with queue capacity of 100 and 3 consumers
        OrderProcessingSystem system = new OrderProcessingSystem(100, 3);
        system.start();

        // Simulate variable workload - 50 orders arriving quickly
        System.out.println("Submitting 50 orders in burst...");
        for (int i = 1; i &lt;= 50; i++) {
            boolean accepted = system.submitOrder(new Order("ORDER-" + i, "CUST-" + i, 99.99));
            System.out.println("Order " + i + " submitted: " + (accepted ? "accepted" : "rejected"));
        }

        // Let consumers process
        Thread.sleep(5000);
        system.stop();
    }
}</code></pre><h3>Example Output</h3><pre><code>Submitting 50 orders in burst...
Order 1 submitted: accepted
Order 2 submitted: accepted
...
Order 50 submitted: accepted
Processed order ORDER-1 for customer CUST-1
Processed order ORDER-2 for customer CUST-2
Processed order ORDER-3 for customer CUST-3
Processed order ORDER-5 for customer CUST-5
Processed order ORDER-4 for customer CUST-4
...</code></pre><h2>When to Use Queue-based Load Leveling Pattern</h2><p>Understanding when to apply the Queue-based Load Leveling pattern is crucial for making the right architectural decisions. Here's when it shines and when alternatives might be better:</p><h3>&#9989; Ideal Scenarios:</h3><ul><li><p>Your system experiences unpredictable traffic spikes that overwhelm downstream services.</p></li><li><p>You need to decouple producers from consumers for independent scaling.</p></li><li><p>You want to protect backend services from being overwhelmed during peak loads.</p></li><li><p>You need to maintain system responsiveness even when processing capacity is limited.</p></li><li><p>You're building event-driven architectures with variable workloads.</p></li><li><p>You want to absorb temporary failures by buffering requests until services recover.</p></li></ul><h3>&#10060; Skip It When:</h3><ul><li><p>Your workloads are consistent and predictable with no spikes.</p></li><li><p>Real-time processing is required with zero latency tolerance.</p></li><li><p>Message ordering is critical and cannot be compromised.</p></li><li><p>The added complexity of queue management outweighs the benefits.</p></li><li><p>Your system has sufficient capacity to handle peak loads directly.</p></li><li><p>You need synchronous request-response patterns <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">(use Circuit Breaker instead)</mark></em>.</p></li></ul><h2>Best Practices</h2><p><strong>Size Your Queue Appropriately</strong>: Balance memory usage against buffering needs. Too small causes message rejection during spikes; too large wastes resources and increases latency.</p><p><strong>Implement Dead Letter Queues</strong>: Handle messages that repeatedly fail processing. Don't lose critical data&#8212;route failed messages to a separate queue for investigation.</p><p><strong>Monitor Queue Depth</strong>: Track queue size as a key health indicator. Growing queues signal that consumers can't keep up with producers&#8212;a capacity planning signal.</p><p><strong>Use Backpressure Signals</strong>: When queues fill up, signal producers to slow down rather than silently dropping messages. Return meaningful errors to callers.</p><p><strong>Plan for Consumer Scaling</strong>: Design consumers to scale horizontally based on queue depth. Consider auto-scaling rules that add consumers when queue depth exceeds thresholds.</p><p><strong>Handle Message Ordering</strong>: If ordering matters, use partitioned queues or single consumers per partition. Parallel consumers can process messages out of order.</p><p><em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">Found this helpful? Share it with a colleague who's struggling with variable workloads in their distributed systems. Got questions? We'd love to hear from you at </mark><a href="/cdn-cgi/l/email-protection#bcdfd3d2c8dddfc8fccfd4d5dac8d9d0d9caddc8d992d8d9ca"><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">[email&nbsp;protected]</mark></a></em></p>]]></content:encoded></item><item><title><![CDATA[Chain of Responsibility Pattern: Handle Requests Through a Dynamic Chain of Handlers]]></title><description><![CDATA[Master the Chain of Responsibility design pattern with step by step implementation, expense approval workflow examples, and practical code for building flexible request processing pipelines in Java.]]></description><link>https://newsletter.shiftelevate.dev/p/chain-of-responsibility-pattern-handle-requests-through-a-dynamic-chain-of-handlers</link><guid isPermaLink="false">https://newsletter.shiftelevate.dev/p/chain-of-responsibility-pattern-handle-requests-through-a-dynamic-chain-of-handlers</guid><dc:creator><![CDATA[Shift Elevate]]></dc:creator><pubDate>Mon, 02 Feb 2026 17:00:07 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/950ff713-494a-43b5-a47b-deead601fdc9_1200x630.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>The Pain of Rigid Approval Hierarchies</h2><p><strong>Picture this:</strong> You're building an expense management system for a growing company. Your initial approach seems logical: create a method that checks the expense amount and routes it to the appropriate approver based on predefined thresholds.</p><p>Then the organization evolves. The finance team wants to add department-specific approval rules. HR needs special handling for training expenses. The CEO wants certain categories to skip levels. Your approval method becomes a tangled mess of nested conditions that breaks every time the org chart changes.</p><p>Suddenly, you're dealing with code like this:</p><pre><code>public ApprovalResult approveExpense(Expense expense) {
    if (expense.getAmount() &lt;= 500) {
        return teamLead.approve(expense);
    } else if (expense.getAmount() &lt;= 2000) {
        if (expense.getCategory().equals("TRAINING")) {
            return hrManager.approve(expense);
        }
        return manager.approve(expense);
    } else if (expense.getAmount() &lt;= 10000) {
        if (expense.getDepartment().equals("ENGINEERING")) {
            return vpEngineering.approve(expense);
        }
        return director.approve(expense);
    }
    // ... endless conditions and special cases
}</code></pre><p>Sound familiar? The <strong>Chain of Responsibility pattern</strong> solves this challenge by letting you pass requests along a chain of handlers, where each handler decides whether to process the request or pass it to the next handler in the chain.</p><h2>Understanding the Chain of Responsibility Pattern</h2><p><em>The Chain of Responsibility pattern decouples senders of requests from their receivers by giving multiple objects a chance to handle the request. The request passes along a chain until an object handles it, enabling dynamic request routing without tight coupling.</em></p><p>Think of it like an expense approval workflow in a company: when you submit an expense report, it doesn't go directly to the CEO. Instead, it starts with your team lead. If the amount exceeds their approval limit, they escalate it to the manager, who might escalate it to the director, and so on. Each person in the chain either approves the expense or passes it up the hierarchy.</p><p>This pattern promotes <strong>Loose Coupling</strong>, <strong>Single Responsibility</strong>, and <strong>Open Closed Principle</strong> while enabling flexible request handling at runtime.</p><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!sQVx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F182ce212-1914-4e5a-877c-ff3765ec6304_587x530.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!sQVx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F182ce212-1914-4e5a-877c-ff3765ec6304_587x530.png 424w, https://substackcdn.com/image/fetch/$s_!sQVx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F182ce212-1914-4e5a-877c-ff3765ec6304_587x530.png 848w, https://substackcdn.com/image/fetch/$s_!sQVx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F182ce212-1914-4e5a-877c-ff3765ec6304_587x530.png 1272w, https://substackcdn.com/image/fetch/$s_!sQVx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F182ce212-1914-4e5a-877c-ff3765ec6304_587x530.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!sQVx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F182ce212-1914-4e5a-877c-ff3765ec6304_587x530.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/182ce212-1914-4e5a-877c-ff3765ec6304_587x530.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!sQVx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F182ce212-1914-4e5a-877c-ff3765ec6304_587x530.png 424w, https://substackcdn.com/image/fetch/$s_!sQVx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F182ce212-1914-4e5a-877c-ff3765ec6304_587x530.png 848w, https://substackcdn.com/image/fetch/$s_!sQVx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F182ce212-1914-4e5a-877c-ff3765ec6304_587x530.png 1272w, https://substackcdn.com/image/fetch/$s_!sQVx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F182ce212-1914-4e5a-877c-ff3765ec6304_587x530.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><p>Chain of Responsibilities Components</p><h3>Core Components</h3><ul><li><p><strong><a href="#supporting-classes">Supporting Classes</a></strong>: Request and result objects that carry data through the chain <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">(ExpenseRequest, ApprovalResult)</mark></em></p></li><li><p><strong><a href="#abstract-handler">Abstract Handler</a></strong>: Defines the interface and implements common chaining logic for all handlers</p></li><li><p><strong><a href="#concrete-handlers">Concrete Handlers</a></strong>: Extend the abstract handler with specific approval limits and processing logic</p></li></ul><h2>Complete Java Implementation</h2><p>Let's build an expense approval system that demonstrates the Chain of Responsibility pattern's power in managing hierarchical approval workflows.</p><h3>Supporting Classes</h3><pre><code>public class ExpenseRequest {
    private String employeeName;
    private String description;
    private double amount;
    private String category;

    public ExpenseRequest(String employeeName, String description,
                         double amount, String category) {
        this.employeeName = employeeName;
        this.description = description;
        this.amount = amount;
        this.category = category;
    }

    public String getEmployeeName() { return employeeName; }
    public String getDescription() { return description; }
    public double getAmount() { return amount; }
    public String getCategory() { return category; }

    @Override
    public String toString() {
        return String.format("%s - %s ($%.2f) [%s]",
                employeeName, description, amount, category);
    }
}

public class ApprovalResult {
    private boolean approved;
    private String approverRole;
    private String message;

    public ApprovalResult(boolean approved, String approverRole, String message) {
        this.approved = approved;
        this.approverRole = approverRole;
        this.message = message;
    }

    public boolean isApproved() { return approved; }
    public String getApproverRole() { return approverRole; }
    public String getMessage() { return message; }

    @Override
    public String toString() {
        String status = approved ? "Approved" : "Rejected";
        return String.format("%s by %s - %s", status, approverRole, message);
    }
}</code></pre><h3>Abstract Handler</h3><pre><code>public abstract class ExpenseApprover {
    protected ExpenseApprover nextApprover;
    protected double approvalLimit;
    protected String role;

    public ExpenseApprover(String role, double approvalLimit) {
        this.role = role;
        this.approvalLimit = approvalLimit;
    }

    public void setNextApprover(ExpenseApprover nextApprover) {
        this.nextApprover = nextApprover;
    }

    public String getApproverRole() {
        return role;
    }

    public ApprovalResult approve(ExpenseRequest request) {
        if (canApprove(request)) {
            return processApproval(request);
        } else if (nextApprover != null) {
            System.out.println(role + " escalating to " + nextApprover.getApproverRole());
            return nextApprover.approve(request);
        } else {
            return new ApprovalResult(false, role, "Exceeds maximum approval authority");
        }
    }

    protected boolean canApprove(ExpenseRequest request) {
        return request.getAmount() &lt;= approvalLimit;
    }

    protected abstract ApprovalResult processApproval(ExpenseRequest request);
}</code></pre><h3>Concrete Handlers</h3><pre><code>public class TeamLead extends ExpenseApprover {

    public TeamLead() {
        super("Team Lead", 1000.00);
    }

    @Override
    protected ApprovalResult processApproval(ExpenseRequest request) {
        return new ApprovalResult(true, role, "Routine team expense");
    }
}

public class Manager extends ExpenseApprover {

    public Manager() {
        super("Manager", 5000.00);
    }

    @Override
    protected ApprovalResult processApproval(ExpenseRequest request) {
        return new ApprovalResult(true, role, "Departmental budget");
    }
}

public class Director extends ExpenseApprover {

    public Director() {
        super("Director", Double.MAX_VALUE);
    }

    @Override
    protected ApprovalResult processApproval(ExpenseRequest request) {
        return new ApprovalResult(true, role, "Strategic initiative");
    }
}</code></pre><h3>Client</h3><pre><code>public class ExpenseApprovalDemo {
    public static void main(String[] args) {
        System.out.println("=== Expense Approval System ===\n");

        // Build the chain of responsibility
        ExpenseApprover teamLead = new TeamLead();
        ExpenseApprover manager = new Manager();
        ExpenseApprover director = new Director();

        teamLead.setNextApprover(manager);
        manager.setNextApprover(director);

        // Process various expense requests
        ExpenseRequest[] requests = {
            new ExpenseRequest("Alice", "Office supplies", 750.00, "SUPPLIES"),
            new ExpenseRequest("Bob", "Team building event", 2500.00, "ENTERTAINMENT"),
            new ExpenseRequest("Carol", "Conference registration", 4800.00, "TRAINING"),
            new ExpenseRequest("David", "Server infrastructure", 15000.00, "EQUIPMENT")
        };

        for (ExpenseRequest request : requests) {
            System.out.println("Request: " + request);
            ApprovalResult result = teamLead.approve(request);
            System.out.println("Result: " + result);
            System.out.println();
        }
    }
}</code></pre><h4>Expected Output:</h4><pre><code>=== Expense Approval System ===

Request: Alice - Office supplies ($750.00) [SUPPLIES]
Result: Approved by Team Lead - Routine team expense

Request: Bob - Team building event ($2500.00) [ENTERTAINMENT]
Team Lead escalating to Manager
Result: Approved by Manager - Departmental budget

Request: Carol - Conference registration ($4800.00) [TRAINING]
Team Lead escalating to Manager
Result: Approved by Manager - Departmental budget

Request: David - Server infrastructure ($15000.00) [EQUIPMENT]
Team Lead escalating to Manager
Manager escalating to Director
Result: Approved by Director - Strategic initiative</code></pre><h3>&#128640; Get the Complete Implementation</h3><p>The full code with advanced chain configurations and dynamic handler insertion is available in our <a href="https://github.com/shift-elevate/design-patterns">Design Patterns Repository</a>.</p><pre><code># Clone and run the complete demo
git clone https://github.com/shift-elevate/design-patterns.git
cd design-patterns
mvn test -Dtest=ChainOfResponsibilityPatternTest</code></pre><h2>Real World Examples</h2><p>The Chain of Responsibility pattern is extensively used in real-world applications:</p><h3>1. Servlet Filters and Middleware</h3><p>Web frameworks use filter chains to process HTTP requests through multiple handlers. Each filter can authenticate, log, compress, or modify the request before passing it to the next filter. This enables modular request processing where new filters can be added without modifying existing ones.</p><h3>2. Event Handling in UI Frameworks</h3><p>GUI frameworks use event bubbling where UI events propagate through a hierarchy of components. A button click event first goes to the button, then its container, then the window, until some handler processes it. This allows flexible event handling at any level of the component tree.</p><h3>3. Logging Frameworks</h3><p>Logging systems route messages through handler chains based on severity levels. A DEBUG message might go to a file handler, while an ERROR message triggers file logging, email notification, and alerting systems. Each handler decides whether to process the message based on its configured threshold.</p><h2>When to Use Chain of Responsibility Pattern</h2><p>Understanding when to apply the Chain of Responsibility pattern is crucial for making the right architectural decisions. Here's when it shines and when alternatives might be better:</p><h3>&#9989; Ideal Scenarios:</h3><ul><li><p>Multiple objects may handle a request, and the handler isn't known in advance.</p></li><li><p>You want to issue a request to several objects without specifying the receiver explicitly.</p></li><li><p>The set of handlers should be specified dynamically at runtime.</p></li><li><p>You want to decouple request senders from receivers.</p></li><li><p>Requests need to be processed by multiple handlers in sequence.</p></li></ul><h3>&#10060; Skip It When:</h3><ul><li><p>There's always exactly one handler for each request type.</p></li><li><p>The handling logic is simple and unlikely to change.</p></li><li><p>You need guaranteed request handling <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">(chains can leave requests unhandled)</mark></em>.</p></li><li><p>Performance is critical and chain traversal overhead is unacceptable.</p></li></ul><h2>Next Steps: Apply Chain of Responsibility Pattern in Your Project</h2><p>Ready to implement the Chain of Responsibility pattern in your own projects? Here's a structured approach to get you started:</p><ol><li><p><strong>Identify Request Flow</strong>: Look for scenarios where requests pass through multiple processing stages (approvals, validations, transformations).</p></li><li><p><strong>Define Abstract Handler</strong>: Create an abstract class with common chaining logic and successor management.</p></li><li><p><strong>Create Concrete Handlers</strong>: Extend the abstract handler for each processing step with specific logic.</p></li><li><p><strong>Configure the Chain</strong>: Set up the chain order based on your business requirements.</p></li><li><p><strong>Add Fallback Handling</strong>: Ensure requests that reach the end of the chain are handled appropriately.</p></li></ol><p>The Chain of Responsibility pattern transforms rigid, tightly-coupled request handling into flexible, extensible processing pipelines. By decoupling senders from receivers and enabling dynamic handler configuration, you build systems that adapt to changing business rules while maintaining clean, testable code.</p><p><em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">Found this helpful? Share it with a colleague who's struggling with complex approval workflows or request routing logic. Got questions? We'd love to hear from you at </mark><a href="/cdn-cgi/l/email-protection#1d7e7273697c7e695d6e75747b697871786b7c69783379786b"><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">[email&nbsp;protected]</mark></a></em></p>]]></content:encoded></item><item><title><![CDATA[Hardcoded Strings: Replace Magic String with Named Constant Refactoring | Clean Code]]></title><description><![CDATA[Refactor the Hardcoded Strings code smell with Replace Magic String with Named Constant technique, improve maintainability, and reduce typos.]]></description><link>https://newsletter.shiftelevate.dev/p/hardcoded-strings-replace-magic-string-with-named-constant-refactoring-clean-code</link><guid isPermaLink="false">https://newsletter.shiftelevate.dev/p/hardcoded-strings-replace-magic-string-with-named-constant-refactoring-clean-code</guid><dc:creator><![CDATA[Shift Elevate]]></dc:creator><pubDate>Sat, 24 Jan 2026 01:14:24 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/ed3bc1dc-a4f3-4875-a9ed-9d6f23cf1dbf_1200x630.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Hardcoded Strings are literal string values scattered throughout code that lack clear meaning and are difficult to maintain.</em></p><p>We will see how we can refactor using the Replace Magic String with Named Constant technique for better maintainability and configuration management.</p><h3>Clean Code Reference</h3><p>&#10077;</p><p><strong>&#9888;&#65039; Code Smell:</strong><span data-color="rgb(204, 204, 204)" style="color: rgb(204, 204, 204);"> </span><em>Hardcoded Strings</em><br>&#9989;<strong> Refactoring:</strong><span data-color="rgb(204, 204, 204)" style="color: rgb(204, 204, 204);"> </span><em>Replace Magic String with Named Constant</em><br>&#127919;<strong> Goal:</strong><span data-color="rgb(204, 204, 204)" style="color: rgb(204, 204, 204);"> </span><em>Centralized string management and better maintainability</em></p><p>The Hardcoded Strings code smell occurs when literal string values are scattered throughout code without clear meaning or context. The <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">Replace Magic String with Named Constant</mark></em> refactoring technique replaces these literals with descriptive constants that make the code more maintainable and easier to configure.</p><h2>The Code Smell: Hardcoded Strings</h2><p>Hardcoded Strings are one of the most common code smells that reduce code maintainability and make configuration changes difficult. When literal strings appear throughout code without clear context, they make the code harder to understand, modify, and maintain. These strings often represent configuration values, error messages, or business rules that should be centralized.</p><p>Symptoms</p><p>Impact</p><p><em>Literal strings without clear meaning</em></p><p><em>Reduced maintainability</em></p><p><em>Repeated hardcoded strings throughout code</em></p><p><em>Difficult to configure</em></p><p><em>Business rules hidden in strings</em></p><p><em>Higher bug risk</em></p><h4>Here's a typical example of Hardcoded Strings:</h4><pre><code>public class UserService {

    public void createUser(String username, String email, String role) {
        if (!"ADMIN".equals(role) &amp;&amp; !"USER".equals(role) &amp;&amp; !"MODERATOR".equals(role)) {
            throw new IllegalArgumentException("Invalid role. Must be ADMIN, USER, or MODERATOR");
        }

        User user = new User();
        user.setUsername(username);
        user.setEmail(email);
        user.setRole(role);
        user.setStatus("ACTIVE");

        userRepository.save(user);
        emailService.sendEmail(email, "Welcome to Our Platform",
            "Welcome " + username + "! Your account has been created.");
    }

    public void updateUserStatus(String username, String newStatus) {
        if (!"ACTIVE".equals(newStatus) &amp;&amp; !"INACTIVE".equals(newStatus) &amp;&amp; !"SUSPENDED".equals(newStatus)) {
            throw new IllegalArgumentException("Invalid status. Must be ACTIVE, INACTIVE, or SUSPENDED");
        }

        User user = userRepository.findByUsername(username);
        user.setStatus(newStatus);
        userRepository.save(user);

        if ("SUSPENDED".equals(newStatus)) {
            emailService.sendEmail(user.getEmail(), "Account Suspended",
                "Your account has been suspended.");
        }
    }
}
public class UserService {

    public void createUser(String username, String email, String role) {
        if (!"ADMIN".equals(role) &amp;&amp; !"USER".equals(role) &amp;&amp; !"MODERATOR".equals(role)) {
            throw new IllegalArgumentException("Invalid role. Must be ADMIN, USER, or MODERATOR");
        }

        User user = new User();
        user.setUsername(username);
        user.setEmail(email);
        user.setRole(role);
        user.setStatus("ACTIVE");

        userRepository.save(user);
        emailService.sendEmail(email, "Welcome to Our Platform",
            "Welcome " + username + "! Your account has been created.");
    }

    public void updateUserStatus(String username, String newStatus) {
        if (!"ACTIVE".equals(newStatus) &amp;&amp; !"INACTIVE".equals(newStatus) &amp;&amp; !"SUSPENDED".equals(newStatus)) {
            throw new IllegalArgumentException("Invalid status. Must be ACTIVE, INACTIVE, or SUSPENDED");
        }

        User user = userRepository.findByUsername(username);
        user.setStatus(newStatus);
        userRepository.save(user);

        if ("SUSPENDED".equals(newStatus)) {
            emailService.sendEmail(user.getEmail(), "Account Suspended",
                "Your account has been suspended.");
        }
    }
}</code></pre><p>In this example, hardcoded strings like <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">"ADMIN", "USER", "ACTIVE", "SUSPENDED"</mark></em> are scattered throughout the code. This makes the code hard to maintain and prone to typos.</p><h2>The Refactoring: Replace Magic String with Named Constant</h2><p>The Replace Magic String with Named Constant refactoring technique replaces literal strings with descriptive constants that clearly communicate their purpose and meaning. This makes the code more maintainable and easier to configure.</p><h4>Step by Step Refactoring Process:</h4><ol><li><p><strong>Identify hardcoded strings</strong> in the code that lack clear meaning.</p></li><li><p><strong>Determine the purpose</strong> of each hardcoded string (configuration, business rule, etc.).</p></li><li><p><strong>Create descriptive constants</strong> with meaningful names.</p></li><li><p><strong>Replace hardcoded strings</strong> with the named constants.</p></li><li><p><strong>Group related constants</strong> in appropriate classes or enums.</p></li></ol><h4>Here's the refactored version:</h4><p>The scattered hardcoded strings have been organized into meaningful constants and enums:</p><ol><li><p><a href="#user-service-refactored-with-named-">UserService</a> - Refactored with named constants</p></li><li><p><a href="#user-role-enum-for-typesafe-roles">UserRole</a> - Enum for typesafe roles</p></li><li><p><a href="#user-status-enum-for-typesafe-statu">UserStatus</a> - Enum for typesafe statuses</p></li></ol><h3>UserService - Refactored with Named Constants</h3><pre><code>public class UserService {

    private static final String ERROR_INVALID_ROLE = "Invalid role. Must be ADMIN, USER, or MODERATOR";

    public void createUser(String username, String email, String role) {
        if (!UserRole.isValid(role)) {
            throw new IllegalArgumentException(ERROR_INVALID_ROLE);
        }

        User user = new User();
        user.setUsername(username);
        user.setEmail(email);
        user.setRole(role);
        user.setStatus(UserStatus.ACTIVE.getValue());

        userRepository.save(user);
        emailService.sendEmail(email, "Welcome to Our Platform",
            "Welcome " + username + "! Your account has been created.");
    }

    public void updateUserStatus(String username, String newStatus) {
        UserStatus status = UserStatus.fromString(newStatus);

        User user = userRepository.findByUsername(username);
        user.setStatus(status.getValue());
        userRepository.save(user);

        if (status == UserStatus.SUSPENDED) {
            emailService.sendEmail(user.getEmail(), "Account Suspended",
                "Your account has been suspended.");
        }
    }
}</code></pre><h3>UserRole - Enum for Typesafe Roles</h3><pre><code>public enum UserRole {
    ADMIN, USER, MODERATOR;

    public String getValue() {
        return name();
    }

    public static boolean isValid(String role) {
        for (UserRole r : values()) {
            if (r.name().equals(role)) return true;
        }
        return false;
    }
}</code></pre><h3>UserStatus - Enum for Typesafe Statuses</h3><pre><code>public enum UserStatus {
    ACTIVE, INACTIVE, SUSPENDED;

    public String getValue() {
        return name();
    }

    public static UserStatus fromString(String status) {
        for (UserStatus s : values()) {
            if (s.name().equals(status)) return s;
        }
        throw new IllegalArgumentException("Invalid status: " + status);
    }
}</code></pre><h3>Usage Example - Putting It All Together</h3><p>Notice how the refactored version transforms scattered hardcoded strings into centralized enums. <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">UserRole.ADMIN</mark></em> replaces every occurrence of <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">"ADMIN"</mark></em>, and <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">UserStatus.ACTIVE</mark></em> replaces every <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">"ACTIVE"</mark></em> string. This eliminates typo risks and makes the code self-documenting.</p><p>When you use <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">UserStatus.SUSPENDED</mark></em> instead of <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">"SUSPENDED"</mark></em>, it's immediately clear what you're working with. Compare this to the original scattered strings where typos like <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">"SUSPENED"</mark></em> could easily slip through and cause hard-to-debug issues.</p><pre><code>public class UserManagementDemo {
    public static void main(String[] args) {
        UserService userService = new UserService();

        // Create a new user with typesafe role
        userService.createUser("johndoe", "[email&nbsp;protected]", UserRole.USER.getValue());

        // Update status using enum
        userService.updateUserStatus("johndoe", UserStatus.SUSPENDED.getValue());
    }
}</code></pre><h4>Expected Output:</h4><pre><code>Email sent to [email&nbsp;protected]: Welcome to Our Platform
Email sent to [email&nbsp;protected]: Account Suspended</code></pre><h2>Benefits of Replace Magic String with Named Constant</h2><p>Benefit</p><p>Description</p><p><em>Improved Maintainability</em></p><p><em>Constants with descriptive names make the code self-documenting and easier to modify without searching through the entire codebase.</em></p><p><em>Reduced Typos</em></p><p><em>Using constants instead of hardcoded strings eliminates the risk of typos and makes the code more reliable.</em></p><p><em>Centralized Configuration</em></p><p><em>String values are centralized, making it easier to change configuration values and maintain consistency.</em></p><h2>When to Apply Replace Magic String with Named Constant Refactoring</h2><ul><li><p>Literal strings that represent configuration values or business rules.</p></li><li><p>Repeated hardcoded strings throughout the codebase.</p></li><li><p>Error messages and user-facing text that should be consistent.</p></li><li><p>Status values, role names, or other enumerated string values.</p></li><li><p>When you want to make string values configurable or easier to maintain.</p></li></ul><p>Apply Replace Magic String with Named Constant refactoring to one set of hardcoded strings in your current project today. Start with the strings that represent the most important business rules or configuration values.</p><h2>Repository &amp; Resources</h2><p><strong>Complete Code Examples:</strong> <a href="https://github.com/shift-elevate/clean-code">Clean Code Repository</a></p><p>Find the complete implementation of Hardcoded Strings refactoring and other clean code techniques in our dedicated repository. Each example includes:</p><ul><li><p>Before and after code comparisons</p></li><li><p>Unit tests demonstrating the improvements</p></li></ul><p><em>Found this helpful? Share it with a colleague who's struggling with Hardcoded Strings. Got questions? We'd love to hear from you at <a href="/cdn-cgi/l/email-protection#0d6e6263796c6e794d7e65646b796861687b6c79682369687b">[email&nbsp;protected]</a></em></p>]]></content:encoded></item><item><title><![CDATA[Proxy Pattern: Control Access to Objects with Smart Intermediaries]]></title><description><![CDATA[Master the Proxy design pattern in Java with step by step implementation, high-resolution image gallery examples, and practical code for controlling access to expensive objects with lazy loading and caching.]]></description><link>https://newsletter.shiftelevate.dev/p/proxy-pattern-control-access-to-objects-with-smart-intermediaries</link><guid isPermaLink="false">https://newsletter.shiftelevate.dev/p/proxy-pattern-control-access-to-objects-with-smart-intermediaries</guid><dc:creator><![CDATA[Shift Elevate]]></dc:creator><pubDate>Fri, 09 Jan 2026 09:19:39 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/ddeec9bf-6f8a-497a-9809-93ce2aca29bb_1200x630.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>The Pain of Loading Expensive Resources</h2><p><strong>Picture this:</strong> You're building a photo gallery application that displays high-resolution images. Your initial approach seems logical: load all images when the gallery opens so users can browse smoothly.</p><p>Then your beta testers start complaining. The app takes forever to load. Users on mobile devices watch their data allowance disappear. Some users experience crashes on older devices. You check the analytics and discover users typically view only a handful of images before leaving, yet you're eagerly loading every single high-resolution image in the entire gallery.</p><p>You try to optimize by reducing image quality, but the product team pushes back: "Our users expect crisp, high-quality photos. That's our competitive advantage." You're stuck between loading everything upfront or compromising on quality.</p><p>The <strong>Proxy</strong> pattern solves this by providing a lightweight stand-in for expensive objects, controlling when and how they're actually loaded.</p><h2>Understanding the Proxy Pattern</h2><p><em>The Proxy pattern provides a surrogate or placeholder for another object to control access to it. The proxy acts as an intermediary, presenting the same interface as the real object while adding functionality like lazy loading, access control, caching, or logging.</em></p><p>Think of it like viewing images on a modern photo sharing app: instead of loading every full-resolution image immediately (which would be slow), the app shows lightweight thumbnails first. Only when you tap an image does it load the full resolution version. The thumbnail is a proxy for the real high-resolution image.</p><p>This pattern promotes <strong>Resource Efficiency</strong>, <strong>Controlled Access</strong>, and <strong>Performance Optimization</strong> while maintaining the same interface as the real object.</p><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xSGh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbb98bc3-9e79-4ca0-94e4-44f4f1a78cda_587x630.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xSGh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbb98bc3-9e79-4ca0-94e4-44f4f1a78cda_587x630.png 424w, https://substackcdn.com/image/fetch/$s_!xSGh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbb98bc3-9e79-4ca0-94e4-44f4f1a78cda_587x630.png 848w, https://substackcdn.com/image/fetch/$s_!xSGh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbb98bc3-9e79-4ca0-94e4-44f4f1a78cda_587x630.png 1272w, https://substackcdn.com/image/fetch/$s_!xSGh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbb98bc3-9e79-4ca0-94e4-44f4f1a78cda_587x630.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xSGh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbb98bc3-9e79-4ca0-94e4-44f4f1a78cda_587x630.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fbb98bc3-9e79-4ca0-94e4-44f4f1a78cda_587x630.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!xSGh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbb98bc3-9e79-4ca0-94e4-44f4f1a78cda_587x630.png 424w, https://substackcdn.com/image/fetch/$s_!xSGh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbb98bc3-9e79-4ca0-94e4-44f4f1a78cda_587x630.png 848w, https://substackcdn.com/image/fetch/$s_!xSGh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbb98bc3-9e79-4ca0-94e4-44f4f1a78cda_587x630.png 1272w, https://substackcdn.com/image/fetch/$s_!xSGh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbb98bc3-9e79-4ca0-94e4-44f4f1a78cda_587x630.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><p>Proxy Pattern Components</p><h3>Core Components</h3><ul><li><p><strong>Subject Interface</strong>: Defines the common interface for both RealSubject and Proxy.</p></li><li><p><strong>RealSubject</strong>: The real object that the proxy represents <em>(expensive to create/access)</em>.</p></li><li><p><strong>Proxy</strong>: Maintains a reference to RealSubject and controls access to it.</p></li></ul><h3>Subject Interface</h3><pre><code>public interface Image {
    void display();
    void loadFullResolution();
    ImageMetadata getMetadata();
}</code></pre><h3>Supporting Classes</h3><pre><code>public class ImageMetadata {
    private String filename;
    private String format;
    private long fileSize;

    public ImageMetadata(String filename, String format, long fileSize) {
        this.filename = filename;
        this.format = format;
        this.fileSize = fileSize;
    }

    public String getFilename() { return filename; }
    public String getFormat() { return format; }
    public long getFileSize() { return fileSize; }

    @Override
    public String toString() {
        return String.format("%s [%s] - %.2f MB",
                filename, format, fileSize / (1024.0 * 1024.0));
    }
}

public class ThumbnailImage {
    private String filename;
    private byte[] thumbnailData;

    public ThumbnailImage(String filename, ImageMetadata metadata) {
        this.filename = filename;
        loadThumbnail();
    }

    private void loadThumbnail() {
        System.out.println("Loading thumbnail for: " + filename);
        this.thumbnailData = new byte[102400];
        System.out.println("Thumbnail loaded: " + filename + " (100 KB)");
    }

    public void display() {
        System.out.println("Displaying thumbnail: " + filename);
    }
}</code></pre><h3>RealSubject</h3><pre><code>public class HighResolutionImage implements Image {
    private byte[] imageData;
    private ImageMetadata metadata;
    private boolean isLoaded;

    public HighResolutionImage(ImageMetadata metadata) {
        this.metadata = metadata;
        this.isLoaded = false;
        loadFromDisk();
    }

    private void loadFromDisk() {
        System.out.println("Loading high-resolution image: " + metadata.getFilename());
        long startTime = System.currentTimeMillis();

        try {
            Thread.sleep(2000);
            this.imageData = new byte[(int) metadata.getFileSize()];
            this.isLoaded = true;

            long loadTime = System.currentTimeMillis() - startTime;
            System.out.println(String.format("High-res image loaded: %s (%.2f MB) in %d ms",
                    metadata.getFilename(), metadata.getFileSize() / (1024.0 * 1024.0), loadTime));
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

    @Override
    public void display() {
        if (isLoaded) {
            System.out.println("Displaying high-resolution image: " + metadata.getFilename());
        } else {
            System.out.println("Image not loaded yet: " + metadata.getFilename());
        }
    }

    @Override
    public void loadFullResolution() {
        if (!isLoaded) {
            loadFromDisk();
        }
    }

    @Override
    public ImageMetadata getMetadata() {
        return metadata;
    }
}</code></pre><h3>Proxy</h3><pre><code>public class ImageProxy implements Image {
    private HighResolutionImage realImage;
    private ThumbnailImage thumbnail;
    private ImageMetadata metadata;
    private boolean isFullResolutionLoaded;

    public ImageProxy(ImageMetadata metadata) {
        this.metadata = metadata;
        this.isFullResolutionLoaded = false;
        this.thumbnail = new ThumbnailImage(metadata.getFilename(), metadata);
    }

    @Override
    public void display() {
        if (!isFullResolutionLoaded) {
            System.out.println("Displaying thumbnail preview for: " + metadata.getFilename());
            thumbnail.display();
        } else {
            realImage.display();
        }
    }

    @Override
    public void loadFullResolution() {
        if (realImage == null) {
            System.out.println("User requested full resolution for: " + metadata.getFilename());
            realImage = new HighResolutionImage(metadata);
            isFullResolutionLoaded = true;
        } else {
            System.out.println("Full resolution already loaded for: " + metadata.getFilename());
        }
    }

    @Override
    public ImageMetadata getMetadata() {
        return metadata;
    }

    public boolean isFullResolutionLoaded() {
        return isFullResolutionLoaded;
    }
}</code></pre><h3>Client</h3><pre><code>public class PhotoGallery {
    private List&lt;Image&gt; images;
    private String galleryName;

    public PhotoGallery(String galleryName) {
        this.galleryName = galleryName;
        this.images = new ArrayList&lt;&gt;();
    }

    public void addImage(Image image) {
        images.add(image);
    }

    public void displayGallery() {
        System.out.println("\n=== " + galleryName + " ===");
        for (int i = 0; i &lt; images.size(); i++) {
            System.out.println("Image " + (i + 1) + ":");
            images.get(i).display();
        }
    }

    public void viewImage(int index) {
        System.out.println("\n=== Viewing Image " + (index + 1) + " ===");
        Image image = images.get(index);
        System.out.println("Metadata: " + image.getMetadata().toString());
        image.loadFullResolution();
        image.display();
    }

    public void showStatistics() {
        int loadedCount = 0;
        for (Image image : images) {
            if (image instanceof ImageProxy &amp;&amp; ((ImageProxy) image).isFullResolutionLoaded()) {
                loadedCount++;
            }
        }
        System.out.println("\n=== Statistics ===");
        System.out.println("Total: " + images.size() + " | Loaded: " + loadedCount +
                         " | Memory saved: " + (images.size() - loadedCount) * 100 / images.size() + "%");
    }
}

public class ImageGalleryDemo {
    public static void main(String[] args) {
        System.out.println("=== Photo Gallery with Proxy Pattern ===\n");

        PhotoGallery gallery = new PhotoGallery("Vacation Photos");

        gallery.addImage(new ImageProxy(
                new ImageMetadata("sunset_beach.jpg", "JPEG", 10485760)));
        gallery.addImage(new ImageProxy(
                new ImageMetadata("mountain_peak.jpg", "JPEG", 12582912)));
        gallery.addImage(new ImageProxy(
                new ImageMetadata("city_skyline.jpg", "JPEG", 11534336)));

        System.out.println("\nGallery loaded! Only thumbnails in memory.\n");

        gallery.displayGallery();
        gallery.viewImage(1);
        gallery.viewImage(1);
        gallery.showStatistics();
    }
}</code></pre><h4>Expected Output:</h4><pre><code>=== Photo Gallery with Proxy Pattern ===

Loading thumbnail for: sunset_beach.jpg
Thumbnail loaded: sunset_beach.jpg (100 KB)
Loading thumbnail for: mountain_peak.jpg
Thumbnail loaded: mountain_peak.jpg (100 KB)
Loading thumbnail for: city_skyline.jpg
Thumbnail loaded: city_skyline.jpg (100 KB)

Gallery loaded! Only thumbnails in memory.

=== Vacation Photos ===
Image 1:
Displaying thumbnail preview for: sunset_beach.jpg
Displaying thumbnail: sunset_beach.jpg
Image 2:
Displaying thumbnail preview for: mountain_peak.jpg
Displaying thumbnail: mountain_peak.jpg
Image 3:
Displaying thumbnail preview for: city_skyline.jpg
Displaying thumbnail: city_skyline.jpg

=== Viewing Image 2 ===
Metadata: mountain_peak.jpg [JPEG] - 12.00 MB
User requested full resolution for: mountain_peak.jpg
Loading high-resolution image: mountain_peak.jpg
High-res image loaded: mountain_peak.jpg (12.00 MB) in 2001 ms
Displaying high-resolution image: mountain_peak.jpg

=== Viewing Image 2 ===
Metadata: mountain_peak.jpg [JPEG] - 12.00 MB
Full resolution already loaded for: mountain_peak.jpg
Displaying high-resolution image: mountain_peak.jpg

=== Statistics ===
Total: 3 | Loaded: 1 | Memory saved: 66%</code></pre><h3>&#128640; Get the Complete Implementation</h3><p>The full code with advanced proxy implementations is available in our <a href="https://github.com/shift-elevate/design-patterns">Design Patterns Repository</a>.</p><pre><code># Clone and run the complete demo
git clone https://github.com/shift-elevate/design-patterns.git
cd design-patterns
mvn test -Dtest=ProxyPatternTest</code></pre><h2>Real World Examples</h2><p>The Proxy pattern is extensively used in real world applications:</p><h3>1. Database Lazy Loading</h3><p>Object-Relational Mapping frameworks use proxy objects for lazy loading of database entities. When you fetch a parent entity, related child entities are represented by proxy objects that only load from the database when actually accessed, avoiding the N+1 query problem and improving performance.</p><h3>2. Aspect-Oriented Programming</h3><p>Modern application frameworks use dynamic proxies to add cross-cutting concerns like transaction management, security, and logging to business objects. The proxy intercepts method calls and adds behavior before or after the actual method execution without modifying the original code.</p><h3>3. Content Delivery Networks</h3><p>Content delivery networks act as caching proxies for web content, storing copies of images, videos, and static files closer to users geographically. When a user requests content, the proxy serves the cached version if available, only fetching from the origin server when necessary, dramatically reducing latency and bandwidth costs.</p><h2>When to Use the Proxy Pattern</h2><p>Understanding when to apply the Proxy pattern is crucial for making the right architectural decisions. Here's when it shines and when alternatives might be better:</p><h3>&#9989;<strong> Ideal Scenarios:</strong></h3><ul><li><p>You need to delay expensive object creation until actually needed.</p></li><li><p>You want to control access to an object based on permissions.</p></li><li><p>You need to represent remote objects locally.</p></li><li><p>You want to cache results of expensive operations.</p></li><li><p>You need to add reference counting or resource management.</p></li></ul><h3>&#10060;<strong> Skip It When:</strong></h3><ul><li><p>The object being proxied is already lightweight and fast to create.</p></li><li><p>The added indirection and complexity outweigh the benefits.</p></li><li><p>You need direct access to the object's internal state.</p></li><li><p>The proxy layer would add unnecessary performance overhead for your use case.</p></li></ul><h2>Next Steps: Apply Proxy Pattern in Your Project</h2><p>Ready to implement the Proxy pattern in your own projects? Here's a structured approach to get you started:</p><ol><li><p><strong>Identify Expensive Operations</strong>: Look for areas where object creation, resource loading, or method calls are costly in terms of time, memory, or network bandwidth.</p></li><li><p><strong>Define Common Interface</strong>: Create an interface that both the real object and proxy will implement.</p></li><li><p><strong>Implement Real Subject</strong>: Build the actual implementation with the expensive or protected functionality.</p></li><li><p><strong>Create Proxy Class</strong>: Implement a proxy that controls access to the real subject.</p></li><li><p><strong>Add Proxy Logic</strong>: Implement lazy loading, caching, access control, or logging as needed.</p></li><li><p><strong>Test Performance Impact</strong>: Measure the actual benefits in terms of load time, memory usage, and user experience.</p></li></ol><p>The Proxy pattern transforms resource-intensive applications into efficient, responsive systems. By controlling when and how expensive objects are created and accessed, you build applications that start faster, use less memory, and provide better user experiences while maintaining clean, maintainable code.</p><p><em>Found this helpful? Share it with a colleague who's struggling with slow-loading resources or memory-intensive applications. Got questions? We'd love to hear from you at <a href="/cdn-cgi/l/email-protection#55363a3b2134362115263d3c3321303930233421307b313023">[email&nbsp;protected]</a></em></p>]]></content:encoded></item><item><title><![CDATA[Strategy Pattern: Encapsulate Algorithms and Make Them Interchangeable]]></title><description><![CDATA[Master the Strategy design pattern with step by step implementation, GPS navigation routing examples, and practical code for encapsulating algorithms and making them interchangeable at runtime.]]></description><link>https://newsletter.shiftelevate.dev/p/strategy-pattern-encapsulate-algorithms-and-make-them-interchangeable</link><guid isPermaLink="false">https://newsletter.shiftelevate.dev/p/strategy-pattern-encapsulate-algorithms-and-make-them-interchangeable</guid><dc:creator><![CDATA[Shift Elevate]]></dc:creator><pubDate>Tue, 30 Dec 2025 17:14:28 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/dba2d258-740f-426e-99c9-e115b881937f_1200x630.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>The Pain of Hardcoded Algorithm Selection</h2><p><strong>Picture this:</strong> You're building a navigation system for a GPS application that needs to calculate routes using different strategies: fastest route using highways, shortest route by distance, and eco-friendly route for fuel efficiency. Your initial approach seems logical: use conditional logic to select the appropriate calculation method.</p><p>Then business requirements evolve. The product team wants to add bicycle routes, avoid tolls, prefer scenic roads, and incorporate real-time traffic patterns. Your route calculation method becomes a massive switch statement that violates the <strong>Open Closed Principle</strong> and breaks every time you add a new routing strategy.</p><p>Suddenly, you're dealing with code like this:</p><pre><code>public Route calculateRoute(Location start, Location end, String routeType) {
    if (routeType.equals("FASTEST")) {
        return calculateFastestRoute(start, end);
    } else if (routeType.equals("SHORTEST")) {
        return calculateShortestRoute(start, end);
    } else if (routeType.equals("ECO")) {
        return calculateEcoRoute(start, end);
    } else if (routeType.equals("SCENIC")) {
        // Scenic route logic
    } else if (routeType.equals("BICYCLE")) {
        // Bicycle-friendly route logic
    }
    // ... endless conditions
}</code></pre><p>Sound familiar? The <strong>Strategy</strong> pattern solves this challenge by encapsulating algorithms in separate strategy objects, making them interchangeable at runtime.</p><h2>Understanding the Strategy Pattern</h2><p><em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">The Strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. It lets the algorithm vary independently from clients that use it, enabling runtime algorithm selection.</mark></em></p><p>Think of it like choosing different navigation modes on Google Maps: whether you select the fastest route, avoid highways, or optimize for fuel efficiency, the navigation system remains the same. Each routing method is a different strategy that implements the same interface, but with different internal logic.</p><p>This pattern promotes <strong>Algorithm Flexibility</strong>, <strong>Open Closed Principle</strong>, and <strong>Single Responsibility</strong> while enabling dynamic behaviour selection at runtime.</p><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Kczs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb3fed62-b426-4a47-8f17-9f6125d5ffae_587x480.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Kczs!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb3fed62-b426-4a47-8f17-9f6125d5ffae_587x480.png 424w, https://substackcdn.com/image/fetch/$s_!Kczs!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb3fed62-b426-4a47-8f17-9f6125d5ffae_587x480.png 848w, https://substackcdn.com/image/fetch/$s_!Kczs!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb3fed62-b426-4a47-8f17-9f6125d5ffae_587x480.png 1272w, https://substackcdn.com/image/fetch/$s_!Kczs!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb3fed62-b426-4a47-8f17-9f6125d5ffae_587x480.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Kczs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb3fed62-b426-4a47-8f17-9f6125d5ffae_587x480.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fb3fed62-b426-4a47-8f17-9f6125d5ffae_587x480.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!Kczs!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb3fed62-b426-4a47-8f17-9f6125d5ffae_587x480.png 424w, https://substackcdn.com/image/fetch/$s_!Kczs!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb3fed62-b426-4a47-8f17-9f6125d5ffae_587x480.png 848w, https://substackcdn.com/image/fetch/$s_!Kczs!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb3fed62-b426-4a47-8f17-9f6125d5ffae_587x480.png 1272w, https://substackcdn.com/image/fetch/$s_!Kczs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb3fed62-b426-4a47-8f17-9f6125d5ffae_587x480.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><p>Strategy Pattern Components</p><h3>Core Components</h3><ul><li><p><strong><a href="#strategy-interface">Strategy Interface</a></strong>: Defines the common interface for all concrete strategies</p></li><li><p><strong><a href="#supporting-classes">Supporting Classes</a></strong>: Helper classes used by strategies (Location, Route)</p></li><li><p><strong><a href="#concrete-strategies">Concrete Strategies</a></strong>: Implement specific algorithms using the strategy interface</p></li><li><p><strong><a href="#context-class">Context Class</a></strong>: Uses a strategy to execute algorithms and can switch strategies at runtime</p></li></ul><h2>Complete Java Implementation</h2><p>Let's build a navigation route planning system that demonstrates the Strategy pattern's power in managing different routing algorithms.</p><h3>Strategy Interface</h3><pre><code>public interface RouteStrategy {
    Route calculateRoute(Location start, Location destination);
    String getStrategyName();
}</code></pre><h3>Supporting Classes</h3><pre><code>public class Location {
    private String name;
    private double latitude;
    private double longitude;

    public Location(String name, double latitude, double longitude) {
        this.name = name;
        this.latitude = latitude;
        this.longitude = longitude;
    }

    public String getName() { return name; }
    public double getLatitude() { return latitude; }
    public double getLongitude() { return longitude; }

    @Override
    public String toString() {
        return name;
    }
}

public class Route {
    private Location start;
    private Location destination;
    private double distance;
    private int estimatedTime;
    private double fuelCost;
    private String description;

    public Route(Location start, Location destination, double distance,
                int estimatedTime, double fuelCost, String description) {
        this.start = start;
        this.destination = destination;
        this.distance = distance;
        this.estimatedTime = estimatedTime;
        this.fuelCost = fuelCost;
        this.description = description;
    }

    public Location getStart() { return start; }
    public Location getDestination() { return destination; }
    public double getDistance() { return distance; }
    public int getEstimatedTime() { return estimatedTime; }
    public double getFuelCost() { return fuelCost; }
    public String getDescription() { return description; }

    @Override
    public String toString() {
        return String.format("Route: %s &#8594; %s\n" +
                           "  %s\n" +
                           "  Distance: %.1f km\n" +
                           "  Time: %d minutes\n" +
                           "  Fuel Cost: $%.2f",
                start.getName(), destination.getName(),
                description, distance, estimatedTime, fuelCost);
    }
}</code></pre><h3>Concrete Strategies</h3><pre><code>public class FastestRouteStrategy implements RouteStrategy {

    @Override
    public Route calculateRoute(Location start, Location destination) {
        double distance = calculateDistance(start, destination) * 1.15;
        int estimatedTime = 32;
        double fuelCost = 6.50;

        String description = "Via Highway 101 (fastest route with minimal traffic)";

        return new Route(start, destination, distance, estimatedTime, fuelCost, description);
    }

    @Override
    public String getStrategyName() {
        return "Fastest Route (via highways)";
    }

    private double calculateDistance(Location start, Location destination) {
        double latDiff = destination.getLatitude() - start.getLatitude();
        double lonDiff = destination.getLongitude() - start.getLongitude();
        return Math.sqrt(latDiff * latDiff + lonDiff * lonDiff) * 111;
    }
}

public class ShortestRouteStrategy implements RouteStrategy {

    @Override
    public Route calculateRoute(Location start, Location destination) {
        double distance = calculateDistance(start, destination);
        int estimatedTime = 55;
        double fuelCost = 5.80;

        String description = "Via City Center (shortest distance route)";

        return new Route(start, destination, distance, estimatedTime, fuelCost, description);
    }

    @Override
    public String getStrategyName() {
        return "Shortest Route (via city center)";
    }

    private double calculateDistance(Location start, Location destination) {
        double latDiff = destination.getLatitude() - start.getLatitude();
        double lonDiff = destination.getLongitude() - start.getLongitude();
        return Math.sqrt(latDiff * latDiff + lonDiff * lonDiff) * 111;
    }
}

public class EcoFriendlyRouteStrategy implements RouteStrategy {

    @Override
    public Route calculateRoute(Location start, Location destination) {
        double distance = calculateDistance(start, destination) * 1.05;
        int estimatedTime = 42;
        double fuelCost = 4.90;

        String description = "Via Suburban Roads (optimized for fuel efficiency)";

        return new Route(start, destination, distance, estimatedTime, fuelCost, description);
    }

    @Override
    public String getStrategyName() {
        return "Eco-Friendly Route (fuel optimized)";
    }

    private double calculateDistance(Location start, Location destination) {
        double latDiff = destination.getLatitude() - start.getLatitude();
        double lonDiff = destination.getLongitude() - start.getLongitude();
        return Math.sqrt(latDiff * latDiff + lonDiff * lonDiff) * 111;
    }
}</code></pre><h3>Context Class</h3><pre><code>public class NavigationSystem {
    private RouteStrategy routeStrategy;
    private Location currentLocation;

    public NavigationSystem() {
        // Default strategy
        this.routeStrategy = new FastestRouteStrategy();
    }

    public void setRouteStrategy(RouteStrategy strategy) {
        this.routeStrategy = strategy;
        System.out.println("Route strategy changed to: " + strategy.getStrategyName());
    }

    public void setCurrentLocation(Location location) {
        this.currentLocation = location;
    }

    public Route planRoute(Location destination) {
        if (currentLocation == null) {
            throw new IllegalStateException("Current location not set");
        }

        System.out.println("\n=== Planning Route ===");
        System.out.println("Strategy: " + routeStrategy.getStrategyName());

        Route route = routeStrategy.calculateRoute(currentLocation, destination);

        return route;
    }

    public void displayRouteOptions(Location destination) {
        System.out.println("\n=== Available Route Options ===");
        System.out.println("From: " + currentLocation.getName());
        System.out.println("To: " + destination.getName());
        System.out.println();

        RouteStrategy[] strategies = {
            new FastestRouteStrategy(),
            new ShortestRouteStrategy(),
            new EcoFriendlyRouteStrategy()
        };

        for (RouteStrategy strategy : strategies) {
            Route route = strategy.calculateRoute(currentLocation, destination);
            System.out.println(route);
            System.out.println();
        }
    }
}</code></pre><h3>Client</h3><pre><code>public class NavigationDemo {
    public static void main(String[] args) {
        System.out.println("=== GPS Navigation System Demo ===\n");

        // Create locations
        Location home = new Location("Home", 37.7749, -122.4194);
        Location office = new Location("Downtown Office", 37.8044, -122.2712);

        // Create navigation system
        NavigationSystem navigation = new NavigationSystem();
        navigation.setCurrentLocation(home);

        // Display all available route options
        navigation.displayRouteOptions(office);

        System.out.println("=".repeat(60));

        // Plan route using fastest strategy (default)
        System.out.println("\n1. Using Fastest Route Strategy:");
        Route fastestRoute = navigation.planRoute(office);
        System.out.println(fastestRoute);

        // Change to shortest distance strategy
        System.out.println("\n2. Using Shortest Route Strategy:");
        navigation.setRouteStrategy(new ShortestRouteStrategy());
        Route shortestRoute = navigation.planRoute(office);
        System.out.println(shortestRoute);

        // Change to eco-friendly strategy
        System.out.println("\n3. Using Eco-Friendly Route Strategy:");
        navigation.setRouteStrategy(new EcoFriendlyRouteStrategy());
        Route ecoRoute = navigation.planRoute(office);
        System.out.println(ecoRoute);
    }
}</code></pre><h4>Expected Output:</h4><pre><code>=== GPS Navigation System Demo ===

=== Available Route Options ===
From: Home
To: Downtown Office

Route: Home &#8594; Downtown Office
  Via Highway 101 (fastest route with minimal traffic)
  Distance: 19.3 km
  Time: 32 minutes
  Fuel Cost: $6.50

Route: Home &#8594; Downtown Office
  Via City Center (shortest distance route)
  Distance: 16.8 km
  Time: 55 minutes
  Fuel Cost: $5.80

Route: Home &#8594; Downtown Office
  Via Suburban Roads (optimized for fuel efficiency)
  Distance: 17.6 km
  Time: 42 minutes
  Fuel Cost: $4.90

============================================================

1. Using Fastest Route Strategy:

=== Planning Route ===
Strategy: Fastest Route (via highways)
Route: Home &#8594; Downtown Office
  Via Highway 101 (fastest route with minimal traffic)
  Distance: 19.3 km
  Time: 32 minutes
  Fuel Cost: $6.50

2. Using Shortest Route Strategy:
Route strategy changed to: Shortest Route (via city center)

=== Planning Route ===
Strategy: Shortest Route (via city center)
Route: Home &#8594; Downtown Office
  Via City Center (shortest distance route)
  Distance: 16.8 km
  Time: 55 minutes
  Fuel Cost: $5.80

3. Using Eco-Friendly Route Strategy:
Route strategy changed to: Eco-Friendly Route (fuel optimized)

=== Planning Route ===
Strategy: Eco-Friendly Route (fuel optimized)
Route: Home &#8594; Downtown Office
  Via Suburban Roads (optimized for fuel efficiency)
  Distance: 17.6 km
  Time: 42 minutes
  Fuel Cost: $4.90</code></pre><h3>&#128640; Get the Complete Implementation</h3><p>The full code with advanced strategy selection and dynamic routing is available in our <a href="https://github.com/shift-elevate/design-patterns">Design Patterns Repository</a>.</p><pre><code># Clone and run the complete demo
git clone https://github.com/shift-elevate/design-patterns.git
cd design-patterns
mvn test -Dtest=StrategyPatternTest</code></pre><h2>Real World Examples</h2><p>The Strategy pattern is extensively used in real-world applications:</p><h3>1. Payment Processing Systems</h3><p>Modern payment gateways use Strategy patterns to handle different payment methods <em>(credit cards, digital wallets, bank transfers)</em>, with each method implementing the same payment interface but with different processing logic.</p><h3>2. Data Compression Libraries</h3><p>Compression libraries use Strategy patterns to support different compression algorithms <em>(ZIP, GZIP, BZIP2)</em>, allowing users to select the appropriate algorithm based on speed vs. compression ratio requirements.</p><h3>3. Machine Learning Frameworks</h3><p>ML frameworks use Strategy patterns for different algorithms <em>(linear regression, decision trees, neural networks)</em>, enabling easy algorithm swapping and comparison without changing the training infrastructure.</p><h2>When to Use Strategy Pattern</h2><p>Understanding when to apply the Strategy pattern is crucial for making the right architectural decisions. Here's when it shines and when alternatives might be better:</p><h3>&#9989; Ideal Scenarios:</h3><ul><li><p>You have multiple ways to perform a task and want to choose at runtime.</p></li><li><p>You want to avoid large conditional statements for algorithm selection.</p></li><li><p>You need to add new algorithms without modifying existing code.</p></li><li><p>Different algorithms have similar interfaces but different implementations.</p></li></ul><h3>&#10060; Skip It When:</h3><ul><li><p>You only have one or two simple algorithms that rarely change.</p></li><li><p>The algorithms are tightly coupled to the context and can't be easily separated.</p></li><li><p>Performance overhead of strategy objects is unacceptable.</p></li><li><p>The algorithm selection logic is simple and stable.</p></li></ul><h2>Next Steps: Apply Strategy Pattern in Your Project</h2><p>Ready to implement the Strategy pattern in your own projects? Here's a structured approach to get you started:</p><ol><li><p><strong>Identify Algorithm Variations</strong>: Look for areas where you have multiple ways to accomplish the same task (routing, pricing, sorting, validation).</p></li><li><p><strong>Define Strategy Interface</strong>: Create a common interface that all algorithm variations can implement.</p></li><li><p><strong>Implement Concrete Strategies</strong>: Build separate classes for each algorithm variation.</p></li><li><p><strong>Create Context Class</strong>: Build a class that uses strategies and can switch between them.</p></li><li><p><strong>Add Strategy Selection Logic</strong>: Implement logic to choose appropriate strategies based on conditions.</p></li></ol><p>The Strategy pattern transforms rigid algorithmic code into flexible, extensible systems. By encapsulating algorithms in separate strategy objects, you build systems that can adapt to changing requirements while maintaining clean, testable code.</p><p><em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">Found this helpful? Share it with a colleague who's struggling with complex algorithm selection logic. Got questions? We'd love to hear from you at </mark><a href="/cdn-cgi/l/email-protection#c2a1adacb6a3a1b682b1aaaba4b6a7aea7b4a3b6a7eca6a7b4"><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">[email&nbsp;protected]</mark></a></em></p>]]></content:encoded></item><item><title><![CDATA[Data Clumps: Extract Class Refactoring | Clean Code]]></title><description><![CDATA[Refactor the Data Clumps code smell with Extract Class technique using healthcare patient records example, reduce parameter lists, and improve code organization.]]></description><link>https://newsletter.shiftelevate.dev/p/data-clumps-extract-class-refactoring-clean-code</link><guid isPermaLink="false">https://newsletter.shiftelevate.dev/p/data-clumps-extract-class-refactoring-clean-code</guid><dc:creator><![CDATA[Shift Elevate]]></dc:creator><pubDate>Fri, 26 Dec 2025 17:32:17 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/6165252d-4fcf-4452-ba11-1b78568fc3ab_1200x630.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Data Clumps are groups of data that always appear together but are not organized into a single object.</em></p><p>We will see how we can refactor using the Extract Class technique to group related data and reduce parameter lists.</p><h3>Clean Code Reference</h3><p>&#10077;</p><p><strong>&#9888;&#65039; Code Smell:</strong><span data-color="rgb(204, 204, 204)" style="color: rgb(204, 204, 204);"> </span><em>Data Clumps</em><br>&#9989;<strong> Refactoring:</strong><span data-color="rgb(204, 204, 204)" style="color: rgb(204, 204, 204);"> </span><em>Extract Class</em><br>&#127919;<strong> Goal:</strong><span data-color="rgb(204, 204, 204)" style="color: rgb(204, 204, 204);"> </span><em>Group related data and reduce parameter coupling</em></p><p>The Data Clumps code smell occurs when the same group of data items appears together in multiple places throughout the code, but they are not organized into a single object. The <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">Extract Class</mark></em> refactoring technique groups these related data items into a meaningful object, reducing parameter lists and improving code organization.</p><h2>The Code Smell: Data Clumps</h2><p>Data Clumps are groups of data that always appear together but are scattered across multiple parameters, method signatures, and classes. This creates tight coupling between methods and makes the code harder to maintain. When you need to add or modify related data, you have to change multiple places in the code.</p><p>Symptoms</p><p>Impact</p><p><em>Same group of parameters in multiple methods</em></p><p><em>Tight coupling</em></p><p><em>Related data scattered across code</em></p><p><em>Difficult to maintain</em></p><p><em>Long parameter lists</em></p><p><em>Poor code organization</em></p><h4>Here's a typical example of Data Clumps:</h4><pre><code>public class PatientManager {

    public void registerPatient(String firstName, String lastName,
                               String phone, String email,
                               String insuranceProvider, String policyNumber) {
        Patient patient = new Patient();
        patient.setFirstName(firstName);
        patient.setLastName(lastName);
        patient.setPhone(phone);
        patient.setEmail(email);
        patient.setInsuranceProvider(insuranceProvider);
        patient.setPolicyNumber(policyNumber);

        savePatient(patient);
    }

    public void updateContactInfo(String firstName, String lastName,
                                 String newPhone, String newEmail) {
        Patient patient = findPatient(firstName, lastName);
        patient.setPhone(newPhone);
        patient.setEmail(newEmail);

        savePatient(patient);
    }

    public void scheduleAppointment(String firstName, String lastName,
                                   String phone, String email,
                                   String appointmentDate, String appointmentType) {
        String message = String.format("Appointment scheduled for %s %s on %s for %s",
                firstName, lastName, appointmentDate, appointmentType);

        sendEmailConfirmation(email, "Appointment Confirmation", message);
        sendSmsConfirmation(phone, message);
    }

    private Patient findPatient(String firstName, String lastName) {
        return new Patient();
    }

    private void savePatient(Patient patient) { }

    private void sendEmailConfirmation(String email, String subject, String message) { }

    private void sendSmsConfirmation(String phone, String message) { }
}

public class Patient {
    private String firstName;
    private String lastName;
    private String phone;
    private String email;
    private String insuranceProvider;
    private String policyNumber;

    public String getFirstName() { return firstName; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
    public String getLastName() { return lastName; }
    public void setLastName(String lastName) { this.lastName = lastName; }
    public String getPhone() { return phone; }
    public void setPhone(String phone) { this.phone = phone; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
    public String getInsuranceProvider() { return insuranceProvider; }
    public void setInsuranceProvider(String insuranceProvider) { this.insuranceProvider = insuranceProvider; }
    public String getPolicyNumber() { return policyNumber; }
    public void setPolicyNumber(String policyNumber) { this.policyNumber = policyNumber; }
}</code></pre><p>In this example, we can see data clumps everywhere: patient demographics <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">(firstName, lastName)</mark></em>, contact information <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">(phone, email)</mark></em>, insurance details <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">(provider, policyNumber)</mark></em>, and appointment information <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">(appointmentDate, appointmentType)</mark> </em>appear together in multiple methods. This creates long parameter lists and makes the code hard to maintain.</p><h2>The Refactoring: Extract Class</h2><p>The Extract Class refactoring technique groups related data items into meaningful objects, reducing parameter lists and improving code organization. This makes the code more maintainable and reduces coupling between methods.</p><p>By creating dedicated classes for patient demographics, contact information, insurance details, and appointments, we transform the scattered primitive parameters into cohesive objects. Each extracted class encapsulates related data and provides meaningful methods, making the code more organized and easier to understand.</p><h4>Step by Step Refactoring Process:</h4><ol><li><p><strong>Identify data clumps</strong> that appear together in multiple places.</p></li><li><p><strong>Create new classes</strong> to represent these data groups.</p></li><li><p><strong>Move related data</strong> into the new classes.</p></li><li><p><strong>Update method signatures</strong> to use the new objects.</p></li><li><p><strong>Move related behaviour</strong> into the new classes.</p></li></ol><h4>Here's the refactored version:</h4><p>The scattered data clumps have been organized into meaningful classes:</p><ol><li><p><a href="#patient-manager-simplified-service-">PatientManager</a> - Simplified service class</p></li><li><p><a href="#patient-demographics-extracted-demo">PatientDemographics</a> - Extracted demographic data</p></li><li><p><a href="#contact-information-extracted-conta">ContactInformation</a> - Extracted contact details</p></li><li><p><a href="#insurance-details-extracted-insuran">InsuranceDetails</a> - Extracted insurance data</p></li><li><p><a href="#appointment-extracted-appointment-i">Appointment</a> - Extracted appointment information</p></li><li><p><a href="#patient-coordinator-class">Patient</a> - Coordinator class</p></li></ol><h3>PatientManager - Simplified Service Class</h3><pre><code>public class PatientManager {

    public void registerPatient(PatientDemographics demographics,
                               ContactInformation contactInfo,
                               InsuranceDetails insurance) {
        Patient patient = new Patient(demographics, contactInfo, insurance);
        savePatient(patient);
    }

    public void updateContactInfo(PatientDemographics demographics,
                                 ContactInformation newContactInfo) {
        Patient patient = findPatient(demographics);
        patient.updateContactInfo(newContactInfo);
        savePatient(patient);
    }

    public void scheduleAppointment(PatientDemographics demographics,
                                   ContactInformation contactInfo,
                                   Appointment appointment) {
        String message = String.format("Appointment scheduled for %s on %s for %s",
                demographics.getFullName(), appointment.getDate(), appointment.getType());

        contactInfo.sendEmailConfirmation("Appointment Confirmation", message);
        contactInfo.sendSmsConfirmation(message);
    }

    private Patient findPatient(PatientDemographics demographics) {
        return new Patient();
    }

    private void savePatient(Patient patient) { }
}</code></pre><h3>PatientDemographics - Extracted Demographic Data</h3><pre><code>public class PatientDemographics {
    private final String firstName;
    private final String lastName;

    public PatientDemographics(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public String getFullName() {
        return firstName + " " + lastName;
    }

    public String getFirstName() { return firstName; }
    public String getLastName() { return lastName; }

    @Override
    public String toString() {
        return String.format("Patient{name='%s %s'}", firstName, lastName);
    }
}
</code></pre><h3>ContactInformation - Extracted Contact Details</h3><pre><code>public class ContactInformation {
    private final String phone;
    private final String email;

    public ContactInformation(String phone, String email) {
        this.phone = phone;
        this.email = email;
    }

    public void sendEmailConfirmation(String subject, String message) {
        System.out.println("Email sent to " + email + ": " + subject);
    }

    public void sendSmsConfirmation(String message) {
        System.out.println("SMS sent to " + phone + ": " + message);
    }

    public String getPhone() { return phone; }
    public String getEmail() { return email; }

    @Override
    public String toString() {
        return String.format("Contact{phone='%s', email='%s'}", phone, email);
    }
}public class ContactInformation {
    private final String phone;
    private final String email;

    public ContactInformation(String phone, String email) {
        this.phone = phone;
        this.email = email;
    }

    public void sendEmailConfirmation(String subject, String message) {
        System.out.println("Email sent to " + email + ": " + subject);
    }

    public void sendSmsConfirmation(String message) {
        System.out.println("SMS sent to " + phone + ": " + message);
    }

    public String getPhone() { return phone; }
    public String getEmail() { return email; }

    @Override
    public String toString() {
        return String.format("Contact{phone='%s', email='%s'}", phone, email);
    }
}</code></pre><h3>InsuranceDetails - Extracted Insurance Data</h3><pre><code>public class InsuranceDetails {
    private final String provider;
    private final String policyNumber;

    public InsuranceDetails(String provider, String policyNumber) {
        this.provider = provider;
        this.policyNumber = policyNumber;
    }

    public String getFormattedCoverage() {
        return String.format("%s (Policy: %s)", provider, policyNumber);
    }

    public String getProvider() { return provider; }
    public String getPolicyNumber() { return policyNumber; }

    @Override
    public String toString() {
        return getFormattedCoverage();
    }
}
public class InsuranceDetails {
    private final String provider;
    private final String policyNumber;

    public InsuranceDetails(String provider, String policyNumber) {
        this.provider = provider;
        this.policyNumber = policyNumber;
    }

    public String getFormattedCoverage() {
        return String.format("%s (Policy: %s)", provider, policyNumber);
    }

    public String getProvider() { return provider; }
    public String getPolicyNumber() { return policyNumber; }

    @Override
    public String toString() {
        return getFormattedCoverage();
    }
}</code></pre><h3>Appointment - Extracted Appointment Information</h3><pre><code>public class Appointment {
    private final String date;
    private final String type;

    public Appointment(String date, String type) {
        this.date = date;
        this.type = type;
    }

    public String getDate() { return date; }
    public String getType() { return type; }

    @Override
    public String toString() {
        return String.format("Appointment{date='%s', type='%s'}", date, type);
    }
}public class Appointment {
    private final String date;
    private final String type;

    public Appointment(String date, String type) {
        this.date = date;
        this.type = type;
    }

    public String getDate() { return date; }
    public String getType() { return type; }

    @Override
    public String toString() {
        return String.format("Appointment{date='%s', type='%s'}", date, type);
    }
}</code></pre><h3>Patient - Coordinator Class</h3><pre><code>public class Patient {
    private final PatientDemographics demographics;
    private ContactInformation contactInfo;
    private final InsuranceDetails insurance;

    public Patient(PatientDemographics demographics,
                  ContactInformation contactInfo,
                  InsuranceDetails insurance) {
        this.demographics = demographics;
        this.contactInfo = contactInfo;
        this.insurance = insurance;
    }

    public void updateContactInfo(ContactInformation newContactInfo) {
        this.contactInfo = newContactInfo;
    }

    public PatientDemographics getDemographics() { return demographics; }
    public ContactInformation getContactInfo() { return contactInfo; }
    public InsuranceDetails getInsurance() { return insurance; }

    @Override
    public String toString() {
        return String.format("Patient{demographics=%s, contact=%s, insurance=%s}",
                demographics, contactInfo, insurance);
    }
}public class Patient {
    private final PatientDemographics demographics;
    private ContactInformation contactInfo;
    private final InsuranceDetails insurance;

    public Patient(PatientDemographics demographics,
                  ContactInformation contactInfo,
                  InsuranceDetails insurance) {
        this.demographics = demographics;
        this.contactInfo = contactInfo;
        this.insurance = insurance;
    }

    public void updateContactInfo(ContactInformation newContactInfo) {
        this.contactInfo = newContactInfo;
    }

    public PatientDemographics getDemographics() { return demographics; }
    public ContactInformation getContactInfo() { return contactInfo; }
    public InsuranceDetails getInsurance() { return insurance; }

    @Override
    public String toString() {
        return String.format("Patient{demographics=%s, contact=%s, insurance=%s}",
                demographics, contactInfo, insurance);
    }
}</code></pre><h3>Usage Example - Putting It All Together</h3><p>Notice how the refactored version transforms 8 primitive parameters (firstName, lastName, phone, email, insuranceProvider, policyNumber, appointmentDate, appointmentType) into just 4 meaningful objects. The <code>PatientDemographics</code> object encapsulates demographic data, <code>ContactInformation</code> handles contact details, <code>InsuranceDetails</code> manages insurance data, and <code>Appointment</code> represents appointment information. This not only reduces parameter counts but also makes the code self-documenting.</p><p>When you call <code>demographics.getFullName()</code> or <code>insurance.getFormattedCoverage()</code>, it's immediately clear what you're working with. Compare this to the original scattered parameters where they could easily be confused or passed in the wrong order.</p><pre><code>public class PatientRegistrationDemo {
    public static void main(String[] args) {
        System.out.println("=== Patient Registration System ===\n");

        PatientDemographics demographics = new PatientDemographics("John", "Doe");
        ContactInformation contactInfo = new ContactInformation("555-123-4567", "[email&nbsp;protected]");
        InsuranceDetails insurance = new InsuranceDetails("Blue Cross Blue Shield", "BCBS123456");

        PatientManager patientManager = new PatientManager();
        patientManager.registerPatient(demographics, contactInfo, insurance);

        System.out.println("\nPatient registered successfully:");
        System.out.println("  Name: " + demographics.getFullName());
        System.out.println("  Email: " + contactInfo.getEmail());
        System.out.println("  Insurance: " + insurance.getFormattedCoverage());

        System.out.println("\n=== Scheduling Appointment ===");
        Appointment appointment = new Appointment("2024-03-15 10:00 AM", "Annual Checkup");
        patientManager.scheduleAppointment(demographics, contactInfo, appointment);

        System.out.println("\n=== Updating Contact Information ===");
        ContactInformation updatedContact = new ContactInformation("555-111-2222", "[email&nbsp;protected]");
        patientManager.updateContactInfo(demographics, updatedContact);
        System.out.println("Contact information updated successfully");
    }
}</code></pre><h4>Expected Output:</h4><pre><code>=== Patient Registration System ===

Patient registered successfully:
  Name: John Doe
  Email: [email&nbsp;protected]
  Insurance: Blue Cross Blue Shield (Policy: BCBS123456)

=== Scheduling Appointment ===
Email sent to [email&nbsp;protected]: Appointment Confirmation
SMS sent to 555-123-4567: Appointment scheduled for John Doe on 2024-03-15 10:00 AM for Annual Checkup

=== Updating Contact Information ===
Contact information updated successfully</code></pre><h2>Benefits of Extract Class for Data Clumps</h2><p>Benefit</p><p>Description</p><p><em>Reduced Parameter Lists</em></p><p><em>Methods have fewer parameters, making them easier to call and understand.</em></p><p><em>Better Code Organization</em></p><p><em>Related data is grouped together, making the code more logical and maintainable.</em></p><p><em>Reduced Coupling</em></p><p><em>Changes to related data only require updates in one place, reducing the impact of changes.</em></p><p><em>Meaningful Methods</em></p><p><em>Each object provides domain-specific methods like </em><code>getFullName()</code><em> or </em><code>getFormattedCoverage()</code><em>, making the code more expressive.</em></p><h2>When to Apply Extract Class for Data Clumps</h2><ul><li><p>The same group of parameters appears in multiple method signatures.</p></li><li><p>Related data items are scattered across multiple classes.</p></li><li><p>Long parameter lists that are hard to understand and maintain.</p></li><li><p>When you find yourself passing the same set of parameters to multiple methods.</p></li><li><p>When related data changes together and should be treated as a unit.</p></li></ul><p>Apply Extract Class refactoring to one set of data clumps in your current project today. Start with the data clumps that appear most frequently across your codebase.</p><h2>Repository &amp; Resources</h2><p><strong>Complete Code Examples:</strong> <a href="https://github.com/shift-elevate/clean-code">Clean Code Repository</a></p><p>Find the complete implementation of Data Clumps refactoring and other clean code techniques in our dedicated repository. Each example includes:</p><ul><li><p>Before and after code comparisons</p></li><li><p>Unit tests demonstrating the improvements</p></li></ul><p><em>Found this helpful? Share it with a colleague who's struggling with Data Clumps. Got questions? We'd love to hear from you at <a href="/cdn-cgi/l/email-protection#771418190316140337041f1e1103121b120116031259131201">[email&nbsp;protected]</a></em></p>]]></content:encoded></item><item><title><![CDATA[Gateway Aggregation Pattern: Consolidating Multiple Service Calls into Single Requests]]></title><description><![CDATA[Master the Gateway Aggregation pattern with practical Java implementations demonstrating parallel service calls and response aggregation for reducing client complexity in distributed systems.]]></description><link>https://newsletter.shiftelevate.dev/p/gateway-aggregation-pattern-consolidating-multiple-service-calls-into-single-requests</link><guid isPermaLink="false">https://newsletter.shiftelevate.dev/p/gateway-aggregation-pattern-consolidating-multiple-service-calls-into-single-requests</guid><dc:creator><![CDATA[Shift Elevate]]></dc:creator><pubDate>Wed, 17 Dec 2025 13:41:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/0f46c33f-a87e-40e2-992f-d6d35fba7e3b_1200x630.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>In distributed systems, clients often need to make multiple service calls to gather all required data, leading to increased complexity, latency, and network overhead. The Gateway Aggregation pattern provides a solution by consolidating multiple service calls into a single request through an API gateway, reducing client complexity and improving performance.</em></p><p>This pattern is essential for modern microservices architectures where clients need data from multiple services to render a single view. By implementing intelligent aggregation strategies, you can significantly reduce client complexity while maintaining system performance and reliability.</p><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qReq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43e0e6a1-4db3-49a4-ac17-5ba44b5894ed_1920x1058.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qReq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43e0e6a1-4db3-49a4-ac17-5ba44b5894ed_1920x1058.jpeg 424w, https://substackcdn.com/image/fetch/$s_!qReq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43e0e6a1-4db3-49a4-ac17-5ba44b5894ed_1920x1058.jpeg 848w, https://substackcdn.com/image/fetch/$s_!qReq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43e0e6a1-4db3-49a4-ac17-5ba44b5894ed_1920x1058.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!qReq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43e0e6a1-4db3-49a4-ac17-5ba44b5894ed_1920x1058.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qReq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43e0e6a1-4db3-49a4-ac17-5ba44b5894ed_1920x1058.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/43e0e6a1-4db3-49a4-ac17-5ba44b5894ed_1920x1058.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!qReq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43e0e6a1-4db3-49a4-ac17-5ba44b5894ed_1920x1058.jpeg 424w, https://substackcdn.com/image/fetch/$s_!qReq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43e0e6a1-4db3-49a4-ac17-5ba44b5894ed_1920x1058.jpeg 848w, https://substackcdn.com/image/fetch/$s_!qReq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43e0e6a1-4db3-49a4-ac17-5ba44b5894ed_1920x1058.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!qReq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43e0e6a1-4db3-49a4-ac17-5ba44b5894ed_1920x1058.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a><p>Gateway Aggregation</p><p>This guide walks you through the Gateway Aggregation pattern from concept to practical implementation, covering parallel service calls, response aggregation, and handling multiple service requests.</p><ul><li><p><a href="#understanding-the-gateway-aggregati">Understanding the Gateway Aggregation Pattern</a></p></li><li><p><a href="#implementing-the-gateway-aggregatio">Implementing the Gateway Aggregation Pattern</a></p></li><li><p><a href="#when-to-use-gateway-aggregation-pat">When to Use Gateway Aggregation Pattern</a></p></li><li><p><a href="#best-practices">Best Practices</a></p></li></ul><h2>Understanding the Gateway Aggregation Pattern</h2><p>The Gateway Aggregation pattern uses an API gateway to collect data from multiple backend services and combine them into a single response. This reduces the number of round trips between clients and services, improving performance and simplifying client-side logic.</p><h3>Core Architecture</h3><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!l9ho!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa052c908-21dc-4439-b22a-112f8b6d0754_1218x871.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!l9ho!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa052c908-21dc-4439-b22a-112f8b6d0754_1218x871.png 424w, https://substackcdn.com/image/fetch/$s_!l9ho!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa052c908-21dc-4439-b22a-112f8b6d0754_1218x871.png 848w, https://substackcdn.com/image/fetch/$s_!l9ho!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa052c908-21dc-4439-b22a-112f8b6d0754_1218x871.png 1272w, https://substackcdn.com/image/fetch/$s_!l9ho!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa052c908-21dc-4439-b22a-112f8b6d0754_1218x871.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!l9ho!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa052c908-21dc-4439-b22a-112f8b6d0754_1218x871.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a052c908-21dc-4439-b22a-112f8b6d0754_1218x871.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!l9ho!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa052c908-21dc-4439-b22a-112f8b6d0754_1218x871.png 424w, https://substackcdn.com/image/fetch/$s_!l9ho!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa052c908-21dc-4439-b22a-112f8b6d0754_1218x871.png 848w, https://substackcdn.com/image/fetch/$s_!l9ho!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa052c908-21dc-4439-b22a-112f8b6d0754_1218x871.png 1272w, https://substackcdn.com/image/fetch/$s_!l9ho!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa052c908-21dc-4439-b22a-112f8b6d0754_1218x871.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><p>Gateway Aggregation Interactions</p><h3>Key Benefits</h3><ul><li><p><strong>Reduced Client Complexity</strong>: Clients make fewer requests and handle less data aggregation logic.</p></li><li><p><strong>Improved Performance</strong>: Parallel service calls reduce overall response time.</p></li><li><p><strong>Reduced Network Overhead</strong>: Fewer round trips between clients and services.</p></li><li><p><strong>Better Error Handling</strong>: Track errors from individual services and return partial results.</p></li><li><p><strong>Consistent Data Format</strong>: Standardized response format across all aggregated endpoints.</p></li></ul><h2>Implementing the Gateway Aggregation Pattern</h2><p>Let's build a gateway aggregation system that demonstrates the core pattern with parallel service calls and response aggregation.</p><h3>Implementation Overview</h3><ul><li><p><strong>AggregationGateway</strong>: Core gateway managing service calls, aggregation rules, and parallel execution</p></li><li><p><strong>Service Clients</strong>: Mock implementations for calling backend services</p></li><li><p><strong>Configuration Models</strong>: Simple data structures for rules, requests, and responses</p></li><li><p><strong>E-commerce Example</strong>: Practical dashboard aggregation demonstration</p></li></ul><h4>Note on Implementation</h4><p>This implementation uses <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">CompletableFuture</mark></em> for parallel service calls, providing non-blocking aggregation. The gateway executes multiple service calls concurrently and waits for all results before combining them into a single response. For production systems, consider using reactive frameworks like Project Reactor or RxJava for better resource utilization and backpressure handling.</p><h3>Core Aggregation Gateway Implementation</h3><h4>Gateway Class Structure</h4><p>The core gateway manages service calls and aggregation rules:</p><pre><code>public class AggregationGateway {
    private final Map&lt;String, AggregationRule&gt; aggregationRules = new ConcurrentHashMap&lt;&gt;();
    private final ExecutorService executorService;

    public AggregationGateway() {
        this.executorService = Executors.newFixedThreadPool(20);
    }

    public void registerRule(AggregationRule rule) {
        aggregationRules.put(rule.endpoint(), rule);
    }

    public void shutdown() {
        executorService.shutdown();
    }
}</code></pre><h4>Processing Requests and Parallel Execution</h4><p>The gateway processes requests by executing multiple service calls in parallel:</p><pre><code>public AggregatedResponse processRequest(String endpoint, Map&lt;String, Object&gt; requestData) {
    AggregationRule rule = aggregationRules.get(endpoint);
    if (rule == null) {
        return new AggregatedResponse(false, "Unknown endpoint: " + endpoint, null);
    }

    // Start all service calls in parallel
    List&lt;CompletableFuture&lt;ServiceResponse&gt;&gt; futures = new ArrayList&lt;&gt;();
    for (ServiceCall call : rule.serviceCalls()) {
        futures.add(CompletableFuture.supplyAsync(() -&gt;
            callService(call, requestData), executorService));
    }

    // Wait for all calls to complete
    try {
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
            .get(rule.timeoutMs(), TimeUnit.MILLISECONDS);
    } catch (Exception e) {
        System.err.println("Some services timed out");
    }

    // Collect results
    Map&lt;String, Object&gt; aggregatedData = new HashMap&lt;&gt;();
    List&lt;ServiceError&gt; errors = new ArrayList&lt;&gt;();

    for (int i = 0; i &lt; futures.size(); i++) {
        ServiceCall call = rule.serviceCalls().get(i);
        try {
            ServiceResponse response = futures.get(i).get(100, TimeUnit.MILLISECONDS);
            if (response.success()) {
                aggregatedData.put(call.responseKey(), response.data());
            } else {
                errors.add(new ServiceError(call.serviceName(), response.errorMessage()));
            }
        } catch (Exception e) {
            errors.add(new ServiceError(call.serviceName(), "Service unavailable"));
        }
    }

    boolean success = errors.isEmpty();
    return new AggregatedResponse(success,
        success ? "Success" : "Partial success", aggregatedData, errors);
}

private ServiceResponse callService(ServiceCall call, Map&lt;String, Object&gt; requestData) {
    try {
        // Simulate service call with mock data
        Thread.sleep(100 + (long)(Math.random() * 200));

        // Simulate occasional failures
        if (Math.random() &lt; 0.1) {
            return new ServiceResponse(false, call.serviceName(), "Service error", null);
        }

        // Return mock response
        return new ServiceResponse(true, call.serviceName(), null,
            Map.of("service", call.serviceName(), "data", "mock data"));
    } catch (Exception e) {
        return new ServiceResponse(false, call.serviceName(), e.getMessage(), null);
    }
}</code></pre><h3>Configuration and Response Models</h3><p>Simple data structures for the gateway:</p><pre><code>// Aggregation rule defines which services to call
record AggregationRule(String endpoint, List&lt;ServiceCall&gt; serviceCalls, long timeoutMs) {}

// Individual service call configuration
record ServiceCall(String serviceName, String responseKey) {}

// Aggregated response from gateway
public class AggregatedResponse {
    private final boolean success;
    private final String message;
    private final Map&lt;String, Object&gt; data;
    private final List&lt;ServiceError&gt; errors;

    public AggregatedResponse(boolean success, String message, Map&lt;String, Object&gt; data) {
        this(success, message, data, new ArrayList&lt;&gt;());
    }

    public AggregatedResponse(boolean success, String message, Map&lt;String, Object&gt; data,
                             List&lt;ServiceError&gt; errors) {
        this.success = success;
        this.message = message;
        this.data = data != null ? new HashMap&lt;&gt;(data) : new HashMap&lt;&gt;();
        this.errors = errors != null ? new ArrayList&lt;&gt;(errors) : new ArrayList&lt;&gt;();
    }

    public boolean isSuccess() { return success; }
    public String getMessage() { return message; }
    public Map&lt;String, Object&gt; getData() { return data; }
    public List&lt;ServiceError&gt; getErrors() { return errors; }
}

// Service response and error records
record ServiceResponse(boolean success, String serviceName, String errorMessage, Object data) {}
record ServiceError(String serviceName, String errorMessage) {}</code></pre><h2>When to Use Gateway Aggregation Pattern</h2><p>Understanding when to apply the Gateway Aggregation pattern is crucial for making the right architectural decisions. Here's when it shines and when alternatives might be better:</p><h3>&#9989; Ideal Scenarios:</h3><ul><li><p>Clients need data from multiple services to render a single view (dashboards, profiles, etc.).</p></li><li><p>You want to reduce network round trips and latency for mobile or low-bandwidth clients.</p></li><li><p>Different client types need different data aggregations from the same backend services.</p></li><li><p>You need to simplify client-side logic by moving complexity to the gateway.</p></li><li><p>You want to provide backward compatibility while refactoring monoliths to microservices.</p></li></ul><h3>&#10060; Skip It When:</h3><ul><li><p>You only have simple single-service requests with no aggregation needs.</p></li><li><p>Services have vastly different SLAs making aggregation unreliable.</p></li><li><p>Real-time consistency across all services is critical and aggregation adds unacceptable latency.</p></li><li><p>The added complexity of maintaining aggregation rules doesn't justify the client-side simplification.</p></li></ul><h2>Best Practices</h2><p><strong>Set Appropriate Timeouts</strong>: Critical services need shorter timeouts, while non-critical services can tolerate longer ones.</p><p><strong>Handle Partial Failures</strong>: Return partial results when some services fail. If notifications fail, still return user profile and orders.</p><p><strong>Use Parallel Execution</strong>: Execute independent service calls concurrently. Use sequential calls only when services have dependencies.</p><p><strong>Keep It Simple</strong>: Start with basic aggregation and parallel execution. Focus on the core pattern before adding complexity.</p><p><em>Found this helpful? Share it with a colleague who's struggling with client complexity in their microservices architecture. Got questions? We'd love to hear from you at <a href="/cdn-cgi/l/email-protection#6c0f0302180d0f182c1f04050a180900091a0d18094208091a">[email&nbsp;protected]</a></em></p>]]></content:encoded></item><item><title><![CDATA[Mediator Pattern: Simplify Object Communication with Centralized Control]]></title><description><![CDATA[Master the Mediator design pattern in Java with step by step implementation, air traffic control examples, and practical code for simplifying object communication with centralized control.]]></description><link>https://newsletter.shiftelevate.dev/p/mediator-pattern-simplify-object-communication-with-centralized-control</link><guid isPermaLink="false">https://newsletter.shiftelevate.dev/p/mediator-pattern-simplify-object-communication-with-centralized-control</guid><dc:creator><![CDATA[Shift Elevate]]></dc:creator><pubDate>Sat, 06 Dec 2025 10:38:05 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/8833f6d5-0d45-4668-a6f3-daba027b3fef_1200x630.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>The Pain of Complex Object Interactions</h2><p><strong>Picture this:</strong> You're building an air traffic control system where multiple aircraft need to coordinate landings and takeoffs. Your initial approach seems logical: aircraft communicate directly with each other to avoid collisions.</p><p>But as the airport grows, you realize the chaos: each aircraft needs to know about every other aircraft, constantly checking positions, coordinating runway usage, and negotiating priorities. Adding a new aircraft means every existing aircraft's logic needs updating. It's a tangled web of dependencies where planes are tightly coupled to each other.</p><p>The <strong>Mediator</strong> pattern solves this complexity by introducing a control tower that centralizes all communication, promoting loose coupling and making the system easier to understand and maintain.</p><h2>Understanding the Mediator Pattern</h2><p><em>The Mediator pattern defines an object that encapsulates how a set of objects interact. Instead of objects referring to each other directly, they communicate through the mediator, which acts as a central hub for all interactions.</em></p><p>Think of it like an air traffic control tower: instead of pilots communicating directly with each other (which would be chaotic), they all communicate through the control tower, which coordinates and manages all interactions safely and efficiently.</p><p>This pattern promotes <strong>Loose Coupling</strong>, <strong>Centralized Control</strong>, and <strong>Simplified Communication</strong> while reducing the complexity of object interactions.</p><p><strong>Without Mediator</strong>: Components communicate directly with each other, creating tight coupling. Each component needs to know about every other component, leading to complex dependencies and difficult maintenance.</p><p><strong>With Mediator</strong>: Components communicate through a central mediator. The mediator coordinates all interactions, and components only need to know about the mediator, not each other.</p><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!vqQs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3aa2fb2-846f-4ab9-9f24-e310f94d9fc1_520x380.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!vqQs!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3aa2fb2-846f-4ab9-9f24-e310f94d9fc1_520x380.png 424w, https://substackcdn.com/image/fetch/$s_!vqQs!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3aa2fb2-846f-4ab9-9f24-e310f94d9fc1_520x380.png 848w, https://substackcdn.com/image/fetch/$s_!vqQs!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3aa2fb2-846f-4ab9-9f24-e310f94d9fc1_520x380.png 1272w, https://substackcdn.com/image/fetch/$s_!vqQs!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3aa2fb2-846f-4ab9-9f24-e310f94d9fc1_520x380.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!vqQs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3aa2fb2-846f-4ab9-9f24-e310f94d9fc1_520x380.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d3aa2fb2-846f-4ab9-9f24-e310f94d9fc1_520x380.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!vqQs!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3aa2fb2-846f-4ab9-9f24-e310f94d9fc1_520x380.png 424w, https://substackcdn.com/image/fetch/$s_!vqQs!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3aa2fb2-846f-4ab9-9f24-e310f94d9fc1_520x380.png 848w, https://substackcdn.com/image/fetch/$s_!vqQs!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3aa2fb2-846f-4ab9-9f24-e310f94d9fc1_520x380.png 1272w, https://substackcdn.com/image/fetch/$s_!vqQs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3aa2fb2-846f-4ab9-9f24-e310f94d9fc1_520x380.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><p>Mediator Pattern Components</p><h3>Core Components</h3><ul><li><p><strong><a href="#mediator-interface">Mediator Interface</a></strong>: Defines the communication interface for coordinating operations.</p></li><li><p><strong><a href="#concrete-mediator">Concrete Mediator</a></strong>: Implements the mediator interface and coordinates all component interactions.</p></li><li><p><strong><a href="#aircraft-component">Aircraft Component</a></strong>: Communicates with the tower for landing, takeoff, and position reporting.</p></li><li><p><strong><a href="#ground-control-component">Ground Control Component</a></strong>: Receives notifications from the tower about aircraft activities.</p></li></ul><h2>Complete Java Implementation</h2><p>Let's build an air traffic control system that demonstrates the Mediator pattern's power in preventing direct communication chaos.</p><h3>Mediator Interface</h3><pre><code>public interface ControlTower {
    void requestLanding(Aircraft aircraft);
    void requestTakeoff(Aircraft aircraft);
    void reportPosition(Aircraft aircraft, String position);
    void registerAircraft(Aircraft aircraft);
    void notifyGroundControl(GroundControl groundControl);
}</code></pre><h3>Concrete Mediator</h3><pre><code>public class AirTrafficControlTower implements ControlTower {
    private List&lt;Aircraft&gt; aircrafts = new ArrayList&lt;&gt;();
    private GroundControl groundControl;
    private boolean runwayAvailable = true;

    @Override
    public void registerAircraft(Aircraft aircraft) {
        aircrafts.add(aircraft);
    }

    @Override
    public void notifyGroundControl(GroundControl groundControl) {
        this.groundControl = groundControl;
    }

    @Override
    public void requestLanding(Aircraft aircraft) {
        if (runwayAvailable) {
            runwayAvailable = false;
            System.out.println("Tower: " + aircraft.getName() + " cleared for landing");

            // Notify other aircraft to hold
            for (Aircraft other : aircrafts) {
                if (other != aircraft) {
                    other.receiveMessage("Hold position, " + aircraft.getName() + " is landing");
                }
            }

            // Notify ground control
            if (groundControl != null) {
                groundControl.receiveNotification(aircraft.getName() + " is landing");
            }
        } else {
            aircraft.receiveMessage("Runway occupied, please hold");
        }
    }

    @Override
    public void requestTakeoff(Aircraft aircraft) {
        if (runwayAvailable) {
            runwayAvailable = false;
            System.out.println("Tower: " + aircraft.getName() + " cleared for takeoff");
        } else {
            aircraft.receiveMessage("Runway occupied, hold for takeoff");
        }
    }

    @Override
    public void reportPosition(Aircraft aircraft, String position) {
        System.out.println("Tower: " + aircraft.getName() + " reported at " + position);
        runwayAvailable = true;

        // Notify ground control about aircraft position
        if (groundControl != null) {
            groundControl.receiveNotification(aircraft.getName() + " at " + position);
        }
    }
}</code></pre><h3>Aircraft Component</h3><pre><code>public class Aircraft {
    private String name;
    private ControlTower tower;

    public Aircraft(String name, ControlTower tower) {
        this.name = name;
        this.tower = tower;
        tower.registerAircraft(this);
    }

    public void requestLanding() {
        System.out.println(name + ": Requesting landing clearance");
        tower.requestLanding(this);
    }

    public void requestTakeoff() {
        System.out.println(name + ": Requesting takeoff clearance");
        tower.requestTakeoff(this);
    }

    public void reportPosition(String position) {
        System.out.println(name + ": Reporting position - " + position);
        tower.reportPosition(this, position);
    }

    public void receiveMessage(String message) {
        System.out.println(name + " received: " + message);
    }

    public String getName() {
        return name;
    }
}</code></pre><h3>Ground Control Component</h3><pre><code>public class GroundControl {
    private String name;

    public GroundControl(String name) {
        this.name = name;
    }

    public void receiveNotification(String message) {
        System.out.println(name + " notified: " + message);
    }
}</code></pre><h3>Client</h3><pre><code>public class AirTrafficControllerDemo {
    public static void main(String[] args) {
        // Create control tower (mediator)
        ControlTower tower = new AirTrafficControlTower();

        // Create and set ground control
        GroundControl groundControl = new GroundControl("Ground Control");
        tower.notifyGroundControl(groundControl);

        // Create aircraft
        Aircraft flight1 = new Aircraft("Flight 101", tower);
        Aircraft flight2 = new Aircraft("Flight 202", tower);

        System.out.println("=== Air Traffic Control Demo ===\n");

        // Flight 1 requests landing
        flight1.requestLanding();

        System.out.println();

        // Flight 2 tries to land while runway is occupied
        flight2.requestLanding();

        System.out.println();

        // Flight 1 reports landed
        flight1.reportPosition("Gate A");

        System.out.println();

        // Now Flight 2 can land
        flight2.requestLanding();
    }
}</code></pre><h4>Expected Output:</h4><pre><code>=== Air Traffic Control Demo ===

Flight 101: Requesting landing clearance
Tower: Flight 101 cleared for landing
Flight 202 received: Hold position, Flight 101 is landing
Ground Control notified: Flight 101 is landing

Flight 202: Requesting landing clearance
Flight 202 received: Runway occupied, please hold

Flight 101: Reporting position - Gate A
Tower: Flight 101 reported at Gate A
Ground Control notified: Flight 101 at Gate A

Flight 202: Requesting landing clearance
Tower: Flight 202 cleared for landing
Flight 101 received: Hold position, Flight 202 is landing
Ground Control notified: Flight 202 is landing</code></pre><h3>&#128640; Get the Complete Implementation</h3><p>The full code with advanced mediator implementations and complex interaction scenarios is available in our <a href="https://github.com/shift-elevate/design-patterns">Design Patterns Repository</a>.</p><pre><code># Clone and run the complete demo
git clone https://github.com/shift-elevate/design-patterns.git
cd design-patterns
mvn test -Dtest=MediatorPatternTest</code></pre><h2>Real World Examples</h2><p>The Mediator pattern is extensively used in real world applications:</p><h3>1. GUI Frameworks</h3><p>Modern GUI frameworks use the Mediator pattern extensively for component communication. Instead of UI components directly communicating with each other, they communicate through a central mediator that manages events, state changes, and component interactions.</p><h3>2. Chat Applications</h3><p>Chat applications use the Mediator pattern to manage communication between users. The chat server acts as a mediator, receiving messages from one user and distributing them to all other users in the chat room.</p><h3>3. Workflow Management Systems</h3><p>Business process management systems use the Mediator pattern to coordinate different workflow steps, ensuring that each step communicates through a central workflow engine rather than directly with other steps.</p><h2>When to Use the Mediator Pattern</h2><p>Understanding when to apply the Mediator pattern is crucial for making the right architectural decisions. Here's when it shines and when alternatives might be better:</p><h3>&#9989;<strong> Ideal Scenarios:</strong></h3><ul><li><p>You have a set of objects that communicate in well defined but complex ways.</p></li><li><p>You want to avoid tight coupling between objects that interact.</p></li><li><p>You need to reuse objects in different contexts without changing their communication logic.</p></li><li><p>You want to centralize control over how objects interact.</p></li></ul><h3>&#10060;<strong> Skip It When:</strong></h3><ul><li><p>The communication between objects is simple and doesn't benefit from centralization.</p></li><li><p>You have a small number of objects with minimal interactions.</p></li></ul><h2>Next Steps: Apply Mediator Pattern in Your Project</h2><p>Ready to implement the Mediator pattern in your own projects? Here's a structured approach to get you started:</p><ol><li><p><strong>Identify Complex Interactions</strong>: Look for areas where multiple objects need to communicate with each other.</p></li><li><p><strong>Define Communication Protocol</strong>: Create a mediator interface that defines how objects should interact.</p></li><li><p><strong>Implement Central Mediator</strong>: Build a concrete mediator that handles all object interactions.</p></li><li><p><strong>Refactor Components</strong>: Modify existing objects to communicate through the mediator.</p></li><li><p><strong>Test Interaction Scenarios</strong>: Ensure all communication flows work correctly through the mediator.</p></li></ol><p>The Mediator pattern transforms complex, tightly coupled object interactions into clean, maintainable systems. By centralizing communication through a mediator, you build systems that are easier to understand, modify, and extend.</p><p><em>Found this helpful? Share it with a colleague who's struggling with complex object interactions. Got questions? We'd love to hear from you at <a href="/cdn-cgi/l/email-protection#34575b5a4055574074475c5d5240515851425540511a505142">[email&nbsp;protected]</a></em></p>]]></content:encoded></item><item><title><![CDATA[Large Class: Extract Class Refactoring | Clean Code]]></title><description><![CDATA[Refactor the Large Class code smell with Extract Class technique, improve single responsibility, and enhance maintainability.]]></description><link>https://newsletter.shiftelevate.dev/p/large-class-extract-class-refactoring-clean-code</link><guid isPermaLink="false">https://newsletter.shiftelevate.dev/p/large-class-extract-class-refactoring-clean-code</guid><dc:creator><![CDATA[Shift Elevate]]></dc:creator><pubDate>Wed, 03 Dec 2025 15:19:05 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/3244f564-c7f1-465e-8b49-0539244b5579_1200x630.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Large Class is a code smell where a class has grown too large and tries to do too much.</em></p><p>We will see how we can refactor using the Extract Class technique for better maintainability and single responsibility.</p><h3>Clean Code Reference</h3><p>&#10077;</p><p><strong>&#9888;&#65039; Code Smell:</strong><span data-color="rgb(204, 204, 204)" style="color: rgb(204, 204, 204);"> </span><em>Large Class</em><br>&#9989;<strong> Refactoring:</strong><span data-color="rgb(204, 204, 204)" style="color: rgb(204, 204, 204);"> </span><em>Extract Class</em><br>&#127919;<strong> Goal:</strong><span data-color="rgb(204, 204, 204)" style="color: rgb(204, 204, 204);"> </span><em>Single Responsibility Principle and focused classes</em></p><p>The Large Class code smell occurs when a class becomes too large and takes on too many responsibilities, violating the Single Responsibility Principle. <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">The Extract Class</mark></em> refactoring technique breaks down large classes into smaller, focused classes that each have a single, well-defined responsibility.</p><h2>The Code Smell: Large Class</h2><p>Large Class is one of the most common code smells that indicates a violation of the Single Responsibility Principle. When a class grows too large, it becomes difficult to understand, test, and maintain. Large classes often contain multiple unrelated responsibilities, making them fragile and prone to bugs.</p><p>Symptoms</p><p>Impact</p><p><em>Too many fields and methods</em></p><p><em>Difficult to understand</em></p><p><em>Multiple unrelated responsibilities</em></p><p><em>Hard to test and maintain</em></p><p><em>Frequent changes affecting many methods</em></p><p><em>Higher bug risk</em></p><h4>Here's a typical example of Large Class:</h4><pre><code>public class CustomerManager {
    // Customer data fields
    private String customerId;
    private String firstName;
    private String lastName;
    private String email;
    private String phone;
    private String address;
    private String city;
    private String state;
    private String zipCode;
    private String country;
    
    // Account management fields
    private double accountBalance;
    private String accountStatus;
    private Date accountCreatedDate;
    private Date lastLoginDate;
    private int loyaltyPoints;

    // Notification preferences
    private boolean emailNotifications;
    private boolean smsNotifications;
    private boolean pushNotifications;
    private String preferredLanguage;
    
    // What does this massive class do? Everything!
    
    // Customer data methods
    public void updatePersonalInfo(String firstName, String lastName, String email) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.email = email;
        validateEmail(email);
        logPersonalInfoUpdate();
    }
    
    public void updateAddress(String address, String city, String state, String zipCode, String country) {
        this.address = address;
        this.city = city;
        this.state = state;
        this.zipCode = zipCode;
        this.country = country;
        validateAddress();
        logAddressUpdate();
    }
    
    // Account management methods
    public void processPayment(double amount) {
        if (accountBalance &gt;= amount) {
            accountBalance -= amount;
            updateLoyaltyPoints(amount);
            logTransaction(amount);
            sendPaymentConfirmation();
        } else {
            throw new InsufficientFundsException("Insufficient account balance");
        }
    }
    
    public void addFunds(double amount) {
        accountBalance += amount;
        logTransaction(amount);
        sendBalanceUpdateNotification();
    }
    
    public void updateAccountStatus(String status) {
        this.accountStatus = status;
        logStatusChange(status);
        if ("SUSPENDED".equals(status)) {
            sendAccountSuspensionNotification();
        }
    }

    // Notification methods
    public void updateNotificationPreferences(boolean email, boolean sms, boolean push) {
        this.emailNotifications = email;
        this.smsNotifications = sms;
        this.pushNotifications = push;
        logPreferenceUpdate();
    }
    
    public void sendNotification(String message, String type) {
        if ("EMAIL".equals(type) &amp;&amp; emailNotifications) {
            sendEmailNotification(message);
        } else if ("SMS".equals(type) &amp;&amp; smsNotifications) {
            sendSMSNotification(message);
        } else if ("PUSH".equals(type) &amp;&amp; pushNotifications) {
            sendPushNotification(message);
        }
    }
    
    // Private helper methods (too many!)
    private void validateEmail(String email) { /* validation logic */ }
    private void validateAddress() { /* validation logic */ }
    private void logPersonalInfoUpdate() { /* logging logic */ }
    private void logAddressUpdate() { /* logging logic */ }
    private void logTransaction(double amount) { /* logging logic */ }
    private void logStatusChange(String status) { /* logging logic */ }
    private void logPreferenceUpdate() { /* logging logic */ }
    private void updateLoyaltyPoints(double amount) { /* loyalty logic */ }
    private void sendPaymentConfirmation() { /* notification logic */ }
    private void sendBalanceUpdateNotification() { /* notification logic */ }
    private void sendAccountSuspensionNotification() { /* notification logic */ }
    private void sendEmailNotification(String message) { /* email logic */ }
    private void sendSMSNotification(String message) { /* SMS logic */ }
    private void sendPushNotification(String message) { /* push logic */ }
}</code></pre><p>In this example, the <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">CustomerManager</mark></em> class is trying to do everything: manage customer data, handle account operations, and manage notifications. This violates the Single Responsibility Principle and makes the class difficult to understand, test, and maintain.</p><h2>The Refactoring: Extract Class</h2><p>The Extract Class refactoring technique breaks down large classes into smaller, focused classes that each have a single, well-defined responsibility. This improves code organization, testability, and maintainability.</p><h4>Step by Step Refactoring Process:</h4><ol><li><p><strong>Identify distinct responsibilities</strong> within the large class.</p></li><li><p><strong>Group related fields and methods</strong> that belong together.</p></li><li><p><strong>Create new classes</strong> for each identified responsibility.</p></li><li><p><strong>Move fields and methods</strong> to their appropriate classes.</p></li><li><p><strong>Update the original class</strong> to use the new classes through composition.</p></li></ol><h4>Here's the refactored version:</h4><p>The large class has been broken down into four focused classes:</p><ol><li><p><a href="#1-customer-manager-main-coordinator">CustomerManager</a> - Main coordinator class</p></li><li><p><a href="#2-customer-profile-manages-customer">CustomerProfile</a> - Manages customer personal information</p></li><li><p><a href="#3-account-manager-handles-account-o">AccountManager</a> - Handles account operations</p></li><li><p><a href="#4-notification-manager-manages-noti">NotificationManager</a> - Manages notification preferences and sending</p></li></ol><h3>1. CustomerManager - Main Coordinator Class</h3><pre><code>// Main class now focuses on coordinating other classes
public class CustomerManager {
    private CustomerProfile customerProfile;
    private AccountManager accountManager;
    private NotificationManager notificationManager;

    public CustomerManager(String customerId) {
        this.customerProfile = new CustomerProfile(customerId);
        this.accountManager = new AccountManager();
        this.notificationManager = new NotificationManager();
    }

    public void updatePersonalInfo(String firstName, String lastName, String email) {
        customerProfile.updatePersonalInfo(firstName, lastName, email);
    }

    public void updateAddress(String address, String city, String state, String zipCode, String country) {
        customerProfile.updateAddress(address, city, state, zipCode, country);
    }

    public void processPayment(double amount) {
        accountManager.processPayment(amount);
        notificationManager.sendPaymentConfirmation();
    }
}</code></pre><h3>2. CustomerProfile - Manages Customer Personal Information</h3><pre><code>// Focused class for customer profile data
public class CustomerProfile {
    private String customerId;
    private String firstName;
    private String lastName;
    private String email;
    private String phone;
    private String address;
    private String city;
    private String state;
    private String zipCode;
    private String country;
    
    public CustomerProfile(String customerId) {
        this.customerId = customerId;
    }
    
    public void updatePersonalInfo(String firstName, String lastName, String email) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.email = email;
        validateEmail(email);
        logPersonalInfoUpdate();
    }
    
    public void updateAddress(String address, String city, String state, String zipCode, String country) {
        this.address = address;
        this.city = city;
        this.state = state;
        this.zipCode = zipCode;
        this.country = country;
        validateAddress();
        logAddressUpdate();
    }
    
    private void validateEmail(String email) { /* validation logic */ }
    private void validateAddress() { /* validation logic */ }
    private void logPersonalInfoUpdate() { /* logging logic */ }
    private void logAddressUpdate() { /* logging logic */ }
    
    // Getters and other profile-related methods
}</code></pre><h3>3. AccountManager - Handles Account Operations</h3><pre><code>// Focused class for account operations
public class AccountManager {
    private double accountBalance;
    private String accountStatus;
    private Date accountCreatedDate;
    private Date lastLoginDate;
    private int loyaltyPoints;
    
    public void processPayment(double amount) {
        if (accountBalance &gt;= amount) {
            accountBalance -= amount;
            logTransaction(amount);
        } else {
            throw new InsufficientFundsException("Insufficient account balance");
        }
    }
    
    public void addFunds(double amount) {
        accountBalance += amount;
        logTransaction(amount);
    }
    
    public void updateAccountStatus(String status) {
        this.accountStatus = status;
        logStatusChange(status);
    }
    
    public void updateLoyaltyPoints(double amount) {
        loyaltyPoints += (int) (amount * 0.1); // 10% of amount as points
    }
    
    private void logTransaction(double amount) { /* logging logic */ }
    private void logStatusChange(String status) { /* logging logic */ }
    
    // Getters and other account-related methods
}</code></pre><h3>4. NotificationManager - Manages Notification Preferences and Sending</h3><pre><code>// Focused class for notifications
public class NotificationManager {
    private boolean emailNotifications;
    private boolean smsNotifications;
    private boolean pushNotifications;
    private String preferredLanguage;
    
    public NotificationManager() {
        this.emailNotifications = true;
        this.smsNotifications = false;
        this.pushNotifications = true;
        this.preferredLanguage = "EN";
    }
    
    public void updateNotificationPreferences(boolean email, boolean sms, boolean push) {
        this.emailNotifications = email;
        this.smsNotifications = sms;
        this.pushNotifications = push;
        logPreferenceUpdate();
    }
    
    public void sendNotification(String message, String type) {
        if ("EMAIL".equals(type) &amp;&amp; emailNotifications) {
            sendEmailNotification(message);
        } else if ("SMS".equals(type) &amp;&amp; smsNotifications) {
            sendSMSNotification(message);
        } else if ("PUSH".equals(type) &amp;&amp; pushNotifications) {
            sendPushNotification(message);
        }
    }
    
    public void sendPaymentConfirmation() {
        sendNotification("Payment processed successfully", "EMAIL");
    }

    private void logPreferenceUpdate() { /* logging logic */ }
    private void sendEmailNotification(String message) { /* email logic */ }
    private void sendSMSNotification(String message) { /* SMS logic */ }
    private void sendPushNotification(String message) { /* push logic */ }
}</code></pre><h3>Usage Example - Putting It All Together</h3><pre><code>public class Main {
    public static void main(String[] args) {
        // Create CustomerManager with refactored structure
        CustomerManager customerManager = new CustomerManager("CUST002");

        // Same operations, but now delegated to appropriate classes
        customerManager.updatePersonalInfo("Jane", "Smith", "[email&nbsp;protected]");
        customerManager.updateAddress("456 Oak Ave", "Chicago", "IL", "60601", "USA");

        customerManager.addFunds(750.0);
        customerManager.processPayment(200.0);

        customerManager.addOrder("ORDER003", 120.0);
        customerManager.addOrder("ORDER004", 80.0);

        customerManager.updateNotificationPreferences(true, true, false);

        System.out.println("Account Balance: $" + customerManager.getAccountBalance());
        System.out.println("Loyalty Points: " + customerManager.getLoyaltyPoints());
    }
}</code></pre><h4>Expected Output:</h4><pre><code>Personal info updated for customer: CUST002
Address updated for customer: CUST002
Transaction logged: $750.0
Email sent: Account balance updated
Transaction logged: $200.0
Email sent: Payment processed successfully
Email sent: Order ORDER003 confirmed
Email sent: Order ORDER004 confirmed
Notification preferences updated
Account Balance: $550.0
Loyalty Points: 20</code></pre><h2>Benefits of Extract Class</h2><p>Benefit</p><p>Description</p><p><em>Single Responsibility</em></p><p><em>Each class now has a single, well-defined responsibility, making them easier to understand and maintain.</em></p><p><em>Improved Testability</em></p><p><em>Smaller, focused classes are easier to test in isolation with fewer dependencies and edge cases.</em></p><p><em>Better Reusability</em></p><p><em>Extracted classes can be reused in other contexts without bringing unnecessary dependencies.</em></p><h2>When to Apply Extract Class Refactoring</h2><ul><li><p><strong>When classes have too many fields and methods</strong> (typically more than 20-30 methods).</p></li><li><p><strong>When classes have multiple reasons to change</strong> (violating Single Responsibility Principle).</p></li><li><p><strong>When classes contain groups of related methods</strong> that could be logically separated.</p></li><li><p><strong>When classes are difficult to understand</strong> due to their size and complexity.</p></li><li><p><strong>When classes have low cohesion</strong> where methods don't work together toward a common purpose.</p></li></ul><p>Apply Extract Class refactoring to one large class in your current project today. Start with the class that has the most obvious distinct responsibilities.</p><h2>Repository &amp; Resources</h2><p><strong>Complete Code Examples:</strong> <a href="https://github.com/shift-elevate/clean-code">Clean Code Repository</a></p><p>Find the complete implementation of Large Class refactoring and other clean code techniques in our dedicated repository. Each example includes:</p><ul><li><p>Before and after code comparisons</p></li><li><p>Unit tests demonstrating the improvements</p></li></ul><p><em>Found this helpful? Share it with a colleague who's struggling with Large Classes. Got questions? We'd love to hear from you at <a href="/cdn-cgi/l/email-protection#fb9894958f9a988fbb8893929d8f9e979e8d9a8f9ed59f9e8d">[email&nbsp;protected]</a></em></p>]]></content:encoded></item><item><title><![CDATA[Circuit Breaker Pattern: Preventing Cascading Failures in Distributed Systems]]></title><description><![CDATA[Master the Circuit Breaker pattern with state management, fallback strategies, and production-ready Java implementations for preventing cascading failures in distributed systems.]]></description><link>https://newsletter.shiftelevate.dev/p/circuit-breaker-pattern-preventing-cascading-failures-in-distributed-systems</link><guid isPermaLink="false">https://newsletter.shiftelevate.dev/p/circuit-breaker-pattern-preventing-cascading-failures-in-distributed-systems</guid><dc:creator><![CDATA[Shift Elevate]]></dc:creator><pubDate>Thu, 27 Nov 2025 12:02:06 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/3d4e2bea-188c-40ef-a3fa-471ad74188ba_1200x630.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In distributed systems, a single failing service can bring down entire applications through cascading failures. The Circuit Breaker pattern acts as a protective barrier, monitoring service health and automatically failing fast when services become unhealthy, preventing resource exhaustion and system-wide outages.</p><p>This pattern is essential for building resilient microservices architectures where service dependencies can fail independently. By implementing intelligent circuit protection, you can maintain system stability even when individual services experience issues.</p><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-0-Z!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc873ff0c-a94f-45ba-9a35-4b79ab40cff4_1216x768.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-0-Z!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc873ff0c-a94f-45ba-9a35-4b79ab40cff4_1216x768.jpeg 424w, https://substackcdn.com/image/fetch/$s_!-0-Z!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc873ff0c-a94f-45ba-9a35-4b79ab40cff4_1216x768.jpeg 848w, https://substackcdn.com/image/fetch/$s_!-0-Z!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc873ff0c-a94f-45ba-9a35-4b79ab40cff4_1216x768.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!-0-Z!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc873ff0c-a94f-45ba-9a35-4b79ab40cff4_1216x768.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-0-Z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc873ff0c-a94f-45ba-9a35-4b79ab40cff4_1216x768.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c873ff0c-a94f-45ba-9a35-4b79ab40cff4_1216x768.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!-0-Z!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc873ff0c-a94f-45ba-9a35-4b79ab40cff4_1216x768.jpeg 424w, https://substackcdn.com/image/fetch/$s_!-0-Z!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc873ff0c-a94f-45ba-9a35-4b79ab40cff4_1216x768.jpeg 848w, https://substackcdn.com/image/fetch/$s_!-0-Z!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc873ff0c-a94f-45ba-9a35-4b79ab40cff4_1216x768.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!-0-Z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc873ff0c-a94f-45ba-9a35-4b79ab40cff4_1216x768.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a><p>This guide walks you through the Circuit Breaker pattern from concept to production ready implementation, covering state management, fallback strategies, and real world deployment patterns.</p><ul><li><p><a href="#understanding-the-circuit-breaker-p">Understanding the Circuit Breaker Pattern</a></p></li><li><p><a href="#implementing-the-circuit-breaker-pa">Implementing the Circuit Breaker Pattern</a></p></li><li><p><a href="#when-to-use-circuit-breaker-pattern">When to Use Circuit Breaker Pattern</a></p></li><li><p><a href="#production-deployment-consideration">Production Deployment Considerations</a></p></li></ul><h2>Understanding the Circuit Breaker Pattern</h2><p>The Circuit Breaker pattern monitors the health of external services and automatically prevents calls to failing services. It operates in three distinct states, each serving a specific purpose in maintaining system resilience.</p><h3>Circuit Breaker States</h3><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!faAN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e6e2cb3-885e-4366-8f33-246e304832d3_994x1868.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!faAN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e6e2cb3-885e-4366-8f33-246e304832d3_994x1868.png 424w, https://substackcdn.com/image/fetch/$s_!faAN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e6e2cb3-885e-4366-8f33-246e304832d3_994x1868.png 848w, https://substackcdn.com/image/fetch/$s_!faAN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e6e2cb3-885e-4366-8f33-246e304832d3_994x1868.png 1272w, https://substackcdn.com/image/fetch/$s_!faAN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e6e2cb3-885e-4366-8f33-246e304832d3_994x1868.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!faAN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e6e2cb3-885e-4366-8f33-246e304832d3_994x1868.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5e6e2cb3-885e-4366-8f33-246e304832d3_994x1868.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!faAN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e6e2cb3-885e-4366-8f33-246e304832d3_994x1868.png 424w, https://substackcdn.com/image/fetch/$s_!faAN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e6e2cb3-885e-4366-8f33-246e304832d3_994x1868.png 848w, https://substackcdn.com/image/fetch/$s_!faAN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e6e2cb3-885e-4366-8f33-246e304832d3_994x1868.png 1272w, https://substackcdn.com/image/fetch/$s_!faAN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e6e2cb3-885e-4366-8f33-246e304832d3_994x1868.png 1456w" sizes="100vw"></picture><div></div></div></a><p>Circuit Breaker States</p><h3>Key Benefits</h3><ul><li><p><strong>Prevents Cascading Failures</strong>: Stops unhealthy services from affecting the entire system</p></li><li><p><strong>Fast Failure Detection</strong>: Immediately identifies and isolates failing services</p></li><li><p><strong>Automatic Recovery</strong>: Tests service health and automatically recovers when services are restored</p></li><li><p><strong>Resource Protection</strong>: Prevents resource exhaustion from repeated failed calls</p></li><li><p><strong>Graceful Degradation</strong>: Provides fallback mechanisms during service outages</p></li></ul><h2>Implementing the Circuit Breaker Pattern</h2><p>Let's build a comprehensive circuit breaker implementation that handles various failure scenarios and provides monitoring capabilities.</p><h3>Implementation Overview</h3><ul><li><p><strong>Circuit Breaker Class</strong>: Simple circuit breaker with three states and failure tracking</p></li><li><p><strong>Shipment Service Example</strong>: Real-world shipment booking with fallback handling</p></li><li><p><strong>Supporting Classes</strong>: Basic data models for demonstration</p></li></ul><h3>Circuit Breaker Implementation</h3><h4>Define the Circuit States</h4><pre><code>public enum CircuitState {
    CLOSED,     // Normal operation - requests pass through
    OPEN,       // Too many failures - requests are blocked
    HALF_OPEN   // Testing if service recovered
}</code></pre><h4>Create a Response Model</h4><pre><code>public class ShipmentResponse {
    private final boolean success;
    private final String message;
    private final String trackingNumber;

    public ShipmentResponse(boolean success, String message, String trackingNumber) {
        this.success = success;
        this.message = message;
        this.trackingNumber = trackingNumber;
    }
}</code></pre><h4>Create the Circuit Breaker</h4><pre><code>public class CircuitBreaker {
    private final int failureThreshold;        // Max failures before opening
    private final long timeoutDuration;        // Wait time before testing recovery
    private int failureCount = 0;
    private CircuitState state = CircuitState.CLOSED;
    private long lastFailureTime = 0;

    public CircuitBreaker(int failureThreshold, long timeoutDuration) {
        this.failureThreshold = failureThreshold;
        this.timeoutDuration = timeoutDuration;
    }

    public ShipmentResponse executeShipment(ShipmentOperation operation) throws Exception {
        // Check if circuit is OPEN
        if (state == CircuitState.OPEN) {
            if (shouldAttemptReset()) {
                state = CircuitState.HALF_OPEN;
            } else {
                throw new CircuitBreakerOpenException("Service unavailable");
            }
        }

        try {
            ShipmentResponse result = operation.process();
            onSuccess();
            return result;
        } catch (Exception e) {
            onFailure();
            throw e;
        }
    }

    private boolean shouldAttemptReset() {
        return System.currentTimeMillis() - lastFailureTime &gt; timeoutDuration;
    }

    private void onSuccess() {
        if (state == CircuitState.HALF_OPEN) {
            // Service recovered - close the circuit
            failureCount = 0;
            state = CircuitState.CLOSED;
        }
    }

    private void onFailure() {
        failureCount++;
        lastFailureTime = System.currentTimeMillis();

        if (failureCount &gt;= failureThreshold) {
            state = CircuitState.OPEN;
        }
    }

    public CircuitState getState() {
        return state;
    }
}

// Functional interface for shipment operations
interface ShipmentOperation {
    ShipmentResponse process() throws Exception;
}

class CircuitBreakerOpenException extends Exception {
    public CircuitBreakerOpenException(String message) {
        super(message);
    }
}</code></pre><h3>Practical Example: Shipment Service</h3><h4>Shipment Service with Circuit Breaker</h4><pre><code>public class ShipmentService {
    private final CircuitBreaker circuitBreaker;
    private final ShipmentProvider shipmentProvider;

    public ShipmentService() {
        // Circuit opens after 5 failures, waits 30 seconds before retry
        this.circuitBreaker = new CircuitBreaker(5, 30000);
        this.shipmentProvider = new ShipmentProvider();
    }

    public ShipmentResponse createShipment(String orderId, String destination, double weight) {
        try {
            // Execute shipment through circuit breaker
            ShipmentResponse response = circuitBreaker.executeShipment(() -&gt; {
                return shipmentProvider.createShipment(orderId, destination, weight);
            });
            return response;
        } catch (CircuitBreakerOpenException e) {
            // Circuit is open - service unavailable
            return new ShipmentResponse(false,
                "Shipment service temporarily unavailable. Please try again later.",
                null);
        } catch (Exception e) {
            // Shipment failed
            return new ShipmentResponse(false,
                "Shipment creation failed: " + e.getMessage(),
                null);
        }
    }
}
</code></pre><h4>Shipment Provider (External Service)</h4><pre><code>public class ShipmentProvider {
    public ShipmentResponse createShipment(String orderId, String destination, double weight) throws Exception {
        // Simulate network delay
        Thread.sleep(100);

        // Simulate random failures (30% chance)
        if (Math.random() &lt; 0.3) {
            throw new Exception("Shipment provider API timeout");
        }

        // Generate tracking number
        String trackingNumber = "TRK-" + System.currentTimeMillis();
        return new ShipmentResponse(true,
            "Shipment created to " + destination + " (" + weight + " kg)",
            trackingNumber);
    }
}</code></pre><h3>Putting It All Together</h3><pre><code>public class Main {
    public static void main(String[] args) {
        ShipmentService shipmentService = new ShipmentService();

        // Create multiple shipments
        for (int i = 1; i &lt;= 10; i++) {
            ShipmentResponse response = shipmentService.createShipment(
                "ORDER-" + i,
                "New York, NY",
                2.5);

            System.out.println("Shipment " + i + ":");
            System.out.println("  Success: " + response.isSuccess());
            System.out.println("  Message: " + response.getMessage());
            System.out.println("  Tracking Number: " + response.getTrackingNumber());
            System.out.println();

            try {
                Thread.sleep(500); // Wait between requests
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
</code></pre><h3>Example Output:</h3><pre><code>Shipment 1:
  Success: true
  Message: Shipment created to New York, NY (2.5 kg)
  Tracking Number: TRK-1234567890123

Shipment 2:
  Success: false
  Message: Shipment creation failed: Shipment provider API timeout
  Tracking Number: null

Shipment 3:
  Success: false
  Message: Shipment creation failed: Shipment provider API timeout
  Tracking Number: null

Shipment 4:
  Success: true
  Message: Shipment created to New York, NY (2.5 kg)
  Tracking Number: TRK-1234567890456

Shipment 5:
  Success: false
  Message: Shipment creation failed: Shipment provider API timeout
  Tracking Number: null

Shipment 6:
  Success: false
  Message: Shipment service temporarily unavailable. Please try again later.
  Tracking Number: null

... (circuit stays open for 30 seconds)</code></pre><h2>When to Use Circuit Breaker Pattern</h2><p>Understanding when to apply the Circuit Breaker pattern is crucial for making the right architectural decisions. Here's when it shines and when alternatives might be better:</p><h3>&#9989; Ideal Scenarios:</h3><ul><li><p>Your application depends on external services that can fail or become slow.</p></li><li><p>You need to prevent cascading failures across microservices.</p></li><li><p>You want to fail fast instead of waiting for timeouts on failing services.</p></li><li><p>You need to protect critical operations from resource exhaustion.</p></li><li><p>You're building distributed systems with multiple service dependencies.</p></li><li><p>You want automatic recovery testing when services become healthy again.</p></li></ul><h3>&#10060; Skip It When:</h3><ul><li><p>Your application has no external dependencies or service calls.</p></li><li><p>The overhead of circuit breaker management outweighs benefits.</p></li><li><p>You need guaranteed delivery for every single request <em>(use queues instead)</em>.</p></li><li><p>Your services have predictable, stable behaviour with no failure scenarios.</p></li><li><p>The added complexity doesn't justify the resilience benefits for your use case.</p></li></ul><h2>Production Deployment Considerations</h2><h3>1. Thread Safety for Concurrent Access</h3><p>For production applications handling concurrent requests, use thread safe variables <em>(<mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">AtomicInteger</mark>, <mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">AtomicLong</mark>)</em> instead of regular primitives to prevent race conditions when multiple threads access the circuit breaker simultaneously.</p><h3>2. Configuration and Monitoring</h3><p>Choose appropriate failure thresholds and timeout durations based on service criticality, and add comprehensive logging to track circuit state changes, failure counts, and recovery events for diagnosing issues in production.</p><h3>3. Fallback Strategies</h3><p>When the circuit is open, provide meaningful fallback responses such as cached data, default values, or user-friendly error messages instead of just throwing exceptions to maintain graceful degradation.</p><p>The Circuit Breaker pattern is fundamental to building resilient distributed systems. By implementing intelligent circuit protection with proper monitoring and fallback strategies, you can prevent cascading failures and maintain system stability even when individual services experience issues.</p><p><em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">Found this helpful? Share it with a colleague who's struggling with cascading failures in their microservices architecture. Got questions? We'd love to hear from you at </mark><a href="/cdn-cgi/l/email-protection#2e4d41405a4f4d5a6e5d4647485a4b424b584f5a4b004a4b58"><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">[email&nbsp;protected]</mark></a></em></p>]]></content:encoded></item><item><title><![CDATA[Primitive Obsession: Replace Primitive with Object Refactoring | Clean Code]]></title><description><![CDATA[Refactor the Primitive Obsession code smell with Replace Primitive with Object technique, improve domain modelling, and enhance type safety.]]></description><link>https://newsletter.shiftelevate.dev/p/primitive-obsession-replace-primitive-with-object-refactoring-clean-code</link><guid isPermaLink="false">https://newsletter.shiftelevate.dev/p/primitive-obsession-replace-primitive-with-object-refactoring-clean-code</guid><dc:creator><![CDATA[Shift Elevate]]></dc:creator><pubDate>Tue, 18 Nov 2025 06:35:05 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/cf25bdbd-202d-4717-923a-70cdc3a8a804_1200x630.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Primitive Obsession is a code smell where primitive types are used instead of small objects to represent domain concepts.</em></p><p>We will see how we can refactor using the Replace Primitive with Object technique for better domain modeling and type safety.</p><p>&#10077;</p><p><strong>&#9888;&#65039; Code Smell:</strong><span data-color="rgb(204, 204, 204)" style="color: rgb(204, 204, 204);"> </span><em>Primitive Obsession</em><br>&#9989;<strong> Refactoring:</strong><span data-color="rgb(204, 204, 204)" style="color: rgb(204, 204, 204);"> </span><em>Replace Primitive with Object</em><br>&#127919;<strong> Goal:</strong><span data-color="rgb(204, 204, 204)" style="color: rgb(204, 204, 204);"> </span><em>Rich domain models with meaningful types</em></p><p>The Primitive Obsession code smell occurs when primitive types (strings, numbers, booleans) are used to represent domain concepts instead of creating dedicated objects. The <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">Replace Primitive with Object refactoring</mark></em> technique replaces these primitives with meaningful objects that better represent the domain and provide type safety.</p><h2>The Code Smell: Primitive Obsession</h2><p>Primitive Obsession is a subtle but important code smell that reduces the expressiveness of your domain model. When you use primitive types to represent domain concepts, you lose the opportunity to encapsulate related behaviour and make your code more self-documenting. This leads to scattered validation logic and makes the code harder to understand and maintain.</p><p>Symptoms</p><p>Impact</p><p><em>Primitive types used for domain concepts</em></p><p><em>Reduced expressiveness</em></p><p><em>Scattered validation logic</em></p><p><em>Poor domain modeling</em></p><p><em>Type confusion and errors</em></p><p><em>Higher bug risk</em></p><h4>Here's a typical example of Primitive Obsession:</h4><pre><code>public class User {
    private String email;
    private String phoneNumber;
    private String zipCode;
    private double salary;
    private String status;

    public User(String email, String phoneNumber, String zipCode,
                double salary, String status) {
        this.email = email;
        this.phoneNumber = phoneNumber;
        this.zipCode = zipCode;
        this.salary = salary;
        this.status = status;
    }

    public boolean isValidEmail() {
        return email != null &amp;&amp; email.contains("@") &amp;&amp; email.contains(".");
    }

    public boolean isValidPhoneNumber() {
        return phoneNumber != null &amp;&amp; phoneNumber.matches("\\+?[1-9]\\d{1,14}");
    }

    public boolean isValidZipCode() {
        return zipCode != null &amp;&amp; zipCode.matches("\\d{5}(-\\d{4})?");
    }

    public boolean isActive() {
        return "ACTIVE".equals(status);
    }
}</code></pre><p>In this example, the <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">User</mark></em> class uses primitive types like <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">String</mark></em> for email, phone number, zip code, and status, and <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">double</mark></em> for salary. This creates several problems: validation logic is scattered, there's no type safety, and the domain concepts are not clearly expressed.</p><h2>The Refactoring: Replace Primitive with Object</h2><p>The Replace Primitive with Object refactoring technique creates dedicated objects to represent domain concepts, encapsulating related behaviour and providing type safety. This makes the code more expressive and easier to maintain.</p><h4>Step by Step Refactoring Process:</h4><ol><li><p><strong>Identify primitive types</strong> that represent domain concepts.</p></li><li><p><strong>Create dedicated classes</strong> for each domain concept.</p></li><li><p><strong>Move validation logic</strong> into the new classes.</p></li><li><p><strong>Replace primitive fields</strong> with the new objects.</p></li><li><p><strong>Update methods</strong> to work with the new objects.</p></li></ol><h4>Here's the refactored version:</h4><pre><code>// User class now uses meaningful objects
public class User {
    private Email email;
    private PhoneNumber phoneNumber;
    private ZipCode zipCode;
    private Money salary;
    private UserStatus status;

    public User(Email email, PhoneNumber phoneNumber, ZipCode zipCode,
                Money salary, UserStatus status) {
        this.email = email;
        this.phoneNumber = phoneNumber;
        this.zipCode = zipCode;
        this.salary = salary;
        this.status = status;
    }

    public boolean isActive() {
        return status.isActive();
    }
}

// Dedicated class for email with validation
public class Email {
    private final String value;

    public Email(String email) {
        if (!isValid(email)) {
            throw new IllegalArgumentException("Invalid email format: " + email);
        }
        this.value = email;
    }

    private boolean isValid(String email) {
        return email != null &amp;&amp; email.contains("@") &amp;&amp; email.contains(".");
    }

    public String getValue() {
        return value;
    }

    public String getDomain() {
        return value.substring(value.indexOf("@") + 1);
    }
}

// Dedicated class for phone numbers
public class PhoneNumber {
    private final String value;

    public PhoneNumber(String phoneNumber) {
        if (!isValid(phoneNumber)) {
            throw new IllegalArgumentException("Invalid phone number format: " + phoneNumber);
        }
        this.value = phoneNumber;
    }

    private boolean isValid(String phoneNumber) {
        return phoneNumber != null &amp;&amp; phoneNumber.matches("\\+?[1-9]\\d{1,14}");
    }

    public String getValue() {
        return value;
    }

    public String getCountryCode() {
        if (value.startsWith("+")) {
            return value.substring(1, 3);
        }
        return "US"; // Default assumption
    }
}

// Dedicated class for zip codes
public class ZipCode {
    private final String value;

    public ZipCode(String zipCode) {
        if (!isValid(zipCode)) {
            throw new IllegalArgumentException("Invalid zip code format: " + zipCode);
        }
        this.value = zipCode;
    }

    private boolean isValid(String zipCode) {
        return zipCode != null &amp;&amp; zipCode.matches("\\d{5}(-\\d{4})?");
    }

    public String getValue() {
        return value;
    }
}

// Dedicated class for money
public class Money {
    private final double amount;

    public Money(double amount) {
        if (amount &lt; 0) {
            throw new IllegalArgumentException("Amount cannot be negative");
        }
        this.amount = amount;
    }

    public double getAmount() {
        return amount;
    }

    public String getFormattedAmount() {
        return String.format("$%.2f", amount);
    }
}

// Enum for user status
public enum UserStatus {
    ACTIVE, INACTIVE, SUSPENDED, PENDING;
    
    public boolean isActive() {
        return this == ACTIVE;
    }
}</code></pre><h2>Benefits of Replace Primitive with Object</h2><p>Benefit</p><p>Description</p><p><em>Rich Domain Model</em></p><p><em>Objects represent domain concepts clearly, making the code more expressive and self-documenting.</em></p><p><em>Type Safety</em></p><p><em>Compile-time type checking prevents errors and makes the code more robust.</em></p><p><em>Encapsulated behaviour</em></p><p><em>Related validation and business logic is encapsulated within the appropriate objects.</em></p><h2>When to Apply Replace Primitive with Object Refactoring</h2><ul><li><p>Primitive types used to represent domain concepts <em>(email, phone, money, etc.)</em>.</p></li><li><p>Scattered validation logic for the same primitive type.</p></li><li><p>Multiple parameters of the same primitive type that could be confused.</p></li><li><p>Business logic that operates on primitive values.</p></li><li><p>When you want to make your domain model more expressive and type-safe.</p></li></ul><h2>Repository &amp; Resources</h2><p><strong>Complete Code Examples:</strong> <a href="https://github.com/shift-elevate/clean-code">Clean Code Repository</a></p><p>Find the complete implementation of Primitive Obsession refactoring and other clean code techniques in our dedicated repository. Each example includes:</p><ul><li><p>Before and after code comparisons</p></li><li><p>Unit tests demonstrating the improvements</p></li></ul><p><em>Found this helpful? Share it with a colleague who's struggling with Primitive Obsession. Got questions? We'd love to hear from you at <a href="/cdn-cgi/l/email-protection#04676b6a7065677044776c6d6270616861726570612a606172">[email&nbsp;protected]</a></em></p>]]></content:encoded></item><item><title><![CDATA[Iterator Pattern: Traverse Collections with Consistent Interface]]></title><description><![CDATA[Master the Iterator design pattern in Java with step by step implementation, music playlist system examples, and practical code for traversing collections with consistent interface.]]></description><link>https://newsletter.shiftelevate.dev/p/iterator-pattern-traverse-collections-with-consistent-interface</link><guid isPermaLink="false">https://newsletter.shiftelevate.dev/p/iterator-pattern-traverse-collections-with-consistent-interface</guid><dc:creator><![CDATA[Shift Elevate]]></dc:creator><pubDate>Sat, 15 Nov 2025 03:22:34 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/5c65b273-284e-4e45-b1f8-7b71da3f7547_1200x630.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>The Pain of Inconsistent Collection Access</h2><p><strong>Picture this:</strong> You're building a music playlist application that needs to support different types of collections: regular playlists, shuffled playlists, and recently played tracks. Your initial approach seems logical: create different methods for each collection type to access their elements.</p><p>But then requirements expand: "We need to support playlists with different sorting orders, filtered playlists, and playlists that combine multiple sources. Also, users want to be able to skip forward, go backward, and jump to specific positions in any playlist type."</p><p>Suddenly, you're creating different traversal methods for each collection type, making your code inconsistent and hard to maintain. Adding new collection types means creating new traversal logic, and clients need to know the specific implementation details of each collection. Sound familiar?</p><p>The <strong>Iterator</strong> pattern solves this by providing a way to access the elements of an aggregate object sequentially without exposing its underlying representation.</p><h2>Understanding the Iterator Pattern</h2><p><em>The Iterator pattern provides a way to access the elements of an aggregate object sequentially without exposing its underlying representation. It encapsulates the traversal logic and provides a consistent interface for iterating over different types of collections.</em></p><p>Think of it like a universal remote control for different types of media players: whether you're playing a CD, streaming music, or listening to a radio station, the remote provides the same buttons <em>(play, pause, next, previous)</em> even though the underlying mechanisms are completely different.</p><p>This pattern promotes <strong>Encapsulation</strong>, <strong>Consistency</strong>, and <strong>Flexibility</strong> while hiding the complexity of different collection implementations.</p><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mYLs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12eb5e87-49a3-4943-982e-a310b76eac81_710x510.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mYLs!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12eb5e87-49a3-4943-982e-a310b76eac81_710x510.png 424w, https://substackcdn.com/image/fetch/$s_!mYLs!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12eb5e87-49a3-4943-982e-a310b76eac81_710x510.png 848w, https://substackcdn.com/image/fetch/$s_!mYLs!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12eb5e87-49a3-4943-982e-a310b76eac81_710x510.png 1272w, https://substackcdn.com/image/fetch/$s_!mYLs!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12eb5e87-49a3-4943-982e-a310b76eac81_710x510.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mYLs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12eb5e87-49a3-4943-982e-a310b76eac81_710x510.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/12eb5e87-49a3-4943-982e-a310b76eac81_710x510.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!mYLs!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12eb5e87-49a3-4943-982e-a310b76eac81_710x510.png 424w, https://substackcdn.com/image/fetch/$s_!mYLs!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12eb5e87-49a3-4943-982e-a310b76eac81_710x510.png 848w, https://substackcdn.com/image/fetch/$s_!mYLs!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12eb5e87-49a3-4943-982e-a310b76eac81_710x510.png 1272w, https://substackcdn.com/image/fetch/$s_!mYLs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12eb5e87-49a3-4943-982e-a310b76eac81_710x510.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><p>Iterator Pattern Components</p><h3>Core Components</h3><ul><li><p><strong><a href="#iterator-interface">Iterator Interface</a></strong>: Defines the interface for accessing and traversing elements</p></li><li><p><strong><a href="#concrete-iterator">Concrete Iterator</a></strong>: Implements the iterator interface and keeps track of the current position</p></li><li><p><strong><a href="#aggregate-interface">Aggregate Interface</a></strong>: Defines the interface for creating an iterator object</p></li><li><p><strong><a href="#concrete-aggregate">Concrete Aggregate</a></strong>: Implements the aggregate interface and returns an appropriate iterator</p></li></ul><h2>Complete Java Implementation</h2><p>Let's build a music playlist system that demonstrates the Iterator pattern's power in providing consistent collection traversal.</p><h3>Iterator Interface</h3><pre><code>public interface SongIterator {
    boolean hasNext();
    Song next();
    Song current();
    void reset();
}

public class Song {
    private String title;
    private String artist;
    private String genre;

    public Song(String title, String artist, String genre) {
        this.title = title;
        this.artist = artist;
        this.genre = genre;
    }

    public String getTitle() { return title; }
    public String getArtist() { return artist; }
    public String getGenre() { return genre; }

    @Override
    public String toString() {
        return String.format("%s by %s [%s]", title, artist, genre);
    }
}</code></pre><h3>Aggregate Interface</h3><pre><code>public interface Playlist {
    SongIterator createIterator();
    void addSong(Song song);
    void removeSong(Song song);
    String getPlaylistName();
    int getSongCount();
}</code></pre><h3>Concrete Iterator</h3><pre><code>public class ShuffledPlaylistIterator implements SongIterator {
    private List&lt;Song&gt; songs;
    private int currentIndex;
    
    public ShuffledPlaylistIterator(List&lt;Song&gt; songs) {
        this.songs = new ArrayList&lt;&gt;(songs);
        shuffleSongs();
        this.currentIndex = 0;
    }
    
    private void shuffleSongs() {
        Collections.shuffle(songs);
    }
    
    @Override
    public boolean hasNext() {
        return currentIndex &lt; songs.size();
    }
    
    @Override
    public Song next() {
        if (hasNext()) {
            return songs.get(currentIndex++);
        }
        return null;
    }
    
    @Override
    public Song current() {
        if (currentIndex &gt; 0 &amp;&amp; currentIndex &lt;= songs.size()) {
            return songs.get(currentIndex - 1);
        }
        return null;
    }
    
    @Override
    public void reset() {
        shuffleSongs(); // Reshuffle on reset
        currentIndex = 0;
    }
}

public class FilteredPlaylistIterator implements SongIterator {
    private List&lt;Song&gt; filteredSongs;
    private int currentIndex;
    
    public FilteredPlaylistIterator(List&lt;Song&gt; songs, String filterGenre) {
        this.filteredSongs = songs.stream()
            .filter(song -&gt; song.getGenre().equalsIgnoreCase(filterGenre))
            .collect(Collectors.toList());
        this.currentIndex = 0;
    }
    
    @Override
    public boolean hasNext() {
        return currentIndex &lt; filteredSongs.size();
    }
    
    @Override
    public Song next() {
        if (hasNext()) {
            return filteredSongs.get(currentIndex++);
        }
        return null;
    }
    
    @Override
    public Song current() {
        if (currentIndex &gt; 0 &amp;&amp; currentIndex &lt;= filteredSongs.size()) {
            return filteredSongs.get(currentIndex - 1);
        }
        return null;
    }
    
    @Override
    public void reset() {
        currentIndex = 0;
    }
}</code></pre><h3>Concrete Aggregate</h3><pre><code>public class ShuffledPlaylist implements Playlist {
    private String name;
    private List&lt;Song&gt; songs;
    
    public ShuffledPlaylist(String name) {
        this.name = name;
        this.songs = new ArrayList&lt;&gt;();
    }
    
    @Override
    public SongIterator createIterator() {
        return new ShuffledPlaylistIterator(songs);
    }
    
    @Override
    public void addSong(Song song) {
        songs.add(song);
    }
    
    @Override
    public void removeSong(Song song) {
        songs.remove(song);
    }
    
    @Override
    public String getPlaylistName() {
        return name;
    }
    
    @Override
    public int getSongCount() {
        return songs.size();
    }
}

public class FilteredPlaylist implements Playlist {
    private String name;
    private List&lt;Song&gt; songs;
    private String filterGenre;
    
    public FilteredPlaylist(String name, String filterGenre) {
        this.name = name;
        this.songs = new ArrayList&lt;&gt;();
        this.filterGenre = filterGenre;
    }
    
    @Override
    public SongIterator createIterator() {
        return new FilteredPlaylistIterator(songs, filterGenre);
    }
    
    @Override
    public void addSong(Song song) {
        songs.add(song);
    }
    
    @Override
    public void removeSong(Song song) {
        songs.remove(song);
    }
    
    @Override
    public String getPlaylistName() {
        return name + " (Filtered: " + filterGenre + ")";
    }
    
    @Override
    public int getSongCount() {
        return (int) songs.stream()
            .filter(song -&gt; song.getGenre().equalsIgnoreCase(filterGenre))
            .count();
    }
}</code></pre><h3>Client</h3><pre><code>public class MusicPlayer {
    private Playlist currentPlaylist;
    private SongIterator iterator;
    private int songsPlayed;

    public void loadPlaylist(Playlist playlist) {
        this.currentPlaylist = playlist;
        this.iterator = playlist.createIterator();
        this.songsPlayed = 0;
        System.out.println("Loaded playlist: " + playlist.getPlaylistName());
        System.out.println("Total songs: " + playlist.getSongCount());
    }

    public void playNext() {
        if (iterator != null &amp;&amp; iterator.hasNext()) {
            Song song = iterator.next();
            songsPlayed++;
            System.out.println("Now playing: " + song);
            System.out.println("Progress: " + songsPlayed + "/" + currentPlaylist.getSongCount());
        } else {
            System.out.println("End of playlist reached");
        }
    }

    public void showCurrentSong() {
        if (iterator != null) {
            Song current = iterator.current();
            if (current != null) {
                System.out.println("Current song: " + current);
            } else {
                System.out.println("No song currently playing");
            }
        }
    }

    public void resetPlaylist() {
        if (iterator != null) {
            iterator.reset();
            songsPlayed = 0;
            System.out.println("Playlist reset to beginning");
        }
    }

    public void showPlaylistInfo() {
        if (currentPlaylist != null) {
            System.out.println("Playlist: " + currentPlaylist.getPlaylistName());
            System.out.println("Songs: " + currentPlaylist.getSongCount());
        }
    }
}

public class MusicPlayerDemo {
    public static void main(String[] args) {
        // Create sample songs
        List&lt;Song&gt; sampleSongs = Arrays.asList(
            new Song("Bohemian Rhapsody", "Queen", "Rock"),
            new Song("Imagine", "John Lennon", "Pop"),
            new Song("Hotel California", "Eagles", "Rock")
        );

        MusicPlayer player = new MusicPlayer();

        System.out.println("=== Music Player Iterator Pattern Demo ===\n");

        // Test Shuffled Playlist
        System.out.println("1. Shuffled Playlist:");
        System.out.println("====================");
        ShuffledPlaylist shuffledPlaylist = new ShuffledPlaylist("Shuffled Mix");
        sampleSongs.forEach(shuffledPlaylist::addSong);

        player.loadPlaylist(shuffledPlaylist);
        player.playNext();
        player.playNext();
        player.showCurrentSong();

        // Test Filtered Playlist
        System.out.println("\n2. Filtered Playlist (Rock only):");
        System.out.println("=================================");
        FilteredPlaylist rockPlaylist = new FilteredPlaylist("Rock Collection", "Rock");
        sampleSongs.forEach(rockPlaylist::addSong);

        player.loadPlaylist(rockPlaylist);
        player.playNext();
        player.playNext();

        // Test reset functionality
        System.out.println("\n3. Reset and Replay:");
        System.out.println("===================");
        player.resetPlaylist();
        player.playNext();
    }
}</code></pre><h4>Expected Output:</h4><pre><code>=== Music Player Iterator Pattern Demo ===

1. Shuffled Playlist:
====================
Loaded playlist: Shuffled Mix
Total songs: 3
Now playing: Hotel California by Eagles [Rock]
Progress: 1/3
Now playing: Imagine by John Lennon [Pop]
Progress: 2/3
Current song: Imagine by John Lennon [Pop]

2. Filtered Playlist (Rock only):
=================================
Loaded playlist: Rock Collection (Filtered: Rock)
Total songs: 2
Now playing: Bohemian Rhapsody by Queen [Rock]
Progress: 1/2
Now playing: Hotel California by Eagles [Rock]
Progress: 2/2

3. Reset and Replay:
===================
Playlist reset to beginning
Now playing: Bohemian Rhapsody by Queen [Rock]
Progress: 1/2</code></pre><h3>&#128640; Get the Complete Implementation</h3><p>The full code with advanced iterator implementations and collection management is available in our <a href="https://github.com/shift-elevate/design-patterns">Design Patterns Repository</a>.</p><pre><code># Clone and run the complete demo
git clone https://github.com/shift-elevate/design-patterns.git
cd design-patterns
mvn test -Dtest=IteratorPatternTest</code></pre><h2>Real World Examples</h2><p>The Iterator pattern is extensively used in real world applications:</p><h3>1. Java Collections Framework</h3><p>Java's Collections Framework uses the Iterator pattern extensively. The <em><mark data-color="#aff3fb" style="background-color: rgb(175, 243, 251); color: rgb(0, 0, 0);">Iterator</mark></em> interface provides a consistent way to traverse different types of collections <em>(ArrayList, LinkedList, HashSet, etc.)</em> without exposing their internal structure.</p><h3>2. Database Result Sets</h3><p>Database APIs use the Iterator pattern for result sets, allowing applications to traverse query results without knowing the specific database implementation or result set structure.</p><h3>3. File System Traversal</h3><p>File system APIs use the Iterator pattern to traverse directories and files, providing a consistent interface regardless of the underlying file system implementation.</p><h2>When to Use the Iterator Pattern</h2><p>Understanding when to apply the Iterator pattern is crucial for making the right architectural decisions. Here's when it shines and when alternatives might be better:</p><h3>&#9989; Ideal Scenarios:</h3><ul><li><p>You need to traverse different types of collections in a uniform way.</p></li><li><p>You want to hide the complexity of different collection implementations from clients.</p></li><li><p>You need to support multiple traversal algorithms for the same collection.</p></li><li><p>You want to provide a consistent interface for accessing collection elements.</p></li></ul><h3>&#10060; Skip It When:</h3><ul><li><p>The collection is simple and unlikely to change.</p></li><li><p>You need random access to collection elements.</p></li><li><p>The overhead of creating iterator objects is too high for your use case.</p></li></ul><h2>Next Steps: Apply Iterator Pattern in Your Project</h2><p>Ready to implement the Iterator pattern in your own projects? Here's a structured approach to get you started:</p><ol><li><p><strong>Identify Collection Types</strong>: Look for areas where you have different types of collections that need to be traversed.</p></li><li><p><strong>Define Iterator Interface</strong>: Create a common interface for traversing all collection types.</p></li><li><p><strong>Implement Concrete Iterators</strong>: Build specific iterators for each collection type.</p></li><li><p><strong>Create Aggregate Interface</strong>: Define an interface for creating appropriate iterators.</p></li><li><p><strong>Test Consistent Traversal</strong>: Ensure all collection types can be traversed using the same interface.</p></li></ol><p>The Iterator pattern transforms inconsistent collection access into uniform, maintainable traversal mechanisms. By providing a consistent iterator interface, you build systems that are easier to use and extend, regardless of the underlying collection implementation.</p><p><em>Found this helpful? Share it with a colleague who's struggling with inconsistent collection access. Got questions? We'd love to hear from you at <a href="/cdn-cgi/l/email-protection#b9dad6d7cdd8dacdf9cad1d0dfcddcd5dccfd8cddc97dddccf">[email&nbsp;protected]</a></em></p>]]></content:encoded></item></channel></rss>