发布于 2026-01-06 0 阅读
0

价值对象的隐藏价值 价值对象简介 价值对象的特征: 总结 链接:

值对象的隐藏值

介绍

价值对象的特征:

概括

链接:

介绍

在就 Vaughn Vernon 的《实现领域驱动设计》进行讲座之后,我决定写一篇与领域驱动设计相关的文章,重点关注被低估的价值对象。根据我的经验,开发人员在项目中滥用实体。这种行为通常会导致出现大量贫血的领域模型,这些模型缺乏或只有极少的业务逻辑。我认为,其中一些贫血的领域模型可以快速转换为价值对象。

注:所有示例均使用 C# 编写,并采用最新的 C# 语法。我希望使用其他语言的开发人员也能轻松理解。

例子:

让我们从一个简单的例子开始,例如下面代码片段中的 Car 实体。

public class Car
{
    /* Properties */
    public string Color { get; set; }
    public bool Metallic { get; set; }

    public string FuelType { get; set; }
    public int EngineCapacity { get; set; }
    { ... }

    /* Methods */
    public void Drive(int distance) { ... }
    { ... }
}
Enter fullscreen mode Exit fullscreen mode

我们将重点关注与汽车颜色相关的属性,即颜色和金属度属性。为简化起见,颜色属性用字符串类型表示。

价值对象的特征:


1) 测量、量化或描述某一领域的主题

在我们的示例中,Color`v` 和 `v`Metallic是 `Value` 对象的最佳候选对象。两者都描述了车辆的外观。因此,提取新类:

public class Color
{
    public string Hue { get; set; }
    public bool Metallic { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

每当Hue颜色Metallic发生变化时,我们都会收到一种新的颜色,所以让我们进入下一个要点。

2)是不可改变的

不可变性意味着对象在初始化后其状态不能更改。在 Java 或 C# 中,可以通过将所有参数传递给值对象的构造函数来实现这一点。对象的状态将根据参数值进行设置。当然,还可以根据这些值计算一些额外的属性。
注意:对象本身不能通过公共方法或私有方法调用属性设置器。这是被禁止的。

让我们根据前面的思考来完善我们的例子。

public class Color
{
    public Color(string hue, bool metallic)
    {
        Hue = hue;
        Metallic = metallic;
        if (hue == "white" && !metallic)
        {
            BasicPallete = true;
        }
     }

    public string Hue { get; }
    public bool Metallic { get; }
    public bool BasicPallete { get; }
}
Enter fullscreen mode Exit fullscreen mode

在上面的例子中,我们计算了该BasicPallete属性。例如,它可能会影响汽车的价格计算。

3) 将关联的属性值组合成一个整体单元

这意味着所有属性都是有界的,每个属性都提供了描述对象整体价值的关键信息。单个属性的值并不能提供关于对象的完整信息。例如,知道某种颜色是否具有金属光泽并不能告诉我们它的色调。同样的情况也发生在我们知道色调,但却不知道它是否具有金属光泽的时候。

价值就是一个经常被提及的完美例子。我们以100美元为例。货币和金额是紧密相连的。假设有人将我们对象中的货币从美元换成了墨西哥比索(MXN)。这种变化会产生显著的影响。100墨西哥比索大约只值5.25美元。没有人愿意成为这种微小变化的受害者。

4)当测量结果发生变化时,可以进行替换。

在我们的例子中,汽车是一个实体,颜色是一个值对象。假设有一天我们决定改变汽车的颜色。假设我们买了一辆绿色的非金属漆汽车,但几年后,我们想换成绿色金属漆。我们不能直接在原有漆面上添加金属光泽,而是需要重新喷涂新的绿色金属漆。

Color color = new Color("green", false);
//color.Metallic = true;  -> This is not allowed

color = new Color("green", true);
Enter fullscreen mode Exit fullscreen mode

5) 可以与其他值对象进行比较

在 C# 等语言中,默认情况下,当我们尝试比较两个对象时,比较范围仅限于它们在内存中的位置(称为引用相等性)。两个对象可以拥有相同的属性值,但它们并不相等。对于值对象而言,这种假设是错误的。在这种情况下,我们需要比较给定类型及其所有属性值是否相等(称为值相等性)。

根据我们的例子,如果两种颜色色调相同,无论它们是否具有金属光泽,它们都是相同的颜色。两辆绿色金属漆的汽车颜色相同。

因此,让我们提升课堂教学水平,贯彻价值平等原则:

public class Color
{
    public Color(string hue, bool metallic)
    {
        Hue = hue;
        Metallic = metallic;
        if (hue == "white" && !metallic)
        {
            BasicPallete = true;
        }
    }

    public string Hue { get; }
    public bool Metallic { get; }
    public bool BasicPallete { get; }

    public override bool Equals(object obj)
    {
        return this.Equals(obj as Color);
    }

    public bool Equals(Color otherColor)
    {
        if (Object.ReferenceEquals(otherColor, null)) return false;
        if (Object.ReferenceEquals(this, otherColor)) return true;
        if (this.GetType() != otherColor.GetType()) return false;

        return (Hue == otherColor.Hue) && (Metallic == otherColor.Metallic);
    }

    public static bool operator ==(Color leftColor, Color rightColor)
    {
        if (Object.ReferenceEquals(leftColor, null))
        {
            // null == null = true
            return (Object.ReferenceEquals(rightColor, null));
        }
        return leftColor.Equals(rightColor);
    }

    public static bool operator !=(Color leftColor, Color rightColor)
    {
        return !(leftColor == rightColor);
    }

    public override int GetHashCode()
    {
        return (Hue.GetHashCode() * 0x100000) + (Metallic.GetHashCode() * 0x1000) + BasicPallete.GetHashCode();
    }
}
Enter fullscreen mode Exit fullscreen mode

6)无副作用

这是基本规则。如果没有这条规则,Value 对象就可以被视为属性的简单容器。要理解它,我们应该首先理解无副作用函数。无副作用函数会产生结果,但不会改变其自身状态。这类函数在函数式编程范式中至关重要。

值对象公开的所有方法都应该是无副作用的函数,这样就不会违反值对象的不可变性。

我们以汽车重新喷漆为例。我们可以通过在类中添加这样的函数来实现重新喷漆。

public class Color
{
    public Color(string hue, bool metallic)
    {
        Hue = hue;
        Metallic = metallic;
        if (hue == "white" && !metallic)
        {
            BasicPallete = true;
        }
     }

    public string Hue { get; }
    public bool Metallic { get; }
    public bool BasicPallete { get; }

    public Color Repaint(string hue, bool metallic)
    {
        return new Color(hue, metallic);
    }

    // Value Equality part
    {...}
}
Enter fullscreen mode Exit fullscreen mode

正如我们所见,这与汽车重新喷漆的情况类似。但方法和值对象之间的联系更紧密,且没有副作用。我们没有修改颜色对象的状态,而是创建了一个具有所需值的新对象。

Color color = new Color("green", false);
color = color.Repaint("green", true);
Enter fullscreen mode Exit fullscreen mode

概括

当然,我并没有涵盖所有与值对象相关的主题,更遑论领域驱动设计(DDD),但我希望这篇文章能启发你在项目中更频繁地使用它。

保持冷静,运用价值对象

PS

请不要走极端。

链接:

文章来源:https://dev.to/rafalpienkowski/the-hidden-value-of-the-value-object-1hga