Skip to Main Content

Java SE (Java Platform, Standard Edition)

Announcement

For appeals, questions and feedback about Oracle Forums, please email oracle-forums-moderators_us@oracle.com. Technical questions should be asked in the appropriate category. Thank you!

Creational Patterns in My Daily Code: Why I Stopped Using new Everywhere

Martin HexAug 14 2025

Amsterdam, April 2024. Rainy. Coffee cold. I’m debugging some legacy code at my first real job, and I keep seeing these weird factory classes that don’t actually do anything. They just… return other objects. I’m like, “Why not just call new and get it over with?” Then my teammate says: “You’re thinking in actions, not in patterns.” That hit harder than my morning espresso.

Turns out, there’s a whole world of creational patterns — ways to create objects without tightly coupling your code to specific classes. I’ve been using a few of them lately, not because I read a book and said “aha!”, but because my code kept breaking when I tried to add new features. So I dug into this lecture my mentor sent me, and honestly? It changed how I write Java.

Let me walk you through the ones I actually use — not in theory, but in the messy reality of my IDE.

Factory Method: When You Don’t Know What You’re Building

The Factory Method is basically a promise: “I’ll give you an object that does what you need, but I won’t tell you exactly which one — that’s up to the subclass.”

I used this when I was building a notification system. We had to send alerts via email, SMS, or push. At first, I had a big if-else chain checking the user’s preference and calling new EmailNotifier(), new SMSNotifier(), etc. Ugly. Hard to test. Impossible to extend.

Then I switched to Factory Method. I made an abstract class NotifierFactory with a method createNotifier(). Each subclass — EmailNotifierFactory, SMSNotifierFactory — implemented it. The client just called factory.createNotifier().send() and didn’t care about the details.

It felt weird at first — like I was adding layers just to avoid typing new. But when we added Slack notifications, I just made a new factory. No changes to existing code. That’s the win.

Simple example:

public abstract class NotifierFactory {
    public final Notifier getNotifier() {
        Notifier notifier = createNotifier();
        notifier.setLogger(getLogger());
        return notifier;
    }
    protected abstract Notifier createNotifier();
    protected Logger getLogger() { return Logger.getInstance(); }
}

public class EmailNotifierFactory extends NotifierFactory {
    @Override
    protected Notifier createNotifier() {
        return new EmailNotifier("smtp.mycorp.com", 587);
    }
}

Now the base factory can handle cross-cutting concerns (like logging), while subclasses focus only on object creation. The client doesn’t even know which factory it’s using — just that it gets a ready-to-use Notifier.

Abstract Factory: When You Need a Whole Family

Abstract Factory is like Factory Method’s older, more organized sibling. It doesn’t make one object — it makes a set of related objects that belong together.

I used this when we had different “themes” for our app — light, dark, and high-contrast. Each theme needed its own button, font, and icon classes. Instead of mixing them up in conditionals, I made a ThemeFactory interface with methods like createButton(), createFont(), etc.

Each theme (LightThemeFactory, DarkThemeFactory) implemented the full set. Now, when the user switches themes, the whole UI changes consistently because all components come from the same factory family.

It’s not just about UI. Think databases: one factory for MySQL components, another for PostgreSQL. You swap the factory, and everything inside stays compatible.

Simple example:

public interface UIFactory {
    Button createButton();
    Font createFont();
    Icon createIcon();
}

public class DarkThemeFactory implements UIFactory {
    public Button createButton() { return new RoundedButton("dark-gray"); }
    public Font createFont() { return new Font("Roboto", 14); }
    public Icon createIcon() { return new FlatIcon("dark"); }
}

// Client code
UIFactory factory = new DarkThemeFactory();
Button btn = factory.createButton();
Font font = factory.createFont();

The client doesn’t care about the theme — it just asks for components. If we later add a HighContrastFactory, no existing code breaks. That’s the power of abstraction.

Builder: When Object Creation Gets Out of Hand

Builder is for when your constructor has six parameters, and three of them are null because you’re not using them this time. You know the pain.

I had a Report class. It could include data, charts, filters, export formats, headers, footers… At some point, I had eight constructors. It was a mess.

Builder fixed that. I moved all that logic into a ReportBuilder class with fluent methods: .withData(), .withChart(), .build(). The actual Report object is only created at the end, fully formed.

The best part? I can have different builders for different report types — like a PDFReportBuilder or DashboardReportBuilder — all following the same interface. The Director (if I use one) just calls the steps in order, and the right report comes out.

Simple example:

public interface ReportBuilder {
    void buildHeader();
    void buildData();
    void buildChart();
    void buildFooter();
    Report getReport();
}

public class PDFReportBuilder implements ReportBuilder {
    private Report report = new Report();

    public void buildHeader() { report.setHeader("Monthly Summary"); }
    public void buildData() { report.setData(fetchMonthlyData()); }
    public void buildChart() { report.setChart(new BarChart()); }
    public void buildFooter() { report.setFooter("Generated: " + LocalDate.now()); }

    public Report getReport() { return report; }
}

// Director
public class ReportDirector {
    public Report construct(ReportBuilder builder) {
        builder.buildHeader();
        builder.buildData();
        builder.buildChart();
        builder.buildFooter();
        return builder.getReport();
    }
}

Now the Director controls the process, but the Builder controls the product. Want a simpler version? Make a SimpleReportBuilder. The Director doesn’t care — it just runs the steps.

Singleton: The One You Love to Hate

Singleton is controversial. Some devs call it an anti-pattern. I get it — global state is dangerous. But sometimes, you really only want one instance.

I used it for a logging service. We didn’t want ten loggers writing to the same file at once. So I made a Logger class with a private constructor and a static getInstance() method. First call creates it. Every call after that returns the same instance.

Yes, it’s global. Yes, it’s hard to test. But in this case, it made sense. And in Java, you can make it thread-safe with synchronized or use enum-based singletons — that’s a neat trick.

Simple example:

public class Logger {
    private static volatile Logger instance;

    private Logger() {}

    public static Logger getInstance() {
        if (instance == null) {
            synchronized (Logger.class) {
                if (instance == null) {
                    instance = new Logger();
                }
            }
        }
        return instance;
    }

    public void log(String level, String message) {
        System.out.printf("[%s] %s - %s%n", level.toUpperCase(), new Date(), message);
    }
}

This double-checked locking ensures thread safety without killing performance. It’s not perfect, but it works — and sometimes, that’s enough.

Conclusion.

Honestly, I started using creational patterns when my own code began to fail me. Factories, builders, singletons — they’re not magic. They’re solutions born from real frustration: duplicated logic, brittle constructors, tests breaking for no good reason.

Now I don’t force patterns into my code. But when I see a long chain of `if-else` just to create an object, or a constructor with half a dozen parameters, I pause. I ask: *Can I hide the creation? Can I separate the “how” from the “what”?*

In Java, these patterns fit well. Interfaces, inheritance, encapsulation — they make it easy to structure object creation cleanly. The key isn’t to use every pattern, but to notice when your code resists change. That’s usually the moment a creational pattern can help.

If you’ve read this far — I’d love to hear your thoughts. Drop a line in the below. How do you handle object creation in your projects? Do you reach for patterns early, or only when things get messy? Have you ever regretted using a factory… or not using one?

Let’s talk. I’m still learning.

Comments
Post Details
Added on Aug 14 2025
0 comments
26 views