You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java重构:Value转Reference与Reference转Value技术示例讲解请求

Let's break down these two refactoring techniques with concrete Java examples—they boil down to choosing whether an object should act as a value (immutable, compared by content) or a reference (unique, compared by identity).

1. Change Value to Reference

When to use this:

You have multiple instances of an object that represent the same real-world entity, and you need to:

  • Share a single instance across all usages
  • Modify the entity's state and have all usages see the updated state

Refactoring Before (Value Object)

A value object is immutable, has no identity beyond its content, and we create new instances even for identical data:

import java.math.BigDecimal;
import java.util.Objects;

public class Customer {
    private final String id;
    private final String name;

    public Customer(String id, String name) {
        this.id = id;
        this.name = name;
    }

    // Equality is based on content (id + name)
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Customer customer = (Customer) o;
        return Objects.equals(id, customer.id) && Objects.equals(name, customer.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }

    // Only getters—no way to modify state after creation
    public String getId() { return id; }
    public String getName() { return name; }
}

public class Order {
    private Customer customer;
    private BigDecimal amount;

    public Order(Customer customer, BigDecimal amount) {
        this.customer = customer;
        this.amount = amount;
    }

    public Customer getCustomer() { return customer; }
}

// Client code
public class Main {
    public static void main(String[] args) {
        // Two separate instances for the same customer
        Customer alice1 = new Customer("C001", "Alice");
        Order order1 = new Order(alice1, new BigDecimal("100.00"));

        Customer alice2 = new Customer("C001", "Alice");
        Order order2 = new Order(alice2, new BigDecimal("200.00"));

        // Same content, but different objects
        System.out.println(alice1 == alice2); // false
    }
}

Refactoring After (Reference Object)

We convert it to a reference object by ensuring only one instance exists per unique entity (using a repository pattern), making it mutable so changes propagate to all usages:

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

public class Customer {
    private String id;
    private String name;

    // Private constructor—only the repository can create instances
    private Customer(String id, String name) {
        this.id = id;
        this.name = name;
    }

    // Mutable: we can update the customer's name
    public void setName(String name) {
        this.name = name;
    }

    // Equality is now based on identity (id only)
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Customer customer = (Customer) o;
        return Objects.equals(id, customer.id);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id);
    }

    public String getId() { return id; }
    public String getName() { return name; }

    // Repository to manage shared instances
    public static class CustomerRepository {
        private static final Map<String, Customer> customers = new HashMap<>();

        public static Customer getCustomer(String id, String name) {
            // Reuse existing instance or create new if needed
            customers.computeIfAbsent(id, k -> new Customer(k, name));
            return customers.get(id);
        }
    }
}

public class Order {
    private Customer customer;
    private BigDecimal amount;

    public Order(Customer customer, BigDecimal amount) {
        this.customer = customer;
        this.amount = amount;
    }

    public Customer getCustomer() { return customer; }
}

// Client code
public class Main {
    public static void main(String[] args) {
        // Get the same instance from the repository
        Customer alice = Customer.CustomerRepository.getCustomer("C001", "Alice");
        Order order1 = new Order(alice, new BigDecimal("100.00"));

        Customer sameAlice = Customer.CustomerRepository.getCustomer("C001", "Alice");
        Order order2 = new Order(sameAlice, new BigDecimal("200.00"));

        // Same object instance
        System.out.println(alice == sameAlice); // true

        // Update the name—all orders see the change
        alice.setName("Alice Smith");
        System.out.println(order1.getCustomer().getName()); // Alice Smith
        System.out.println(order2.getCustomer().getName()); // Alice Smith
    }
}
2. Change Reference to Value

When to use this:

You have a reference object that's causing unexpected state sharing bugs, and you want:

  • Immutable objects that don't share state across usages
  • Changes to create new instances instead of modifying existing ones

Refactoring Before (Reference Object)

A reference object is mutable, shared across multiple contexts, leading to unintended side effects:

import java.math.BigDecimal;

public class Money {
    private BigDecimal amount;
    private String currency;

    public Money(BigDecimal amount, String currency) {
        this.amount = amount;
        this.currency = currency;
    }

    // Modifies the existing instance's state
    public void add(BigDecimal value) {
        this.amount = this.amount.add(value);
    }

    // Getters and setters allow direct state changes
    public BigDecimal getAmount() { return amount; }
    public void setAmount(BigDecimal amount) { this.amount = amount; }
    public String getCurrency() { return currency; }
    public void setCurrency(String currency) { this.currency = currency; }
}

public class ShoppingCart {
    private Money total;

    public ShoppingCart(Money total) {
        this.total = total;
    }

    public void addItem(BigDecimal price) {
        total.add(price);
    }

    public Money getTotal() { return total; }
}

// Client code
public class Main {
    public static void main(String[] args) {
        Money initialTotal = new Money(BigDecimal.ZERO, "USD");
        ShoppingCart cart1 = new ShoppingCart(initialTotal);
        cart1.addItem(new BigDecimal("50.00"));

        // Reuse the same Money instance for another cart
        ShoppingCart cart2 = new ShoppingCart(initialTotal);
        cart2.addItem(new BigDecimal("30.00"));

        // Unintended side effect: both carts show $80 total
        System.out.println(cart1.getTotal().getAmount()); // 80.00
        System.out.println(cart2.getTotal().getAmount()); // 80.00
    }
}

Refactoring After (Value Object)

We convert it to a value object by making it immutable—all "changes" return a new instance, eliminating shared state:

import java.math.BigDecimal;
import java.util.Objects;

public class Money {
    private final BigDecimal amount;
    private final String currency;

    public Money(BigDecimal amount, String currency) {
        this.amount = amount;
        this.currency = currency;
    }

    // Returns a NEW Money instance instead of modifying this one
    public Money add(BigDecimal value) {
        return new Money(this.amount.add(value), this.currency);
    }

    // Equality based on content (amount + currency)
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Money money = (Money) o;
        return Objects.equals(amount, money.amount) && Objects.equals(currency, money.currency);
    }

    @Override
    public int hashCode() {
        return Objects.hash(amount, currency);
    }

    // Only getters—no way to modify state after creation
    public BigDecimal getAmount() { return amount; }
    public String getCurrency() { return currency; }
}

public class ShoppingCart {
    private Money total;

    public ShoppingCart(Money total) {
        this.total = total;
    }

    public void addItem(BigDecimal price) {
        // Replace the total with a new Money instance
        this.total = this.total.add(price);
    }

    public Money getTotal() { return total; }
}

// Client code
public class Main {
    public static void main(String[] args) {
        Money initialTotal = new Money(BigDecimal.ZERO, "USD");
        ShoppingCart cart1 = new ShoppingCart(initialTotal);
        cart1.addItem(new BigDecimal("50.00"));

        ShoppingCart cart2 = new ShoppingCart(initialTotal);
        cart2.addItem(new BigDecimal("30.00"));

        // No more side effects—each cart has its own independent total
        System.out.println(cart1.getTotal().getAmount()); // 50.00
        System.out.println(cart2.getTotal().getAmount()); // 30.00
    }
}

Quick Recap

  • Change Value to Reference: Use when you need shared state and identity (e.g., a customer that multiple orders reference, where updates should reflect everywhere).
  • Change Reference to Value: Use when you want isolated, immutable state (e.g., money amounts, where changes to one shouldn't affect others).

内容的提问来源于stack exchange,提问作者user1298426

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:02:15