枚举(Enums)

枚举(Enums)

想象你正在搭建一个电商平台,需要跟踪每个订单的状态。订单会经历多个状态:已下单(placed)、已确认(confirmed)、已发货(shipped)、已送达(delivered)或已取消(cancelled)。你该如何在代码中表示这些状态?

你可以使用字符串:"PLACED""SHIPPED""DELIVERED"。但如果有人把 "SHIPPED" 误写成 "Shiped",会发生什么?

编译器不会捕获这个错误,你的代码会在运行时悄然失败。

你也可以使用整数:1 表示已下单、2 表示已发货、3 表示已送达。但这样一来,你的代码里就会充斥着「魔法数字」。if (status == 2) 究竟是什么意思?

而这正是 枚举(enum) 要解决的问题。

java 复制代码
编程资源
https://pan.quark.cn/s/7f7c83756948
更多资源
https://pan.quark.cn/s/bda57957c548

1. 什么是枚举(Enum)?

枚举(enum,enumeration 的缩写) 是一种特殊的数据类型,用于定义一组固定的命名常量。与字符串或整数不同,枚举是**类型安全(type-safe)**的------编译器会确保你只能使用所定义集合中实际存在的值。

它们确保一个变量只能取预定义有效选项集合中的某一个值

如果一个值只能是预定义选项集合中的某一个,就考虑使用枚举。

为什么要使用枚举?

为了体会枚举带来的好处,不妨看看不用枚举的替代方案。如果没有枚举,你会把订单状态表示成散落在代码库各处的字符串:

java 复制代码
String status = "PENDING";

// Somewhere else in the codebase...
if (status.equals("PNDING")) {  // Typo! This condition is never true
    processOrder();
}

这段代码可以毫无警告地通过编译。在编译器看来,拼写错误 "PNDING" 是一个完全合法的字符串。这个 bug 只有在客户投诉订单始终未被处理时才会暴露出来。

枚举消除了这一类 bug。当你把 OrderStatus 定义为一个包含 PENDINGCONFIRMEDSHIPPEDDELIVERED 等值的枚举时,编译器就能确切知道哪些值是合法的。

与普通的常量或字符串相比,枚举具有以下几个关键优势:

  • 避免「魔法值」: 代码中不再散落 "PENDING"3 这样的字符串或整数。
  • 提升可读性: 枚举让意图一目了然------OrderStatus.SHIPPED 远比 3 更具描述性。
  • 启用编译器检查: 编译器会校验枚举的使用,尽早发现拼写错误和非法赋值。
  • 支持 IDE 功能: 大多数 IDE 都能为枚举值提供自动补全和重构工具。
  • 减少 bug: 你无法意外地赋一个不属于该枚举的随机字符串或数字。

枚举示例

