DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

SBOMs are essential to circumventing software supply chain attacks, and they provide visibility into various software components.

Related

  • Jakarta EE 11 and the Road Ahead With Jakarta EE 12
  • Effective Exception Handling in Java and Spring Boot Applications
  • How to Identify the Underlying Causes of Connection Timeout Errors for MongoDB With Java
  • How to Introduce a New API Quickly Using Micronaut

Trending

  • Exploring Data Redaction Enhancements in Oracle Database 23ai
  • ZapBot: Building a Chatbot With OpenAI and Gradio
  • When Caches Collide: Solving Race Conditions in Fare Updates
  • Analysis of the Data Processing Framework of Pandas and Snowpark Pandas API
  1. DZone
  2. Coding
  3. Java
  4. Squid Game: The Clean Code Trials — A Java Developer's Survival Story

Squid Game: The Clean Code Trials — A Java Developer's Survival Story

Learn about clean coding techniques to refactor rigid Java methods, embrace patterns like Strategy, avoid anti-patterns, and craft future-proof software.

By 
Shaamik Mitraa user avatar
Shaamik Mitraa
·
Jul. 01, 25 · Analysis
Likes (5)
Comment
Save
Tweet
Share
3.5K Views

Join the DZone community and get the full member experience.

Join For Free

"In the world of code, there are only two outcomes: evolve or perish."

Episode 1: The Red Light of Rigid Code

Scene: A vast ODC filled with desks, whiteboards, and terminals. The chairs are marked with red and green stickers. Software contestants sitting, trembling, gazing at a giant robot doll that watches them intently.

A loudspeaker booms:

"Your first challenge: implement a discount calculator. It must work today... and still work tomorrow."

Contestant #101, a confident junior developer, leans on the terminal and types:

Java
 
 public class DiscountCalculator {
    public double calculate(String customerType, double amount, String couponCode, int orderCount, LocalDateTime firstOrderDate) {
        double discount = 0;

        if ("REGULAR".equals(customerType)) {
            discount = amount * 0.1;
            if ("FEST10".equalsIgnoreCase(couponCode)) {
                discount += 10;
            }
        } else if ("PREMIUM".equals(customerType)) {
            discount = amount * 0.15;
            if ("FEST10".equalsIgnoreCase(couponCode)) {
                discount += 20;
            }
        } else if ("VIP".equals(customerType)) {
            discount = amount * 0.25;
            if (orderCount > 5 && firstOrderDate.isBefore(LocalDateTime.now().minusYears(2))) {
                discount += 50;
            }
        }

        if (discount > 100) {
            discount = 100; // cap
        }

        System.out.println("Discount: " + discount);
        return discount;
    }
}


The doll's eyes turn red. A siren blares.

Boom. Eliminated.

Why This Code Died: Exposing Anti-Patterns

1. God Class / God Method

One class or method tries to handle all responsibilities. That’s a sign it’s doing too much and is hard to maintain. 

Scene: Flashback — Contestant 101 in the waiting area.

Developer Mentor: "You stacked all logic in one method. You became the God... and the God fell."

  • Mixes validation, business logic, coupon logic, and logging.
  • Hard to test specific paths in isolation.
  • Not KISS (Keep It Simple & Stupid).

2. Magic Strings and Constants

Scene: Flashback — A string typo breaks production.

Developer Intern: "I typed 'PremIum' instead of 'PREMIUM'... 11,000 customers got no discount."

  • Strings like "REGULAR", "FEST10", "VIP" are error-prone and scattered.
  • No compile-time safety, hard to change later.

3. If-Else Tower (No Polymorphism)

Scene: Front Man shows an old dusty code file with 43 else-ifs.

Front Man: "The longer the tower, the harder it is to escape."

  • Adding a new type (e.g., “WHOLESALE”) means editing the method — violates OCP (Open/Close Principal.)
  • Code becomes unreadable.

4. Tight Coupling

  • Logic is tied to exact string values.
  • System.out in business logic — violates the separation of concerns.

5. Not Testable

  • How will you unit test VIP logic alone?
  • Everything is entangled.

6. No Reusability or Extensibility

  • You can't plug this into a different pricing engine.

Refactor: Code that Survives the Next Round

Scene: Developer's Resurrection

Mentor: "You're not out. Refactor. And survive."

Step 1: Create Enum for CustomerType

Java
 
public enum CustomerType {
    REGULAR, PREMIUM, VIP;

