Entwurfsmuster in Java: Eine ausführliche Anleitung
What is it and why? (Real-life analogy)
Imagine you are building a house. You could reinvent the wheel every time: figure out how to connect logs, how to make a roof, how to run electricity. Or you could use ready-made blueprints and solutions that thousands of builders before you have already tested. Design patterns are exactly these ready-made "blueprints" for programmers.
In real life, we constantly use patterns without even noticing it. For example:
- The queue at a cafe — this is the "Queue" pattern. People arrive, stand at the end, and are served in order.
- The TV remote control — this is the "Command" pattern. You press a button, and the TV performs an action without knowing how the button is actually constructed.
- Lego builder — this is the "Builder" pattern. You take ready-made parts and assemble a complex structure from them.
In programming, patterns solve typical problems. For example, you need only one object in the program to connect to the database. Or you need different parts of the program to communicate without knowing about each other. Or you need to be able to add new features without rewriting old code.
Important: Patterns are not ready-made code, but a concept. You can implement the same pattern in different ways, but the structure will remain the same.
Patterns are divided into three large groups:
- Creational — responsible for creating objects (how to ensure objects are created correctly)
- Structural — responsible for building relationships between objects (how to assemble objects into a working system)
- Behavioral — responsible for interaction between objects (how objects communicate with each other)
Theory: how it works internally
Let's figure out what happens "under the hood" of patterns. Each pattern is a set of classes and interfaces that interact in a specific way. In Java, this looks like regular classes, but with a clear structure.
The main elements of any pattern:
- Context — the situation in which the pattern is applied
- Problem — what exactly we want to solve
- Solution — how the pattern proposes to do it
For example, the "Singleton" pattern solves the problem: "How to ensure that a class has only one instance?". Solution: hide the constructor (make it private) and create a static method that returns the single object.
The "Observer" pattern solves the problem: "How can some objects learn about changes in other objects?". Solution: the publisher object stores a list of subscribers and notifies them about events.
It is important to understand: patterns are not tied to a specific language. But Java has its own peculiarities. For example, Java does not have functions as first-class objects (like in Python), so to implement some patterns you have to use interfaces and anonymous classes.
Syntax in Java: all forms and variants
In Java, patterns are implemented using standard language tools: classes, interfaces, abstract classes, inheritance, and polymorphism. Let's look at the main syntactic constructions used in patterns.
Interfaces
An interface is a contract. A class that implements an interface must implement all its methods.
// Declaring an interface
interface Observable {
void update(String message);
}
// Class implements the interface
class User implements Observable {
private String name;
User(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name + " received: " + message);
}
}
Abstract classes
An abstract class is a class that cannot be instantiated directly. It can contain both abstract methods (without implementation) and regular ones.
abstract class Shape {
protected String color;
Shape(String color) {
this.color = color;
}
// Abstract method — needs to be implemented in the subclass
abstract double getArea();
// Regular method — already ready
void displayColor() {
System.out.println("Color: " + color);
}
}
class Circle extends Shape {
private double radius;
Circle(String color, double radius) {
super(color);
this.radius = radius;
}
@Override
double getArea() {
return Math.PI * radius * radius;
}
}
Static methods and fields
Static members belong to the class, not the object. This is a key element for the "Singleton" pattern.
class Database {
private static Database instance;
private Database() {
// Private constructor
}
public static Database getInstance() {
if (instance == null) {
instance = new Database();
}
return instance;
}
}
Inner and anonymous classes
In Java, you can create classes inside other classes. This is convenient for patterns that require small helper classes.
class EventManager {
interface EventListener {
void onEvent(String event);
}
private EventListener listener;
void setListener(EventListener listener) {
this.listener = listener;
}
void triggerEvent(String event) {
if (listener != null) {
listener.onEvent(event);
}
}
}
// Usage with an anonymous class
EventManager manager = new EventManager();
manager.setListener(new EventManager.EventListener() {
@Override
public void onEvent(String event) {