枚举非常适合用来定义那些很少会发生变化的类别或状态。

  • 订单状态(例如:PENDINGIN_PROGRESSCOMPLETED
  • 用户角色(例如:ADMINCUSTOMERDRIVER
  • 车辆类型(例如:CARBIKETRUCK
  • 方向(例如:NORTHSOUTHEASTWEST

用枚举代替原始字符串,可以让你的系统更易于理解,也更难被误用。

2. 枚举示例

简单枚举

枚举最基本的形式,是在一个类型下定义一组命名常量。让我们在电商系统中为订单状态建模。

java 复制代码
public enum OrderStatus {
    PLACED,
    CONFIRMED,
    SHIPPED,
    DELIVERED,
    CANCELLED
}
python 复制代码
from enum import Enum

# Simple Enum
class OrderStatus(Enum):
    PLACED = "PLACED"
    CONFIRMED = "CONFIRMED"
    SHIPPED = "SHIPPED"
    DELIVERED = "DELIVERED"
    CANCELLED = "CANCELLED"
cpp 复制代码
enum class OrderStatus {
    PLACED,
    CONFIRMED,
    SHIPPED,
    DELIVERED,
    CANCELLED
};
csharp 复制代码
public enum OrderStatus
{
    PLACED,
    CONFIRMED,
    SHIPPED,
    DELIVERED,
    CANCELLED
}
go 复制代码
type OrderStatus int

const (
	Placed OrderStatus = iota
	Confirmed
	Shipped
	Delivered
	Cancelled
)

func (s OrderStatus) String() string {
	switch s {
	case Placed:
		return "PLACED"
	case Confirmed:
		return "CONFIRMED"
	case Shipped:
		return "SHIPPED"
	case Delivered:
		return "DELIVERED"
	case Cancelled:
		return "CANCELLED"
	default:
		return "UNKNOWN"
	}
}

// Go doesn't have a built-in enum keyword, so you use iota with typed constants to achieve the same result.
typescript 复制代码
enum OrderStatus {
    PLACED = "PLACED",
    CONFIRMED = "CONFIRMED",
    SHIPPED = "SHIPPED",
    DELIVERED = "DELIVERED",
    CANCELLED = "CANCELLED"
}

这个枚举定义了订单所能拥有的有限个合法状态,除此之外都不被允许。

在代码中使用

java 复制代码
OrderStatus status = OrderStatus.SHIPPED;

if (status == OrderStatus.SHIPPED) {
    System.out.println("Your package is on the way!");
}
python 复制代码
status = OrderStatus.SHIPPED

if status == OrderStatus.SHIPPED:
    print("Your package is on the way!")
cpp 复制代码
OrderStatus status = OrderStatus::SHIPPED;

if (status == OrderStatus::SHIPPED) {
    cout << "Your package is on the way!" << endl;
}
csharp 复制代码
package main

import "fmt"

type OrderStatus int

const (
	SHIPPED OrderStatus = iota
)

func main() {
	status := SHIPPED

	if status == SHIPPED {
		fmt.Println("Your package is on the way!")
	}
}
go 复制代码
OrderStatus status = OrderStatus.SHIPPED;

if (status == OrderStatus.SHIPPED)
{
    Console.WriteLine("Your package is on the way!");
}
typescript 复制代码
const status: OrderStatus = OrderStatus.SHIPPED;

if (status === OrderStatus.SHIPPED) {
    console.log("Your package is on the way!");
}

当你只需要一组命名常量时,简单枚举非常好用。但如果每个常量还需要携带额外数据呢?

带属性和方法的枚举

枚举的作用不止是给常量命名。在许多语言中,每个枚举值都可以携带额外数据,甚至定义行为。这让枚举在建模领域概念时出奇地强大。

我们来考虑一个表示美国硬币及其面值的 Coin 枚举。每种硬币都有一个名称(PENNY、NICKEL、DIME、QUARTER)和一个以「分」为单位的面值(1、5、10、25)。与其维护一张独立的面值查找表,不如把面值直接内嵌到枚举中。

java 复制代码
public enum Coin {
    PENNY(1),
    NICKEL(5),
    DIME(10),
    QUARTER(25);

    private final int value;

    Coin(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }
}
python 复制代码
from enum import Enum

class Coin(Enum):
    PENNY = 1
    NICKEL = 5
    DIME = 10
    QUARTER = 25
    
    def __init__(self, value):
        self.coin_value = value
    
    def get_value(self):
        return self.coin_value
cpp 复制代码
enum class Coin {
    PENNY,
    NICKEL,
    DIME,
    QUARTER
};

// C++ enums can't hold fields, so use a helper function
int getCoinValue(Coin coin) {
    switch (coin) {
        case Coin::PENNY:   return 1;
        case Coin::NICKEL:  return 5;
        case Coin::DIME:    return 10;
        case Coin::QUARTER: return 25;
        default:            return 0;
    }
}
csharp 复制代码
// C# enums can have integer values but no methods.
// For richer behavior, use a class with static readonly fields.
public class Coin
{
    public static readonly Coin PENNY = new("PENNY", 1);
    public static readonly Coin NICKEL = new("NICKEL", 5);
    public static readonly Coin DIME = new("DIME", 10);
    public static readonly Coin QUARTER = new("QUARTER", 25);

    public string Name { get; }
    public int Value { get; }

    private Coin(string name, int value)
    {
        Name = name;
        Value = value;
    }

    public int GetValue() => Value;
}
go 复制代码
type Coin struct {
	Name  string
	Value int
}

var (
	Penny   = Coin{"PENNY", 1}
	Nickel  = Coin{"NICKEL", 5}
	Dime    = Coin{"DIME", 10}
	Quarter = Coin{"QUARTER", 25}
)

func (c Coin) GetValue() int {
	return c.Value
}
typescript 复制代码
class Coin {
    static readonly PENNY = new Coin("PENNY", 1);
    static readonly NICKEL = new Coin("NICKEL", 5);
    static readonly DIME = new Coin("DIME", 10);
    static readonly QUARTER = new Coin("QUARTER", 25);

    private constructor(
        public readonly name: string,
        private readonly value: number
    ) {}

    getValue(): number {
        return this.value;
    }
}

在代码中使用

java 复制代码
int total = Coin.DIME.getValue() + Coin.QUARTER.getValue(); // 35
python 复制代码
total = Coin.DIME.get_value() + Coin.QUARTER.get_value()  # 35
cpp 复制代码
int total = getCoinValue(Coin::DIME) + getCoinValue(Coin::QUARTER); // 35
csharp 复制代码
package main

type Coin int

const (
	PENNY Coin = iota + 1
	NICKEL
	DIME
	QUARTER
)

func getCoinValue(c Coin) int {
	switch c {
	case PENNY:
		return 1
	case NICKEL:
		return 5
	case DIME:
		return 10
	case QUARTER:
		return 25
	default:
		return 0
	}
}

func main() {
	total := getCoinValue(DIME) + getCoinValue(QUARTER) // 35
	_ = total
}
go 复制代码
int total = Coin.DIME.GetValue() + Coin.QUARTER.GetValue(); // 35
typescript 复制代码
const total: number = Coin.DIME.getValue() + Coin.QUARTER.getValue(); // 35

这比维护独立的数组或查找表要优雅、安全得多。数据与它所归属的常量紧挨在一起,因此不存在面值与名称脱节的风险。

3. 实战示例:订单处理系统

我们来构建一个小型订单处理系统,它用到两个枚举:已经见过的 OrderStatus,以及 PaymentMethod。它们共同展示了枚举如何为一个真实的领域模型带来结构性与安全性。

Order 类跟踪订单的状态、支付方式和总金额。它提供了在生命周期中推进状态、取消订单以及展示订单信息的方法。

关键在于:枚举控制了合法的转换------订单只能沿状态链向前推进(PLACED → CONFIRMED → SHIPPED → DELIVERED),并且只有在发货前才允许取消。

java 复制代码
public enum OrderStatus {
    PLACED, CONFIRMED, SHIPPED, DELIVERED, CANCELLED
}

public enum PaymentMethod {
    CREDIT_CARD("Credit Card", 2.5),
    DEBIT_CARD("Debit Card", 1.0),
    UPI("UPI", 0.0),
    NET_BANKING("Net Banking", 1.5);

    private final String displayName;
    private final double feePercent;

    PaymentMethod(String displayName, double feePercent) {
        this.displayName = displayName;
        this.feePercent = feePercent;
    }

    public String getDisplayName() { return displayName; }
    public double getFeePercent() { return feePercent; }
}

public class Order {
    private final String orderId;
    private OrderStatus status;
    private final PaymentMethod paymentMethod;
    private final double amount;

    public Order(String orderId, PaymentMethod paymentMethod, double amount) {
        this.orderId = orderId;
        this.paymentMethod = paymentMethod;
        this.amount = amount;
        this.status = OrderStatus.PLACED;
    }

    public boolean advanceStatus() {
        switch (status) {
            case PLACED:
                status = OrderStatus.CONFIRMED;
                return true;
            case CONFIRMED:
                status = OrderStatus.SHIPPED;
                return true;
            case SHIPPED:
                status = OrderStatus.DELIVERED;
                return true;
            default:
                return false;
        }
    }

    public boolean cancel() {
        if (status == OrderStatus.PLACED || status == OrderStatus.CONFIRMED) {
            status = OrderStatus.CANCELLED;
            return true;
        }
        return false; // Can't cancel after shipping
    }

    public double getTotalWithFees() {
        return amount + (amount * paymentMethod.getFeePercent() / 100);
    }

    public void displayInfo() {
        System.out.printf("Order %s | Status: %s | Payment: %s | Amount: $%.2f (with fees: $%.2f)%n",
            orderId, status, paymentMethod.getDisplayName(), amount, getTotalWithFees());
    }
}

// Usage
public class Main {
    public static void main(String[] args) {
        Order order = new Order("ORD-001", PaymentMethod.CREDIT_CARD, 99.99);
        order.displayInfo();

        order.advanceStatus(); // PLACED -> CONFIRMED
        order.advanceStatus(); // CONFIRMED -> SHIPPED
        order.displayInfo();

        System.out.println("Cancel after shipping: " + order.cancel()); // false
    }
}
python 复制代码
from enum import Enum

class OrderStatus(Enum):
    PLACED = "PLACED"
    CONFIRMED = "CONFIRMED"
    SHIPPED = "SHIPPED"
    DELIVERED = "DELIVERED"
    CANCELLED = "CANCELLED"

class PaymentMethod(Enum):
    CREDIT_CARD = ("Credit Card", 2.5)
    DEBIT_CARD = ("Debit Card", 1.0)
    UPI = ("UPI", 0.0)
    NET_BANKING = ("Net Banking", 1.5)

    def __init__(self, display_name: str, fee_percent: float):
        self.display_name = display_name
        self.fee_percent = fee_percent

class Order:
    _status_transitions = {
        OrderStatus.PLACED: OrderStatus.CONFIRMED,
        OrderStatus.CONFIRMED: OrderStatus.SHIPPED,
        OrderStatus.SHIPPED: OrderStatus.DELIVERED,
    }

    def __init__(self, order_id: str, payment_method: PaymentMethod, amount: float):
        self._order_id = order_id
        self._status = OrderStatus.PLACED
        self._payment_method = payment_method
        self._amount = amount

    def advance_status(self) -> bool:
        next_status = self._status_transitions.get(self._status)
        if next_status:
            self._status = next_status
            return True
        return False

    def cancel(self) -> bool:
        if self._status in (OrderStatus.PLACED, OrderStatus.CONFIRMED):
            self._status = OrderStatus.CANCELLED
            return True
        return False

    def get_total_with_fees(self) -> float:
        return self._amount + (self._amount * self._payment_method.fee_percent / 100)

    def display_info(self) -> None:
        print(f"Order {self._order_id} | Status: {self._status.value} | "
              f"Payment: {self._payment_method.display_name} | "
              f"Amount: ${self._amount:.2f} (with fees: ${self.get_total_with_fees():.2f})")

if __name__ == "__main__":
    order = Order("ORD-001", PaymentMethod.CREDIT_CARD, 99.99)
    order.display_info()

    order.advance_status()  # PLACED -> CONFIRMED
    order.advance_status()  # CONFIRMED -> SHIPPED
    order.display_info()

    print(f"Cancel after shipping: {order.cancel()}")  # False
cpp 复制代码
#include <iostream>
#include <string>

enum class OrderStatus {
    PLACED, CONFIRMED, SHIPPED, DELIVERED, CANCELLED
};

std::string orderStatusToString(OrderStatus s) {
    switch (s) {
        case OrderStatus::PLACED:    return "PLACED";
        case OrderStatus::CONFIRMED: return "CONFIRMED";
        case OrderStatus::SHIPPED:   return "SHIPPED";
        case OrderStatus::DELIVERED: return "DELIVERED";
        case OrderStatus::CANCELLED: return "CANCELLED";
        default:                     return "UNKNOWN";
    }
}

struct PaymentMethod {
    std::string displayName;
    double feePercent;

    static const PaymentMethod CREDIT_CARD;
    static const PaymentMethod DEBIT_CARD;
    static const PaymentMethod UPI;
    static const PaymentMethod NET_BANKING;
};

const PaymentMethod PaymentMethod::CREDIT_CARD{"Credit Card", 2.5};
const PaymentMethod PaymentMethod::DEBIT_CARD{"Debit Card", 1.0};
const PaymentMethod PaymentMethod::UPI{"UPI", 0.0};
const PaymentMethod PaymentMethod::NET_BANKING{"Net Banking", 1.5};

class Order {
private:
    std::string orderId;
    OrderStatus status;
    PaymentMethod paymentMethod;
    double amount;

public:
    Order(const std::string& orderId, const PaymentMethod& paymentMethod, double amount)
        : orderId(orderId), status(OrderStatus::PLACED),
          paymentMethod(paymentMethod), amount(amount) {}

    bool advanceStatus() {
        switch (status) {
            case OrderStatus::PLACED:
                status = OrderStatus::CONFIRMED; return true;
            case OrderStatus::CONFIRMED:
                status = OrderStatus::SHIPPED; return true;
            case OrderStatus::SHIPPED:
                status = OrderStatus::DELIVERED; return true;
            default:
                return false;
        }
    }

    bool cancel() {
        if (status == OrderStatus::PLACED || status == OrderStatus::CONFIRMED) {
            status = OrderStatus::CANCELLED;
            return true;
        }
        return false;
    }

    double getTotalWithFees() const {
        return amount + (amount * paymentMethod.feePercent / 100);
    }

    void displayInfo() const {
        printf("Order %s | Status: %s | Payment: %s | Amount: $%.2f (with fees: $%.2f)\
",
            orderId.c_str(), orderStatusToString(status).c_str(),
            paymentMethod.displayName.c_str(), amount, getTotalWithFees());
    }
};

int main() {
    Order order("ORD-001", PaymentMethod::CREDIT_CARD, 99.99);
    order.displayInfo();

    order.advanceStatus(); // PLACED -> CONFIRMED
    order.advanceStatus(); // CONFIRMED -> SHIPPED
    order.displayInfo();

    std::cout << "Cancel after shipping: " << (order.cancel() ? "true" : "false") << std::endl;
    return 0;
}
csharp 复制代码
using System;

public enum OrderStatus
{
    Placed, Confirmed, Shipped, Delivered, Cancelled
}

public class PaymentMethod
{
    public static readonly PaymentMethod CreditCard = new PaymentMethod("Credit Card", 2.5);
    public static readonly PaymentMethod DebitCard = new PaymentMethod("Debit Card", 1.0);
    public static readonly PaymentMethod Upi = new PaymentMethod("UPI", 0.0);
    public static readonly PaymentMethod NetBanking = new PaymentMethod("Net Banking", 1.5);

    public string DisplayName { get; }
    public double FeePercent { get; }

    private PaymentMethod(string displayName, double feePercent)
    {
        DisplayName = displayName;
        FeePercent = feePercent;
    }
}

public class Order
{
    private readonly string _orderId;
    private OrderStatus _status;
    private readonly PaymentMethod _paymentMethod;
    private readonly double _amount;

    public Order(string orderId, PaymentMethod paymentMethod, double amount)
    {
        _orderId = orderId;
        _paymentMethod = paymentMethod;
        _amount = amount;
        _status = OrderStatus.Placed;
    }

    public bool AdvanceStatus()
    {
        switch (_status)
        {
            case OrderStatus.Placed:
                _status = OrderStatus.Confirmed; return true;
            case OrderStatus.Confirmed:
                _status = OrderStatus.Shipped; return true;
            case OrderStatus.Shipped:
                _status = OrderStatus.Delivered; return true;
            default:
                return false;
        }
    }

    public bool Cancel()
    {
        if (_status == OrderStatus.Placed || _status == OrderStatus.Confirmed)
        {
            _status = OrderStatus.Cancelled;
            return true;
        }
        return false;
    }

    public double GetTotalWithFees()
    {
        return _amount + (_amount * _paymentMethod.FeePercent / 100);
    }

    public void DisplayInfo()
    {
        Console.WriteLine(
            $"Order {_orderId} | Status: {_status} | Payment: {_paymentMethod.DisplayName} " +
            $"| Amount: ${_amount:F2} (with fees: ${GetTotalWithFees():F2})");
    }
}

// Usage
public class Program
{
    public static void Main()
    {
        var order = new Order("ORD-001", PaymentMethod.CreditCard, 99.99);
        order.DisplayInfo();

        order.AdvanceStatus(); // Placed -> Confirmed
        order.AdvanceStatus(); // Confirmed -> Shipped
        order.DisplayInfo();

        Console.WriteLine($"Cancel after shipping: {order.Cancel()}"); // False
    }
}
go 复制代码
package main

import "fmt"

type OrderStatus int

const (
	PLACED OrderStatus = iota
	CONFIRMED
	SHIPPED
	DELIVERED
	CANCELLED
)

func (s OrderStatus) String() string {
	names := [...]string{"PLACED", "CONFIRMED", "SHIPPED", "DELIVERED", "CANCELLED"}
	if int(s) >= 0 && int(s) < len(names) {
		return names[s]
	}
	return "UNKNOWN"
}

type PaymentMethod struct {
	displayName string
	feePercent  float64
}

var (
	CREDIT_CARD = PaymentMethod{"Credit Card", 2.5}
	DEBIT_CARD  = PaymentMethod{"Debit Card", 1.0}
	UPI         = PaymentMethod{"UPI", 0.0}
	NET_BANKING = PaymentMethod{"Net Banking", 1.5}
)

func (p PaymentMethod) GetDisplayName() string { return p.displayName }
func (p PaymentMethod) GetFeePercent() float64 { return p.feePercent }

type Order struct {
	orderId       string
	status        OrderStatus
	paymentMethod PaymentMethod
	amount        float64
}

func NewOrder(orderId string, paymentMethod PaymentMethod, amount float64) *Order {
	return &Order{orderId: orderId, paymentMethod: paymentMethod, amount: amount, status: PLACED}
}

func (o *Order) AdvanceStatus() bool {
	switch o.status {
	case PLACED:
		o.status = CONFIRMED
		return true
	case CONFIRMED:
		o.status = SHIPPED
		return true
	case SHIPPED:
		o.status = DELIVERED
		return true
	default:
		return false
	}
}

func (o *Order) Cancel() bool {
	if o.status == PLACED || o.status == CONFIRMED {
		o.status = CANCELLED
		return true
	}
	return false
}

func (o *Order) GetTotalWithFees() float64 {
	return o.amount + (o.amount * o.paymentMethod.GetFeePercent() / 100)
}

func (o *Order) DisplayInfo() {
	fmt.Printf("Order %s | Status: %s | Payment: %s | Amount: $%.2f (with fees: $%.2f)\
",
		o.orderId, o.status, o.paymentMethod.GetDisplayName(), o.amount, o.GetTotalWithFees())
}

// Usage
func main() {
	order := NewOrder("ORD-001", CREDIT_CARD, 99.99)
	order.DisplayInfo()

	order.AdvanceStatus() // PLACED -> CONFIRMED
	order.AdvanceStatus() // CONFIRMED -> SHIPPED
	order.DisplayInfo()

	fmt.Println("Cancel after shipping:", order.Cancel()) // false
}
typescript 复制代码
enum OrderStatus {
    PLACED = "PLACED",
    CONFIRMED = "CONFIRMED",
    SHIPPED = "SHIPPED",
    DELIVERED = "DELIVERED",
    CANCELLED = "CANCELLED"
}

class PaymentMethod {
    static readonly CREDIT_CARD = new PaymentMethod("Credit Card", 2.5);
    static readonly DEBIT_CARD = new PaymentMethod("Debit Card", 1.0);
    static readonly UPI = new PaymentMethod("UPI", 0.0);
    static readonly NET_BANKING = new PaymentMethod("Net Banking", 1.5);

    private constructor(
        public readonly displayName: string,
        public readonly feePercent: number
    ) {}
}

class Order {
    private status: OrderStatus;

    constructor(
        private readonly orderId: string,
        private readonly paymentMethod: PaymentMethod,
        private readonly amount: number
    ) {
        this.status = OrderStatus.PLACED;
    }

    advanceStatus(): boolean {
        const transitions: Partial<Record<OrderStatus, OrderStatus>> = {
            [OrderStatus.PLACED]: OrderStatus.CONFIRMED,
            [OrderStatus.CONFIRMED]: OrderStatus.SHIPPED,
            [OrderStatus.SHIPPED]: OrderStatus.DELIVERED,
        };
        const next = transitions[this.status];
        if (next) {
            this.status = next;
            return true;
        }
        return false;
    }

    cancel(): boolean {
        if (this.status === OrderStatus.PLACED || this.status === OrderStatus.CONFIRMED) {
            this.status = OrderStatus.CANCELLED;
            return true;
        }
        return false;
    }

    getTotalWithFees(): number {
        return this.amount + (this.amount * this.paymentMethod.feePercent / 100);
    }

    displayInfo(): void {
        console.log(
            `Order ${this.orderId} | Status: ${this.status} | ` +
            `Payment: ${this.paymentMethod.displayName} | ` +
            `Amount: $${this.amount.toFixed(2)} (with fees: $${this.getTotalWithFees().toFixed(2)})`
        );
    }
}

// Usage
const order = new Order("ORD-001", PaymentMethod.CREDIT_CARD, 99.99);
order.displayInfo();

order.advanceStatus(); // PLACED -> CONFIRMED
order.advanceStatus(); // CONFIRMED -> SHIPPED
order.displayInfo();

console.log(`Cancel after shipping: ${order.cancel()}`); // false

为什么这样设计是可行的

  • 状态转换受控: advanceStatus() 方法强制订单按照合法的顺序流转。你不能从 PLACED 直接跳到 DELIVERED,也不能从 SHIPPED 回退到 CONFIRMED。枚举配合 switch 语句,让合法的转换变得一目了然。
  • 支付费率自包含: 每个 PaymentMethod 都携带自己的费率百分比。不需要额外的查找表或配置文件来保持同步。新增一种支付方式,只需在枚举中加一个带费率的值,其余代码即可自动生效。
  • 取消规则清晰: cancel() 方法通过枚举比较来执行业务规则。只有发货前才能取消。如果有人试图取消已发货的订单,方法会返回 false。没有歧义,也无需进行字符串匹配。
  • 易于扩展: 需要新增 RETURNED 状态?把它加进枚举并更新 switch 语句即可。如果你漏掉了某个分支的处理,编译器会提醒你。需要新增像 WALLET 这样的支付方式?在枚举里加一行(含显示名称和费率)就行。
java 复制代码
编程资源
https://pan.quark.cn/s/7f7c83756948
更多资源
https://pan.quark.cn/s/bda57957c548
相关推荐
Logintern091 小时前
什么时候应该用多进程什么时候用多线程呢?
开发语言·python
TJHHH.1 小时前
SQL注入学习总结
数据库·笔记·sql·学习·注入
墨雨晨曦881 小时前
2026/08/15 spring AI学习总结
java·tomcat
wno7042 小时前
Spring Boot JdbcTemplate配置Druid多数据源
java·spring boot·后端
ShiXZ2132 小时前
网络调试四剑客:ping / telnet / nc / netstat 速查指令集
运维·开发语言·网络·php
码农大叔的博客2 小时前
golang示例:switch
开发语言·后端·golang
个 人 练 习 生2 小时前
数据结构入门:算法复杂度
开发语言·数据结构·经验分享·学习·程序人生·算法
W_326002 小时前
Python-OpenCV边缘检测与阈值分割:Sobel、Scharr、Laplacian、Canny、全局与自适应阈值
开发语言·图像处理·python·opencv·机器学习
其实防守也摸鱼2 小时前
红队技能总结导图:从入门到精通的完整知识体系
开发语言·人工智能·学习·安全·web安全