Temporary Fields are instance variables that are only used in certain circumstances, making the class interface confusing.
We will see how we can refactor using the Extract Class technique to organize temporary fields into dedicated classes.
Clean Code Reference
⚠️ Code Smell: Temporary Field
✅ Refactoring: Extract Class
🎯 Goal: Clear class interfaces and better field organization
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 Extract Class refactoring technique moves these temporary fields into dedicated classes that better represent their purpose.
The Code Smell: Temporary Field
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.
Symptoms | Impact |
|---|---|
Fields only used in certain methods | Confusing class interface |
Unclear object state | Difficult to understand |
Fields initialized to null or zero | Higher bug risk |
Here's a typical example of Temporary Field:
public class OrderProcessor {
// Core order fields
private String orderId;
private Customer customer;
private List<OrderItem> 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<OrderItem> 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 -> item.getPrice() * item.getQuantity())
.sum();
}
}In this example, OrderProcessor has three groups of temporary fields: discount fields (appliedDiscountCode, discountAmount), payment fields (paymentProcessed, transactionId), and shipment fields (shippingAddress, trackingNumber). These fields are only relevant during specific processing stages, yet they clutter the class interface throughout the object's lifetime.
The Refactoring: Extract Class
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.
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.
Step by Step Refactoring Process:
Identify temporary fields that are only used in specific contexts.
Group related temporary fields that are used together.
Create new classes to represent these contexts.
Move temporary fields into the appropriate classes.
Update methods to work with the new classes.
Here's the refactored version:
The temporary fields have been organized into dedicated classes:
OrderProcessor - Refactored main class
DiscountInfo - Extracted discount data
PaymentInfo - Extracted payment data
ShipmentInfo - Extracted shipment data
OrderProcessor - Refactored Main Class
public class OrderProcessor {
private final String orderId;
private final Customer customer;
private final List<OrderItem> items;
private final double totalAmount;
private DiscountInfo discountInfo;
private PaymentInfo paymentInfo;
private ShipmentInfo shipmentInfo;
public OrderProcessor(String orderId, Customer customer, List<OrderItem> 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 -> item.getPrice() * item.getQuantity())
.sum();
}
}
DiscountInfo - Extracted Discount Data
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);
}
}
PaymentInfo - Extracted Payment Data
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);
}
}
ShipmentInfo - Extracted Shipment Data
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);
}
}
Usage Example - Putting It All Together
Notice how the refactored version transforms 6 scattered temporary fields into 3 focused objects. The OrderProcessor constructor no longer initializes fields to null or zero, each object is created only when its processing stage begins.
When you access paymentInfo.getTransactionId() or shipmentInfo.getTrackingNumber(), 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.
public class OrderProcessingDemo {
public static void main(String[] args) {
System.out.println("=== Order Processing System ===\n");
Customer customer = new Customer("johndoe");
List<OrderItem> 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());
}
}
Expected Output:
=== 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'}
Benefits of Extract Class for Temporary Fields
Benefit | Description |
|---|---|
Clearer Class Interface | The main class interface is cleaner and more focused on its core responsibilities. |
Better State Management | Temporary fields are organized into logical groups with clear purposes. |
Improved Maintainability | Related functionality is grouped together, making it easier to understand and modify. |
When to Apply Extract Class for Temporary Fields
Instance variables that are only used in specific methods or contexts.
Fields that are set but not always used, making the object's state unclear.
Classes with many temporary fields that represent different aspects of processing.
When the class interface becomes confusing due to too many fields.
When you find yourself initializing many fields to default values that might not be used.
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.
Repository & Resources
Complete Code Examples: Clean Code Repository
Find the complete implementation of Temporary Field refactoring and other clean code techniques in our dedicated repository. Each example includes:
Before and after code comparisons
Unit tests demonstrating the improvements
Found this helpful? Share it with a colleague who's struggling with Temporary Fields. Got questions? We'd love to hear from you at [email protected]