    public static CustomerType from(String value) {
        try {
            return CustomerType.valueOf(value.toUpperCase());
        } catch (Exception e) {
            throw new IllegalArgumentException("Invalid customer type: " + value);
        }
    }
}


Step 2: Use Strategy Pattern

Java
 
public interface DiscountStrategy {
    CustomerType type();
    double calculate(double amount, String couponCode, int orderCount, LocalDateTime firstOrderDate);
}


Implement a few strategies:

Java
 
public class RegularDiscount implements DiscountStrategy {
    public CustomerType type() { return CustomerType.REGULAR; }
    public double calculate(double amount, String couponCode, int orderCount, LocalDateTime firstOrderDate) {
        double discount = amount * 0.1;
        if ("FEST10".equalsIgnoreCase(couponCode)) discount += 10;
        return discount;
    }
}

public class PremiumDiscount implements DiscountStrategy {
    public CustomerType type() { return CustomerType.PREMIUM; }
    public double calculate(double amount, String couponCode, int orderCount, LocalDateTime firstOrderDate) {
        double discount = amount * 0.15;
        if ("FEST10".equalsIgnoreCase(couponCode)) discount += 20;
        return discount;
    }
}

public class VipDiscount implements DiscountStrategy {
    public CustomerType type() { return CustomerType.VIP; }
    public double calculate(double amount, String couponCode, int orderCount, LocalDateTime firstOrderDate) {
        double discount = amount * 0.25;
        if (orderCount > 5 && firstOrderDate.isBefore(LocalDateTime.now().minusYears(2))) {
            discount += 50;
        }
        return discount;
    }
}


Step 3: Make It Generic and Plug With Lambdas

Java
 
public class StrategyRegistry<T, R> {
    private final Map<T, Function<R, Double>> strategies = new HashMap<>();

    public void register(T key, Function<R, Double> strategyFunc) {
        strategies.put(key, strategyFunc);
    }

    public double apply(T key, R request) {
        return strategies.getOrDefault(key, r -> 0.0).apply(request);
    }
}


Scene: The Intern's Orientation 

