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

如何为两个同实现的DDD值对象结构体复用代码?

解决方案建议

更优方案:提取通用逻辑到静态工具类

既然两个结构体的核心逻辑(IEquatable<T>、IComparable<T>实现、运算符重载、序列化)完全一致,仅类型标识不同,可将通用逻辑提取到静态工具类中复用,既保留独立类型的语义,又避免重复代码:

  1. 定义封装通用操作的静态工具类:
public static class VersionValueObjectHelper
{
    public static bool Equals<T>(T left, object? right) where T : struct, IEquatable<T>
    {
        return right is T other && left.Equals(other);
    }

    public static int Compare<T>(T left, T right, Func<T, int> getValue)
    {
        return getValue(left).CompareTo(getValue(right));
    }

    public static bool AreEqual<T>(T left, T right, Func<T, int> getValue)
    {
        return getValue(left) == getValue(right);
    }

    public static int ToInt<T>(T value, Func<T, int> getValue)
    {
        return getValue(value);
    }

    public static bool TryParse<T>(int input, Func<int, T> factory, out T result)
    {
        result = factory(input);
        return true;
    }
}
  1. 两个结构体各自实现接口,调用工具类方法:
public readonly struct DefinitionVersion : IEquatable<DefinitionVersion>, IComparable<DefinitionVersion>
{
    public int Value { get; }

    public DefinitionVersion(int value) => Value = value;

    public bool Equals(DefinitionVersion other)
    {
        return VersionValueObjectHelper.AreEqual(this, other, v => v.Value);
    }

    public override bool Equals(object? obj)
    {
        return VersionValueObjectHelper.Equals(this, obj);
    }

    public override int GetHashCode() => Value.GetHashCode();

    public int CompareTo(DefinitionVersion other)
    {
        return VersionValueObjectHelper.Compare(this, other, v => v.Value);
    }

    public static bool operator ==(DefinitionVersion left, DefinitionVersion right)
    {
        return VersionValueObjectHelper.AreEqual(left, right, v => v.Value);
    }

    public static bool operator !=(DefinitionVersion left, DefinitionVersion right)
    {
        return !VersionValueObjectHelper.AreEqual(left, right, v => v.Value);
    }

    public static bool operator <(DefinitionVersion left, DefinitionVersion right)
    {
        return VersionValueObjectHelper.Compare(left, right, v => v.Value) < 0;
    }

    public static bool operator >(DefinitionVersion left, DefinitionVersion right)
    {
        return VersionValueObjectHelper.Compare(left, right, v => v.Value) > 0;
    }

    public static implicit operator int(DefinitionVersion version) => version.Value;
    public static explicit operator DefinitionVersion(int value) => new DefinitionVersion(value);
}

// InstanceVersion结构体仅替换类型名,逻辑完全复用工具类
public readonly struct InstanceVersion : IEquatable<InstanceVersion>, IComparable<InstanceVersion>
{
    public int Value { get; }

    public InstanceVersion(int value) => Value = value;

    public bool Equals(InstanceVersion other)
    {
        return VersionValueObjectHelper.AreEqual(this, other, v => v.Value);
    }

    public override bool Equals(object? obj)
    {
        return VersionValueObjectHelper.Equals(this, obj);
    }

    public override int GetHashCode() => Value.GetHashCode();

    public int CompareTo(InstanceVersion other)
    {
        return VersionValueObjectHelper.Compare(this, other, v => v.Value);
    }

    public static bool operator ==(InstanceVersion left, InstanceVersion right)
    {
        return VersionValueObjectHelper.AreEqual(left, right, v => v.Value);
    }

    public static bool operator !=(InstanceVersion left, InstanceVersion right)
    {
        return !VersionValueObjectHelper.AreEqual(left, right, v => v.Value);
    }

    public static bool operator <(InstanceVersion left, InstanceVersion right)
    {
        return VersionValueObjectHelper.Compare(left, right, v => v.Value) < 0;
    }

    public static bool operator >(InstanceVersion left, InstanceVersion right)
    {
        return VersionValueObjectHelper.Compare(left, right, v => v.Value) > 0;
    }

    public static implicit operator int(InstanceVersion version) => version.Value;
    public static explicit operator InstanceVersion(int value) => new InstanceVersion(value);
}

这种方案的优势:

  • 通用逻辑仅维护一份,避免后续修改遗漏
  • 保留两个结构体的独立类型语义,符合DDD值对象的设计要求
  • 可读性强,无泛型抽象门槛,也无需复杂代码生成

现有三种方案的选择建议

如果不想新增工具类,可根据场景选择:

  1. 复制粘贴:适合两个结构体后续几乎无修改的场景,实现最快,但需注意后续修改要同步更新两份代码,建议加注释提醒。
  2. 代码生成:仅当未来要新增大量类似Version类型时考虑,否则会额外增加模板、脚本的维护成本,属于过度设计。
  3. 泛型结构体:比如用Version<TMarker>加标记类型区分,会增加语义理解门槛,其他开发者需要额外理解标记类型的作用,DDD场景下的类型直观性会下降。

综上,提取通用逻辑到静态工具类是最优选择;若场景简单,复制粘贴也可接受。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 08:25:22