首页 > 其他分享 >DDD | 02-值对象拓展示例

DDD | 02-值对象拓展示例

时间:2024-07-15 20:53:48浏览次数:6  
标签:02 return BigDecimal 示例 param value obj public DDD

示例拓展

金额和货币

创建一个表示金额和货币的值对象(AmountVO),在系统中统一处理货币相关的数据,确保精度和一致性。

import java.math.BigDecimal;
import java.util.Currency;

/**
* 这个AmountVO类使用BigDecimal来精确存储金额值,避免了浮点运算可能带来的精度问题。
同时,利用Currency类来表示货币类型,确保了货币信息的标准化。
此外,重写了equals、hashCode和toString方法以支持比较、哈希运算和提供人性化的字符串表示。
*/
public class AmountVO {

    private final BigDecimal value; // 金额值,使用BigDecimal以精确表示货币值
    private final Currency currency; // 货币类型,使用Java标准库中的Currency类

    /**
     * 构造一个新的金额值对象。
     *
     * @param value   金额值,要求为正数
     * @param currency 货币类型
     */
    public AmountVO(BigDecimal value, Currency currency) {
        if (value.compareTo(BigDecimal.ZERO) < 0) {
            throw new IllegalArgumentException("Amount value must not be negative.");
        }
        this.value = value;
        this.currency = currency;
    }

    /**
     * 获取金额值。
     *
     * @return 金额值
     */
    public BigDecimal getValue() {
        return value;
    }

    /**
     * 获取货币类型。
     *
     * @return 货币类型
     */
    public Currency getCurrency() {
        return currency;
    }

    /**
     * 检查两个AmountVO实例是否代表相同的金额(考虑货币)。
     *
     * @param obj 另一个AmountVO实例
     * @return 如果金额和货币都相同则返回true,否则返回false
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        AmountVO amountVO = (AmountVO) obj;
        return value.equals(amountVO.value) && currency.getCurrencyCode().equals(amountVO.currency.getCurrencyCode());
    }

    /**
     * 生成基于金额和货币类型的哈希码。
     *
     * @return 哈希码
     */
    @Override
    public int hashCode() {
        return Objects.hash(value, currency.getCurrencyCode());
    }

    /**
     * 提供一个友好的字符串表示形式,包括金额和货币代码。
     *
     * @return 字符串表示形式,如 "123.45 USD"
     */
    @Override
    public String toString() {
        return value + " " + currency.getCurrencyCode();
    }
}

为什么金额和货币可以是值对象?

金额和货币作为值对象(Value Object)的原因在于它们符合值对象的核心特征和设计原则,具体体现在以下几个方面:

  • 不可变性:金额和货币组合表示的是一个具体的、不变的值,比如“100美元”或“50欧元”。一旦创建,这个组合不应该被改变,这符合值对象通常设计为不可变对象的原则。
  • 相等性基于值:两个金额和货币的组合如果数值和货币种类完全相同,就被视为相等,这与值对象的相等性定义一致,即通过其内在状态而非身份(内存地址)来判断是否相等。
  • 无唯一标识:与实体对象(Entity)不同,值对象没有唯一的、持久的身份标识。无论在系统中出现多少次“100美元”,它们都是等价的,不需要区分是哪个具体的实例。
  • 可共享:由于值对象表示的是不变的值,因此多个对象可以安全地共享同一个值对象实例,这有助于节省内存并简化逻辑,尤其是在表达如商品价格、账户余额等场景时。
  • 封装复杂性:将金额和货币封装在一个值对象中,可以隐藏货币转换、格式化等复杂逻辑,对外提供简洁的接口,提高了代码的可读性和可维护性。
  • 领域驱动设计(DDD)中的概念匹配:在领域驱动设计中,值对象用来描述没有唯一标识符但具有某种描述性的对象。金额和货币的组合正是对现实世界中经济交易的一种描述,适合用值对象来建模。

综上所述,将金额和货币设计为值对象,能够有效地提升代码的清晰度、一致性和可维护性,同时确保了在处理财务数据时的准确性和安全性。

创建一个表示价格的值对象(PriceVO)是一个很好的方式来封装和管理商品价格相关的数据,确保数据的一致性和精确计算。

import java.math.BigDecimal;
import java.util.Currency;

/**
* 这个PriceVO类不仅封装了价格的金额和货币类型,还提供了一个计算含税价格的方法getTaxInclusiveAmount(),并允许在构造时指定税率。
* 这样的设计使得价格的处理变得灵活且易于理解,同时也保证了在进行价格计算时的准确性和一致性。
*/
public class PriceVO {

    private final BigDecimal amount; // 价格金额,使用BigDecimal确保精度
    private final Currency currency; // 货币类型
    private final BigDecimal taxRate; // 税率,默认为0,表示不含税

    /**
     * 构造一个新的价格值对象。
     *
     * @param amount   价格金额,必须大于0
     * @param currency 货币类型
     * @param taxRate  税率,例如0.08表示8%的税率,默认为0
     */
    public PriceVO(BigDecimal amount, Currency currency, BigDecimal taxRate) {
        if (amount.compareTo(BigDecimal.ZERO) <= 0) {
            throw new IllegalArgumentException("Price amount must be greater than zero.");
        }
        this.amount = amount;
        this.currency = currency;
        this.taxRate = taxRate != null ? taxRate : BigDecimal.ZERO;
    }

    /**
     * 获取价格金额。
     *
     * @return 价格金额
     */
    public BigDecimal getAmount() {
        return amount;
    }

    /**
     * 获取货币类型。
     *
     * @return 货币类型
     */
    public Currency getCurrency() {
        return currency;
    }

    /**
     * 获取含税价格。
     *
     * @return 含税价格
     */
    public BigDecimal getTaxInclusiveAmount() {
        if (taxRate.compareTo(BigDecimal.ZERO) > 0) {
            return amount.multiply(BigDecimal.ONE.add(taxRate));
        }
        return amount;
    }

    /**
     * 获取税率。
     *
     * @return 税率
     */
    public BigDecimal getTaxRate() {
        return taxRate;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        PriceVO priceVO = (PriceVO) o;
        return amount.equals(priceVO.amount) &&
               currency.equals(priceVO.currency) &&
               taxRate.equals(priceVO.taxRate);
    }

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

    @Override
    public String toString() {
        return amount + " " + currency.getCurrencyCode() +
               (taxRate.compareTo(BigDecimal.ZERO) > 0 ? " (Tax: " + taxRate.multiply(new BigDecimal(100)) + "%)" : "");
    }
}

创建一个表示工资的值对象(SalaryVO)可以用来封装员工薪资的细节,包括基本工资、奖金、扣款等部分,并支持不同货币类型。

import java.math.BigDecimal;
import java.util.Currency;

/**
* 这个SalaryVO类封装了工资计算的各个方面,包括基本工资、奖金和扣款,并提供了计算总工资的方法getTotalSalary()。
* 通过使用BigDecimal类型来存储金额,确保了计算的精确度。此外,它还支持不同货币类型,增加了适用范围。
* 这样的设计使得工资数据的处理更加清晰和高效,易于维护和扩展。
*/
public class SalaryVO {

    private final BigDecimal baseSalary; // 基本工资
    private final BigDecimal bonus; // 奖金,默认为0
    private final BigDecimal deductions; // 扣款,默认为0
    private final Currency currency; // 货币类型

    /**
     * 构造一个新的工资值对象。
     *
     * @param baseSalary  基本工资,必须大于0
     * @param bonus       奖金,默认为0
     * @param deductions  扣款,默认为0
     * @param currency    货币类型
     */
    public SalaryVO(BigDecimal baseSalary, BigDecimal bonus, BigDecimal deductions, Currency currency) {
        if (baseSalary.compareTo(BigDecimal.ZERO) <= 0) {
            throw new IllegalArgumentException("Base salary must be greater than zero.");
        }
        this.baseSalary = baseSalary;
        this.bonus = bonus != null ? bonus : BigDecimal.ZERO;
        this.deductions = deductions != null ? deductions : BigDecimal.ZERO;
        this.currency = currency;
    }

    /**
     * 获取基本工资。
     *
     * @return 基本工资
     */
    public BigDecimal getBaseSalary() {
        return baseSalary;
    }

    /**
     * 获取奖金。
     *
     * @return 奖金
     */
    public BigDecimal getBonus() {
        return bonus;
    }

    /**
     * 获取扣款。
     *
     * @return 扣款
     */
    public BigDecimal getDeductions() {
        return deductions;
    }

    /**
     * 获取总工资,即基本工资加上奖金减去扣款。
     *
     * @return 总工资
     */
    public BigDecimal getTotalSalary() {
        return baseSalary.add(bonus).subtract(deductions);
    }

    /**
     * 获取货币类型。
     *
     * @return 货币类型
     */
    public Currency getCurrency() {
        return currency;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        SalaryVO salaryVO = (SalaryVO) o;
        return baseSalary.equals(salaryVO.baseSalary) &&
               bonus.equals(salaryVO.bonus) &&
               deductions.equals(salaryVO.deductions) &&
               currency.equals(salaryVO.currency);
    }

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

    @Override
    public String toString() {
        return "Salary{" +
               "baseSalary=" + baseSalary +
               ", bonus=" + bonus +
               ", deductions=" + deductions +
               ", totalSalary=" + getTotalSalary() +
               ", currency=" + currency.getCurrencyCode() +
               '}';
    }
}

创建一个费用值对象(ExpenseVO)可以帮助我们统一管理和计算各种费用项,确保数据的准确性和一致性。

import java.time.LocalDate;
import java.math.BigDecimal;
import java.util.Currency;

/**
* 这个ExpenseVO类封装了费用的基本信息,如费用名称、金额、货币类型和发生日期,提供了获取这些属性的方法。
* 通过使用BigDecimal类型来处理金额,确保了金额计算的精确性。
* 此外,它还允许在创建时指定费用发生的特定日期,或者默认为创建时的当前日期。
* 这样的设计使得费用数据的管理更加规范和灵活。
*/
public class ExpenseVO {

    private final String title; // 费用标题或名称
    private final BigDecimal amount; // 费用金额,使用BigDecimal确保精度
    private final Currency currency; // 费用货币类型
    private final LocalDate date; // 费用发生日期,默认为当前日期

    /**
     * 构造一个新的费用值对象。
     *
     * @param title    费用标题或名称,不能为空
     * @param amount   费用金额,必须大于0
     * @param currency 费用货币类型,不能为空
     * @param date     费用发生日期,默认为当前日期
     */
    public ExpenseVO(String title, BigDecimal amount, Currency currency, LocalDate date) {
        if (title == null || title.isEmpty()) {
            throw new IllegalArgumentException("Expense title cannot be null or empty.");
        }
        if (amount.compareTo(BigDecimal.ZERO) <= 0) {
            throw new IllegalArgumentException("Expense amount must be greater than zero.");
        }
        if (currency == null) {
            throw new IllegalArgumentException("Currency cannot be null.");
        }
        this.title = title;
        this.amount = amount;
        this.currency = currency;
        this.date = date != null ? date : LocalDate.now();
    }

    /**
     * 获取费用标题或名称。
     *
     * @return 费用标题
     */
    public String getTitle() {
        return title;
    }

    /**
     * 获取费用金额。
     *
     * @return 费用金额
     */
    public BigDecimal getAmount() {
        return amount;
    }

    /**
     * 获取货币类型。
     *
     * @return 货币类型
     */
    public Currency getCurrency() {
        return currency;
    }

    /**
     * 获取费用发生日期。
     *
     * @return 费用日期
     */
    public LocalDate getDate() {
        return date;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        ExpenseVO expenseVO = (ExpenseVO) o;
        return title.equals(expenseVO.title) &&
               amount.equals(expenseVO.amount) &&
               currency.equals(expenseVO.currency) &&
               date.equals(expenseVO.date);
    }

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

    @Override
    public String toString() {
        return "Expense{" +
               "title='" + title + '\'' +
               ", amount=" + amount +
               ", currency=" + currency.getCurrencyCode() +
               ", date=" + date +
               '}';
    }
}

度量数据

创建一个重量值对象(WeightVO)可以用来封装和处理重量相关的数据,确保在系统中重量的表示和计算具有一致性和准确性

/**
* 这个WeightVO类使用BigDecimal来存储重量值,确保了数值的精确度,适用于需要高精度计算的场景。
* 同时,它定义了重量单位,使得重量的表示更加完整和明确。
* 尽管在这个基本实现中没有包含单位转换逻辑,但在实际应用中可以根据需要扩展此类,
* 增加单位之间的转换方法,以适应不同场景下的重量计量需求。
*/

public class WeightVO {

    private final BigDecimal value; // 重量值,使用BigDecimal确保精度
    private final String unit; // 重量单位,如 "kg"(千克)、"g"(克)、"lb"(磅)

    /**
     * 构造一个新的重量值对象。
     *
     * @param value 重量值,必须大于等于0
     * @param unit  重量单位,不能为空,建议使用标准单位
     */
    public WeightVO(BigDecimal value, String unit) {
        if (value.compareTo(BigDecimal.ZERO) < 0) {
            throw new IllegalArgumentException("Weight value must be non-negative.");
        }
        if (unit == null || unit.trim().isEmpty()) {
            throw new IllegalArgumentException("Weight unit cannot be null or empty.");
        }
        this.value = value;
        this.unit = unit.trim().toLowerCase(); // 统一单位为小写,便于比较
    }

    /**
     * 获取重量值。
     *
     * @return 重量值
     */
    public BigDecimal getValue() {
        return value;
    }

    /**
     * 获取重量单位。
     *
     * @return 重量单位
     */
    public String getUnit() {
        return unit;
    }

    /**
     * 检查两个WeightVO实例是否表示相同的重量(忽略单位差异)。
     *
     * @param obj 另一个WeightVO实例
     * @return 如果重量值相同则返回true,否则返回false
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        WeightVO weightVO = (WeightVO) obj;
        return value.compareTo(weightVO.value) == 0;
    }

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

    /**
     * 提供一个友好的字符串表示形式,包括重量值和单位。
     *
     * @return 字符串表示形式,如 "2.5 kg"
     */
    @Override
    public String toString() {
        return value + " " + unit;
    }
}

创建一个长度值对象(LengthVO)旨在封装长度数据及其单位,确保在处理尺寸和距离时的精确性和一致性。

import java.math.BigDecimal;

/**
* 这个LengthVO类通过使用BigDecimal来存储长度值,
* 确保了数值的高精度处理,适合于需要精确测量和计算的场景。
* 同时,它定义了长度单位,使得长度的表述更为明确和规范。
* 虽然这个基本版本未包含单位转换功能,但可以根据实际需求扩展此类,
* 添加不同单位间的转换方法,以适应多样的长度计量需求。
*/
public class LengthVO {

    private final BigDecimal value; // 长度值,使用BigDecimal确保精度
    private final String unit; // 长度单位,如 "m"(米)、"cm"(厘米)、"in"(英寸)

    /**
     * 构造一个新的长度值对象。
     *
     * @param value 长度值,必须大于等于0
     * @param unit  长度单位,不能为空,建议使用标准单位
     */
    public LengthVO(BigDecimal value, String unit) {
        if (value.compareTo(BigDecimal.ZERO) < 0) {
            throw new IllegalArgumentException("Length value must be non-negative.");
        }
        if (unit == null || unit.trim().isEmpty()) {
            throw new IllegalArgumentException("Length unit cannot be null or empty.");
        }
        this.value = value;
        this.unit = unit.trim().toLowerCase(); // 统一单位为小写,便于比较
    }

    /**
     * 获取长度值。
     *
     * @return 长度值
     */
    public BigDecimal getValue() {
        return value;
    }

    /**
     * 获取长度单位。
     *
     * @return 长度单位
     */
    public String getUnit() {
        return unit;
    }

    /**
     * 检查两个LengthVO实例是否表示相同的长度(忽略单位差异)。
     *
     * @param obj 另一个LengthVO实例
     * @return 如果长度值相同则返回true,否则返回false
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        LengthVO lengthVO = (LengthVO) obj;
        return value.compareTo(lengthVO.value) == 0;
    }

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

    /**
     * 提供一个友好的字符串表示形式,包括长度值和单位。
     *
     * @return 字符串表示形式,如 "1.5 m"
     */
    @Override
    public String toString() {
        return value + " " + unit;
    }
}

创建一个体积值对象(VolumeVO)用于封装和处理体积相关的数据,确保在系统中体积的表示和计算既精确又一致

import java.math.BigDecimal;

/**
* 这个VolumeVO类利用BigDecimal来存储体积值,确保了在处理体积数据时的高精度。
* 它还定义了体积单位,使得体积的表示更加标准化和清晰。
* 尽管此基本版本没有包含单位转换逻辑,但根据实际应用场景,
* 你可以进一步扩展此类,加入不同体积单位之间的转换方法,
* 以提高其在多场景下的适用性和便利性。
*/
public class VolumeVO {

    private final BigDecimal value; // 体积值,使用BigDecimal确保精度
    private final String unit; // 体积单位,如 "m³"(立方米)、"L"(升)、"gal"(加仑)

    /**
     * 构造一个新的体积值对象。
     *
     * @param value 体积值,必须大于等于0
     * @param unit  体积单位,不能为空,建议使用标准单位
     */
    public VolumeVO(BigDecimal value, String unit) {
        if (value.compareTo(BigDecimal.ZERO) < 0) {
            throw new IllegalArgumentException("Volume value must be non-negative.");
        }
        if (unit == null || unit.trim().isEmpty()) {
            throw new IllegalArgumentException("Volume unit cannot be null or empty.");
        }
        this.value = value;
        this.unit = unit.trim().toLowerCase(); // 统一单位为小写,便于比较
    }

    /**
     * 获取体积值。
     *
     * @return 体积值
     */
    public BigDecimal getValue() {
        return value;
    }

    /**
     * 获取体积单位。
     *
     * @return 体积单位
     */
    public String getUnit() {
        return unit;
    }

    /**
     * 检查两个VolumeVO实例是否表示相同的体积(忽略单位差异)。
     *
     * @param obj 另一个VolumeVO实例
     * @return 如果体积值相同则返回true,否则返回false
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        VolumeVO volumeVO = (VolumeVO) obj;
        return value.compareTo(volumeVO.value) == 0;
    }

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

    /**
     * 提供一个友好的字符串表示形式,包括体积值和单位。
     *
     * @return 字符串表示形式,如 "2.5 m³"
     */
    @Override
    public String toString() {
        return value + " " + unit;
    }
}

范围或区间

创建一个日期范围值对象(DateRangeVO)可以用来表示起始日期和结束日期之间的一个时间区间,这对于处理时间段查询、事件安排等场景非常有用。

import java.time.LocalDate;

/**
* 这个DateRangeVO类使用LocalDate来表示起始和结束日期,确保了日期操作的便捷性和准确性。
* 它提供了判断日期是否在范围内、计算持续天数等实用方法,以及重写了equals和hashCode方法以支持正确的比较和哈希运算。
* 这样的设计使得处理日期范围相关的业务逻辑变得更加简单和高效。
*/
public class DateRangeVO {

    private final LocalDate startDate; // 起始日期,包含该日
    private final LocalDate endDate; // 结束日期,包含该日

    /**
     * 构造一个新的日期范围值对象。
     *
     * @param startDate 起始日期,必须不晚于结束日期
     * @param endDate   结束日期,必须不早于起始日期
     */
    public DateRangeVO(LocalDate startDate, LocalDate endDate) {
        if (startDate.isAfter(endDate)) {
            throw new IllegalArgumentException("Start date must be on or before end date.");
        }
        this.startDate = startDate;
        this.endDate = endDate;
    }

    /**
     * 获取起始日期。
     *
     * @return 起始日期
     */
    public LocalDate getStartDate() {
        return startDate;
    }

    /**
     * 获取结束日期。
     *
     * @return 结束日期
     */
    public LocalDate getEndDate() {
        return endDate;
    }

    /**
     * 判断给定的日期是否位于当前日期范围内。
     *
     * @param date 要检查的日期
     * @return 如果date在范围内则返回true,否则返回false
     */
    public boolean contains(LocalDate date) {
        return !date.isBefore(startDate) && !date.isAfter(endDate);
    }

    /**
     * 计算日期范围的持续天数。
     *
     * @return 日期范围内的天数
     */
    public long durationDays() {
        return ChronoUnit.DAYS.between(startDate, endDate) + 1; // 加1是因为两端日期都包含在内
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        DateRangeVO that = (DateRangeVO) obj;
        return startDate.equals(that.startDate) && endDate.equals(that.endDate);
    }

    @Override
    public int hashCode() {
        return Objects.hash(startDate, endDate);
    }

    /**
     * 提供一个友好的字符串表示形式,包括起始和结束日期。
     *
     * @return 字符串表示形式,如 "2023-04-01 to 2023-04-7"
     */
    @Override
    public String toString() {
        return startDate + " to " + endDate;
    }
}

创建一个温度区间值对象(TemperatureRangeVO)可以用来表示一个最低温度到最高温度之间的区间,这对于气象预报、工业控制等领域非常有用。

import java.math.BigDecimal;

/**
* 这个TemperatureRangeVO类使用BigDecimal来存储温度值,确保了温度读数的高精度。
* 它定义了温度单位,并提供了判断某个温度是否在给定区间内的方法,以及覆盖了equals和hashCode方法以支持对象的正确比较和集合操作。
* 这样的设计使得处理温度区间的数据更加方便和准确。
*/
public class TemperatureRangeVO {

    private final BigDecimal minValue; // 最低温度值,使用BigDecimal确保精度
    private final BigDecimal maxValue; // 最高温度值,使用BigDecimal确保精度
    private final String unit; // 温度单位,如 "C"(摄氏度)或 "F"(华氏度)

    /**
     * 构造一个新的温度区间值对象。
     *
     * @param minValue 最低温度值,必须小于或等于最大温度值
     * @param maxValue 最高温度值,必须大于或等于最低温度值
     * @param unit     温度单位,不能为空,建议使用标准单位
     */
    public TemperatureRangeVO(BigDecimal minValue, BigDecimal maxValue, String unit) {
        if (minValue.compareTo(maxValue) > 0) {
            throw new IllegalArgumentException("Minimum temperature must be less than or equal to maximum temperature.");
        }
        if (unit == null || unit.trim().isEmpty()) {
            throw new IllegalArgumentException("Temperature unit cannot be null or empty.");
        }
        this.minValue = minValue;
        this.maxValue = maxValue;
        this.unit = unit.trim().toUpperCase(); // 统一单位为大写,便于比较
    }

    /**
     * 获取最低温度值。
     *
     * @return 最低温度值
     */
    public BigDecimal getMinValue() {
        return minValue;
    }

    /**
     * 获取最高温度值。
     *
     * @return 最高温度值
     */
    public BigDecimal getMaxValue() {
        return maxValue;
    }

    /**
     * 获取温度单位。
     *
     * @return 温度单位
     */
    public String getUnit() {
        return unit;
    }

    /**
     * 检查给定的温度值是否位于当前温度区间内。
     *
     * @param temp 温度值
     * @return 如果温度值在区间内则返回true,否则返回false
     */
    public boolean contains(BigDecimal temp) {
        return temp.compareTo(minValue) >= 0 && temp.compareTo(maxValue) <= 0;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        TemperatureRangeVO that = (TemperatureRangeVO) obj;
        return minValue.compareTo(that.minValue) == 0 && maxValue.compareTo(that.maxValue) == 0 && unit.equals(that.unit);
    }

    @Override
    public int hashCode() {
        return Objects.hash(minValue, maxValue, unit);
    }

    /**
     * 提供一个友好的字符串表示形式,包括温度区间和单位。
     *
     * @return 字符串表示形式,如 "10°C to 25°C"
     */
    @Override
    public String toString() {
        return minValue + "°" + unit + " to " + maxValue + "°" + unit;
    }
}

数学模型

创建一个坐标值对象(CoordinateVO)可以用来封装二维空间中的位置信息,通常包括经度(longitude)和纬度(latitude)。


/**
* 这个CoordinateVO类使用double类型来存储经度和纬度值,确保了广泛的兼容性和计算简便性。
* 它验证了坐标值的有效范围,并提供了获取经度和纬度的方法。
* 通过重写equals和hashCode方法,确保了坐标值对象可以在集合中正确地进行比较和存储。
* 此外,toString方法提供了易于理解的坐标表示形式。
* 这样的设计适用于地理信息系统、地图应用等多种需要处理地理位置信息的场景。
*/

public class CoordinateVO {

    private final double longitude; // 经度
    private final double latitude; // 纬度

    /**
     * 构造一个新的坐标值对象。
     *
     * @param longitude 经度,取值范围通常是 -180 到 180
     * @param latitude  纬度,取值范围通常是 -90 到 90
     */
    public CoordinateVO(double longitude, double latitude) {
        if (longitude < -180 || longitude > 180) {
            throw new IllegalArgumentException("Longitude must be between -180 and 180.");
        }
        if (latitude < -90 || latitude > 90) {
            throw new IllegalArgumentException("Latitude must be between -90 and 90.");
        }
        this.longitude = longitude;
        this.latitude = latitude;
    }

    /**
     * 获取经度。
     *
     * @return 经度值
     */
    public double getLongitude() {
        return longitude;
    }

    /**
     * 获取纬度。
     *
     * @return 纬度值
     */
    public double getLatitude() {
        return latitude;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        CoordinateVO coordinateVO = (CoordinateVO) obj;
        return Double.compare(coordinateVO.longitude, longitude) == 0 && Double.compare(coordinateVO.latitude, latitude) == 0;
    }

    @Override
    public int hashCode() {
        return Objects.hash(longitude, latitude);
    }

    /**
     * 提供一个友好的字符串表示形式,展示经纬度坐标。
     *
     * @return 字符串表示形式,如 "(100.75, 30.25)"
     */
    @Override
    public String toString() {
        return "(" + longitude + ", " + latitude + ")";
    }
}

创建一个向量值对象(VectorVO)可以用来表示具有大小(magnitude)和方向的量,通常在物理、工程和计算机图形学等领域中使用。这里我们以二维向量为例,它包含x分量和y分量。


/**
* 这个VectorVO类使用double类型来存储向量的x分量和y分量,
* 提供了获取分量值、计算向量大小(模)、向量加法和减法等基本向量操作方法。
* 同时,重写了equals和hashCode方法以支持向量对象的比较和在集合中的唯一性识别。
* toString方法则用于生成易于阅读的向量表示。
* 此实现可以作为基础,根据具体需求进一步扩展,比如添加向量的标量乘法、点积、叉积等高级操作。
*/
public class VectorVO {

    private final double xComponent; // x分量
    private final double yComponent; // y分量

    /**
     * 构造一个新的二维向量值对象。
     *
     * @param xComponent x分量
     * @param yComponent y分量
     */
    public VectorVO(double xComponent, double yComponent) {
        this.xComponent = xComponent;
        this.yComponent = yComponent;
    }

    /**
     * 获取x分量。
     *
     * @return x分量值
     */
    public double getXComponent() {
        return xComponent;
    }

    /**
     * 获取y分量。
     *
     * @return y分量值
     */
    public double getYComponent() {
        return yComponent;
    }

    /**
     * 计算向量的大小(模)。
     *
     * @return 向量的大小
     */
    public double magnitude() {
        return Math.sqrt(xComponent * xComponent + yComponent * yComponent);
    }

    /**
     * 向量加法。
     *
     * @param other 另一个向量
     * @return 两个向量相加的结果
     */
    public VectorVO add(VectorVO other) {
        return new VectorVO(this.xComponent + other.xComponent, this.yComponent + other.yComponent);
    }

    /**
     * 向量减法。
     *
     * @param other 另一个向量
     * @return 两个向量相减的结果
     */
    public VectorVO subtract(VectorVO other) {
        return new VectorVO(this.xComponent - other.xComponent, this.yComponent - other.yComponent);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        VectorVO vectorVO = (VectorVO) obj;
        return Double.compare(vectorVO.xComponent, xComponent) == 0 && Double.compare(vectorVO.yComponent, yComponent) == 0;
    }

    @Override
    public int hashCode() {
        return Objects.hash(xComponent, yComponent);
    }

    /**
     * 提供一个友好的字符串表示形式,展示向量的x和y分量。
     *
     * @return 字符串表示形式,如 "<1.5, 3.2>"
     */
    @Override
    public String toString() {
        return "<" + xComponent + ", " + yComponent + ">";
    }
}

创建线性回归模型(LinearRegressionModelVO)值对象

线性回归是一种统计方法,用于预测一个连续响应变量与一个或多个预测变量之间的关系。这个值对象将包括模型参数(斜率和截距)、相关系数(如R²)、训练数据集的摘要统计信息等。

import java.util.List;

/**
* 这个LinearRegressionModelVO类封装了线性回归模型的核心参数,
* 包括斜率、截距、R²值,以及训练数据集的特征平均值和标准差列表,
* 这有助于理解和评估模型的质量。
* 通过提供这些信息,该值对象能够支持模型的持久化、比较和调试等高级操作。
*/
public class LinearRegressionModelVO {

    private final double slope; // 斜率,即模型的系数
    private final double intercept; // 截距,即模型的常数项
    private final double rSquared; // R²值,模型拟合优度的度量
    private final List<Double> featureMeans; // 特征(预测变量)的平均值列表
    private final List<Double> featureStdDevs; // 特征的标准差列表

    /**
     * 构造一个新的线性回归模型值对象。
     *
     * @param slope         模型的斜率
     * @param intercept     模型的截距
     * @param rSquared      模型的R²值
     * @param featureMeans  特征的平均值列表
     * @param featureStdDevs 特征的标准差列表
     */
    public LinearRegressionModelVO(double slope, double intercept, double rSquared,
                                   List<Double> featureMeans, List<Double> featureStdDevs) {
        if (featureMeans == null || featureStdDevs == null || featureMeans.size() != featureStdDevs.size()) {
            throw new IllegalArgumentException("Feature means and standard deviations lists must not be null and of the same size.");
        }
        this.slope = slope;
        this.intercept = intercept;
        this.rSquared = rSquared;
        this.featureMeans = featureMeans;
        this.featureStdDevs = featureStdDevs;
    }

    /**
     * 获取模型的斜率。
     *
     * @return 斜率值
     */
    public double getSlope() {
        return slope;
    }

    /**
     * 获取模型的截距。
     *
     * @return 截距值
     */
    public double getIntercept() {
        return intercept;
    }

    /**
     * 获取模型的R²值。
     *
     * @return R²值
     */
    public double getRSquared() {
        return rSquared;
    }

    /**
     * 获取特征的平均值列表。
     *
     * @return 特征平均值列表
     */
    public List<Double> getFeatureMeans() {
        return featureMeans;
    }

    /**
     * 获取特征的标准差列表。
     *
     * @return 特征标准差列表
     */
    public List<Double> getFeatureStdDevs() {
        return featureStdDevs;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        LinearRegressionModelVO that = (LinearRegressionModelVO) obj;
        return Double.compare(that.slope, slope) == 0 &&
               Double.compare(that.intercept, intercept) == 0 &&
               Double.compare(that.rSquared, rSquared) == 0 &&
               featureMeans.equals(that.featureMeans) &&
               featureStdDevs.equals(that.featureStdDevs);
    }

    @Override
    public int hashCode() {
        return Objects.hash(slope, intercept, rSquared, featureMeans, featureStdDevs);
    }

    /**
     * 提供一个友好的字符串表示形式,概述模型的关键参数。
     *
     * @return 字符串表示形式,如 "Slope: 2.5, Intercept: 1.0, R²: 0.85"
     */
    @Override
    public String toString() {
        return "Slope: " + slope + ", Intercept: " + intercept + ", R²: " + rSquared;
    }
}

创建一个多项式回归模型值对象(PolynomialRegressionModelVO)

多项式回归是线性回归的一种扩展,允许因变量和一个或多个自变量之间的关系以多项式形式建模。这个值对象会包含多项式系数、阶次、训练数据的性能指标等信息。

import java.util.List;

/**
* 这个PolynomialRegressionModelVO类不仅记录了多项式回归模型的系数、阶次和R²值,
* 还包含了训练数据集的特征统计信息,有助于深入分析和复用模型。
* 它支持对复杂非线性关系的建模,广泛应用于数据分析、科学计算和机器学习领域。
*/
public class PolynomialRegressionModelVO {

    private final int degree; // 多项式的阶次
    private final List<Double> coefficients; // 多项式系数列表,从常数项到最高次项
    private final double rSquared; // R²值,模型拟合优度的度量
    private final List<Double> featureMeans; // 特征(自变量)的平均值列表
    private final List<Double> featureStdDevs; // 特征的标准差列表

    /**
     * 构造一个新的多项式回归模型值对象。
     *
     * @param degree        多项式的阶次
     * @param coefficients  多项式系数列表
     * @param rSquared      模型的R²值
     * @param featureMeans  特征的平均值列表
     * @param featureStdDevs 特征的标准差列表
     */
    public PolynomialRegressionModelVO(int degree, List<Double> coefficients, double rSquared,
                                       List<Double> featureMeans, List<Double> featureStdDevs) {
        if (coefficients == null || coefficients.isEmpty() || featureMeans == null || featureStdDevs == null ||
                featureMeans.size() != featureStdDevs.size()) {
            throw new IllegalArgumentException("Coefficients, feature means, and standard deviations must not be null or incorrectly sized.");
        }
        if (degree != coefficients.size() - 1) {
            throw new IllegalArgumentException("Degree does not match the number of coefficients provided.");
        }
        this.degree = degree;
        this.coefficients = coefficients;
        this.rSquared = rSquared;
        this.featureMeans = featureMeans;
        this.featureStdDevs = featureStdDevs;
    }

    /**
     * 获取多项式的阶次。
     *
     * @return 阶次
     */
    public int getDegree() {
        return degree;
    }

    /**
     * 获取多项式系数列表。
     *
     * @return 系数列表
     */
    public List<Double> getCoefficients() {
        return coefficients;
    }

    /**
     * 获取模型的R²值。
     *
     * @return R²值
     */
    public double getRSquared() {
        return rSquared;
    }

    /**
     * 获取特征的平均值列表。
     *
     * @return 特征平均值列表
     */
    public List<Double> getFeatureMeans() {
        return featureMeans;
    }

    /**
     * 获取特征的标准差列表。
     *
     * @return 特征标准差列表
     */
    public List<Double> getFeatureStdDevs() {
        return featureStdDevs;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        PolynomialRegressionModelVO that = (PolynomialRegressionModelVO) obj;
        return degree == that.degree &&
               coefficients.equals(that.coefficients) &&
               Double.compare(that.rSquared, rSquared) == 0 &&
               featureMeans.equals(that.featureMeans) &&
               featureStdDevs.equals(that.featureStdDevs);
    }

    @Override
    public int hashCode() {
        return Objects.hash(degree, coefficients, rSquared, featureMeans, featureStdDevs);
    }

    /**
     * 提供一个友好的字符串表示形式,概述模型的关键参数。
     *
     * @return 字符串表示形式,如 "Degree: 3, Coefficients: [2.5, -1.3, 0.6, 1.0], R²: 0.92"
     */
    @Override
    public String toString() {
        return "Degree: " + degree + ", Coefficients: " + coefficients + ", R²: " + rSquared;
    }
}

创建一个矩阵值对象(MatrixVO)

用于表示数学中的矩阵。矩阵是线性代数中的基本概念,广泛应用于计算机图形学、机器学习、工程计算等多个领域。这个值对象将能够存储矩阵的维度信息以及矩阵中的元素值。

import java.util.ArrayList;
import java.util.List;

/**
* 这个MatrixVO类可以用来存储和操作任意大小的矩阵,包括获取和设置特定位置的元素值。
* 通过重写equals和hashCode方法,确保了不同实例间的比较逻辑正确。
* toString方法则提供了一个易于阅读的矩阵表示形式,方便打印和调试。
* 此类设计适用于需要在软件系统中处理矩阵运算的场景,例如计算密集型应用、算法开发等。
*/
public class MatrixVO {

    private final int rows; // 矩阵的行数
    private final int columns; // 矩阵的列数
    private final List<List<Double>> elements; // 矩阵元素,内部列表代表每行的元素

    /**
     * 构造一个新的矩阵值对象。
     *
     * @param rows    矩阵的行数
     * @param columns 矩阵的列数
     * @param elements 矩阵元素列表,每个子列表代表一行
     */
    public MatrixVO(int rows, int columns, List<List<Double>> elements) {
        if (rows <= 0 || columns <= 0) {
            throw new IllegalArgumentException("Number of rows and columns must be positive.");
        }
        if (elements == null || elements.size() != rows || !elements.stream().allMatch(row -> row != null && row.size() == columns)) {
            throw new IllegalArgumentException("Elements list does not match the specified dimensions.");
        }
        this.rows = rows;
        this.columns = columns;
        this.elements = new ArrayList<>(elements); // 使用新列表以避免外部修改影响内部状态
    }

    /**
     * 获取矩阵的行数。
     *
     * @return 行数
     */
    public int getRows() {
        return rows;
    }

    /**
     * 获取矩阵的列数。
     *
     * @return 列数
     */
    public int getColumns() {
        return columns;
    }

    /**
     * 获取指定位置的元素值。
     *
     * @param row 行索引
     * @param column 列索引
     * @return 元素值
     */
    public double getElementAt(int row, int column) {
        return elements.get(row).get(column);
    }

    /**
     * 设置指定位置的元素值。
     *
     * @param row      行索引
     * @param column   列索引
     * @param value    新的元素值
     */
    public void setElementAt(int row, int column, double value) {
        elements.get(row).set(column, value);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        MatrixVO matrixVO = (MatrixVO) obj;
        return rows == matrixVO.rows && columns == matrixVO.columns && elements.equals(matrixVO.elements);
    }

    @Override
    public int hashCode() {
        return Objects.hash(rows, columns, elements);
    }

    /**
     * 提供一个友好的字符串表示形式,展示矩阵的所有元素。
     *
     * @return 字符串表示形式,如 "[[1.0, 2.0], [3.0, 4.0]]"
     */
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder("[");
        for (int i = 0; i < rows; i++) {
            if (i > 0) sb.append(", ");
            sb.append("[");
            for (int j = 0; j < columns; j++) {
                if (j > 0) sb.append(", ");
                sb.append(elements.get(i).get(j));
            }
            sb.append("]");
        }
        sb.append("]");
        return sb.toString();
    }
}

标签:02,return,BigDecimal,示例,param,value,obj,public,DDD
From: https://www.cnblogs.com/dolphinmind/p/18303938

相关文章

  • The 2022 ICPC Asia Shenyang Regional Contest
    Preface本来以为今天有多校的,但到了机房发现并没有,索性就随便找了场比赛VP了然后经典开场三线红温,签了3个题后徐神被一个string关住了(后面发现他犯了个极其弱智的错误导致坐牢一整场),祁神被构造F关了,然后我写A的分类讨论写的很红温中间排名一度经典俯冲铁牌区,但好在最后......
  • 真题模拟2022CSP-J总结
    T1乘方这个题真的是很简单,就特判一下1的情况,其它情况直接暴力枚举即可考试的时候也是非常快的想到T2解密也比较容易首先我们把ed=(p_i−1)(q_i−1)+1打开变为ed=pq−p−q+1+1将n=pq带入原式得:ed=n−p−q+2那么我们现在有了两个式子:p+q=n-ed+2pq=n如果我......
  • Codeforces Round #956 (Div. 2) and ByteRace 2024
    目录写在前面ABCDEF写在最后写在前面比赛地址:https://codeforces.com/contest/1983孩子们我回来了。这场实在是太对胃口,vp不到1h就4题了之后EF也口出来了,然而赛时睡大觉去了没打真是亏死。感觉自己的文字能力已经很牛逼了,不需要再多练了,以后的题解都会写得简略些。A......
  • Python学习代码示例合集
    PythonDemo示例合集PyDemo:Python学习代码示例合集介绍欢迎来到PyDemo,这是一个专为Python学习者设计的代码示例合集。无论你是编程新手还是经验丰富的开发者,PyDemo都将为你提供有用的代码片段,帮助你快速掌握Python编程的各种技巧和应用场景。项目背景Python作......
  • Project2007-2021安装包分享:附网盘地址+安装步骤
    不得不承认,Project是从事项目管理人员最常用的软件之一,它不仅可以提高项目的效率,缩短项目开发周期,操作难度相对来说也比较小。也可以说,Project是一款专注于项目管理的桌面应用软件。它可以帮助用户制定项目计划、分派任务、管理资源、跟踪进度以及生成汇报等。MicrosoftProj......
  • P10720 [GESP202406 五级] 小杨的幸运数字 题解
    题意如果一个数的质因子中只有两个不同的数则输出\(1\),否则输出\(0\)。思路从第一个质因子遍历到\(sum\)的话很明显是\(O(nt)\)最大是\(n^{10}\)很明显会炸掉。所以遍历到\(sum\)是不行的,考虑正整数\(n\)最大的质因数是\(\sqrt{n}\)所以遍历到\(\sqrt{n}\)即......
  • 题解:SP10502 VIDEO - Video game combos
    大意构造一个长度为\(k\)(\(k\)是给定的)的串\(x\),使得对于\(∀1\leqi\leqn,s_i\)在\(x\)中的出现次数之和最大。输出这个最大值。思考考虑对\(s_i\)建AC自动机,然后dp。记\(dp[i][u]\)表示为长度为\(i\)的字符串,且当前已计算的节点是Trie上的编号为\(u......
  • JavaAPI练习(1) (2024.7.15)
        Math类packageMathExercise20240715;//Math类所在的是java.lang包,属于核心包,无需导包publicclassMathExercise{publicstaticvoidmain(String[]args){//Math方法为静态的,不需要创建对象,直接类名调用即可//abs返回参数的绝对......
  • 【题解】 [CSP-J 2021 T1] 分糖果
    题目描述题目大意给定正整数\(n\)、\(L\)、\(R\),求\(\max_{i\in\left[L,R\right]}{i\bmodn}\)。思路题目主要考察:分类讨论。众所周知,对于\(\forallx\),有$(x\bmodn)\in\left[0,n-1\right]$。可以分为两种情况讨论:如果\(\left\lfloor\frac{L......
  • 7.15鲜花——2024dl24灯光秀游记
    前言时光的背面有阑珊灯火那红墙绿树由我们诠说不问从前烟火无惧前路宕落我们将扬帆向未来漂泊就算在背对阳光的角落那不凡荣光把我们包裹每一个山长水阔每一条寻常巷陌每一颗心都依然灼热同灯火唱和少年翘首以盼的天晴等来属于盛夏的蜩鸣那些黑板书写的叮咛成了谁的曾经时光的......