(A new intern enters the Squid Game Arena's code lab, shaking with nervousness. Developer Mentor 218 greets him with a calm voice.)   

Intern:

“Sir… what is this StrategyRegistry thing? And… why use these lambdas and enums instead of just if-else?”

Mentor 218 (sips tea and opens laptop):

“Let me show you. This is how we survive chaos.”

What Is StrategyRegistry<T, R>?

It’s a generic plug-and-play engine that avoids the "god method" syndrome. You pass:

  • T: A key — in our case, CustomerType.
  • R: A request payload — here, DiscountRequest.

Each strategy is a function:

  • Function<DiscountRequest, Double>

And we store them in a map:   

  • Map<CustomerType, Function<DiscountRequest, Double>> strategies

Instead of giant if-else logic like:   

Java
 
if(customerType.equals("PREMIUM")) { ... }


We now just do:   

Java
 
registry.apply(customerTypeEnum, request);


Registering Discount Strategies Using Lambdas

Java
 
StrategyRegistry<CustomerType, DiscountRequest> registry = new StrategyRegistry<>();

registry.register(CustomerType.REGULAR, req -> {
    double discount = req.amount() * 0.1;
    if ("FEST10".equalsIgnoreCase(req.couponCode())) discount += 10;
    return discount;
});

registry.register(CustomerType.PREMIUM, req -> {
    double discount = req.amount() * 0.15;
    if ("FEST10".equalsIgnoreCase(req.couponCode())) discount += 20;
    return discount;
});

registry.register(CustomerType.VIP, req -> {
    double discount = req.amount() * 0.25;
    if (req.orderCount() > 5 && req.firstOrderDate().isBefore(LocalDateTime.now().minusYears(2))) {
        discount += 50;
    }
    return discount;
});


Why Use Functional Programming?

1. No New Classes for Every Variation

  • Avoids boilerplate like PremiumDiscountStrategy implements DiscountStrategy
  • Lambdas are lightweight and in-place

2. Easier to Test

  • Each lambda can be tested in isolation.
  • You can inject different behaviors in tests.

3. Open for Extension, Closed for Modification (OCP)

  • Add a new type without touching existing logic.
  • Register strategy on startup, config, or runtime.

4. Reusability and Modularity

  • Your registry becomes a plugboard. Great for rule engines!

Magic Strings: Death by Literal Comparison

The original code had:

Java
 
if ("FEST10".equalsIgnoreCase(couponCode)) ...


Instead of "FEST10" scattered everywhere:   

1. Create a property-based config:   

Java
 
discount.coupon.festive = FEST10


2. Bind it to a configuration class: If you are using a Spring Boot-like framework. 

Java
 
@ConfigurationProperties(prefix = "discount.coupon")
public class DiscountCouponProperties {
    private String festive;

    public String getFestive() {
        return festive;
    }

    public void setFestive(String festive) {
        this.festive = festive;
    }
}


3. Inject it where you define strategy:   

Java
 
String fest10 = couponProperties.getFestive();

registry.register(CustomerType.REGULAR, req -> {
    double discount = req.amount() * 0.1;
    if (fest10.equalsIgnoreCase(req.couponCode())) discount += 10;
    return discount;
});


Updated Registry Registration (More Generic and Clean)  

Java
 
@Autowired
DiscountCouponProperties couponProperties;

...

registry.register(CustomerType.VIP, req -> {
    double discount = req.amount() * 0.25;
    boolean loyal = req.orderCount() > 5 &&
        req.firstOrderDate().isBefore(LocalDateTime.now().minusYears(2));
    if (loyal) discount += 50;
    return discount;
});


Refactored Strategy class:

Java
 
public class DiscountStrategyRegistration {
 
    public static StrategyRegistry<CustomerType, DiscountRequest> createRegistry() {
        StrategyRegistry<CustomerType, DiscountRequest> registry = new StrategyRegistry<>();
		@Autowired
 		DiscountCouponProperties couponProperties;       

        registry.register(CustomerType.REGULAR, req -> {
            double discount = req.amount() * 0.1;
            if (couponProperties.getFestive().equalsIgnoreCase(req.couponCode())) discount += 10;
            return discount;
        });

        registry.register(CustomerType.PREMIUM, req -> {
            double discount = req.amount() * 0.15;
            if (couponProperties.getFestive().equalsIgnoreCase(req.couponCode())) discount += 20;
            return discount;
        });

        registry.register(CustomerType.VIP, req -> {
            double discount = req.amount() * 0.25;
            if (req.orderCount() > 5 && req.firstOrderDate().isBefore(LocalDateTime.now().minusYears(2))) {
                discount += 50;
            }
            return discount;
        });

        return registry;
    }
}


Final Refactored Calculator

Java
 
package com.example.javaonfly.antipattern.discount;

import java.time.LocalDateTime;

public class DiscountCalculator {
    private final StrategyRegistry<CustomerType, DiscountRequest> registry;

    public DiscountCalculator(StrategyRegistry<CustomerType, DiscountRequest> registry) {
        this.registry = registry;
    }

    public double calculate(String customerTypeStr, double amount, String couponCode, int orderCount, LocalDateTime firstOrderDate) {
        CustomerType type = CustomerType.from(customerTypeStr);
        DiscountRequest req = new DiscountRequest(amount, couponCode, orderCount, firstOrderDate);
        return Math.min(100, registry.apply(type, req));
    }


    public static void main(String[] args) {
        DiscountCalculator calculator = new DiscountCalculator(DiscountStrategyRegistration.createRegistry());
        double value = calculator.calculate("REGULAR",100,"FEST10",1,null);
        System.out.println("Discount calculated :: " + value);
    }
}
//output
Discount calculated :: 20.0


Scene Epilogue

Contestant 456: "So... you don’t write code for today?"

Front Man: "No. We write for tomorrow. For change. For chaos. And we survive."

Contestant 212: "What if change never comes?"

Front Man: "Then your clean code will never break. Isn’t that peace?"

Clean Code Checklist From Episode 1

Anti-Pattern Death Cause Cure
God Class All logic in one SRP: Break into strategies
Magic Strings Typos & unclear Enums and constants
SRP Violation One class, many jobs Delegate to strategy classes
OCP Violation Editing old code Plug strategy via registry
Unreadable Code Future devs hate it Descriptive interfaces
Hard to Test No unit slices Modular + Generic strategies

"Survive by writing clean code that’s ready for change — not written in fear of it."

Java (programming language) Spring Boot Strings

Opinions expressed by DZone contributors are their own.

Related

  • Jakarta EE 11 and the Road Ahead With Jakarta EE 12
  • Effective Exception Handling in Java and Spring Boot Applications
  • How to Identify the Underlying Causes of Connection Timeout Errors for MongoDB With Java
  • How to Introduce a New API Quickly Using Micronaut

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • [email protected]

Let's be friends: