Value Objects

Contents[Show]

A value object is a small object whose equality is based on state, but not identity. Typical value objects are money, numbers, or strings.

ClassIdioms

 

The term Value Object goes back to the seminal book Domain-Driven Design (DDD) by Eric Evans. But, what is a value object? Eric gave the answer in his book:

An object that represents a descriptive aspect of the domain with no conceptual identity is called a Value Object. Value Objects are instantiated to represent elements of the design that we care about only for what they are, not who or which they are.

If this is too formal for you, here is a nice example of the author:

When a child is drawing, he cares about the color of the marker he chooses, and he may care about the sharpness of the tip. But if there are two markers of the same color and shape, he probably won’t care which one he uses. If a marker is lost and replaced by another of the same color from a new pack, he can resume his work unconcerned about the switch.

The key term in the formal definition and the example about the Value Object is equality.

Identity

In general, we have two types of equality: reference equality and value equality. For simplicity reasons, I will ignore id-based equality.

  • Reference equality: two objects are considered to be equal if they reference the same entity in the memory.
  • Value equality: two objects are considered to be equal if all their member have the same value.

Let me switch to Python because Python makes it easy to compare both kinds of equality.

EqualityPython

The short example in the Python shell should make the difference between reference equality and value equality clear.

First, I define two lists list1 and list2 with the same elements. When I compare their identity (list1 is list2), they are different. When I compare their values, they are identical. Python uses for equality (and non-equality) comparison of the memory address of the compared objects. The id operator (id(liste1)) returns a decimal representation of its hexadecimal memory address.  By assigning list1 to list3, both lists refer to the same memory location. Consequentially, id(list3) is identical to id(list1), and the call list1 is list3 returns True.

What does this mean for modern C++20? In C++20, the compiler can generate the equality operator.

Compiler-Generated Equality Operator

 For a user-defined type, you have to choose the appropriate equality semantics.

// equalityReferenceValue.cpp

#include <iostream>

class Date{
 public:
    Date(int y, int m, int d): year(y), month(m), day(d){}
    bool operator==(const Date&) const = default;
 private:
    int year;
    int month;
    int day;
};

class Man{
 public:
    Man(const std::string n, int a): name(n), age(a){}
    bool operator==(const Man&) const = default;
 private:
    std::string name;
    int age;
};

int main() {

    std::cout << std::boolalpha << '\n';

    Date date1(2022, 10, 31);
    Date date2(2022, 10, 31);

    std::cout << "date1 == date2: " << (date1 == date2) << '\n';
    std::cout << "date1 != date2: " << (date1 != date2) << '\n';

    std::cout << '\n';

    Man man1("Rainer Grimm", 56);
    Man man2("Rainer Grimm", 56);

    std::cout << "man1 == man2: " << (man1 == man2) << '\n';
    std::cout << "man1 != man2: " << (man1 != man2) << '\n';

    std::cout << '\n';

}

 

In C++20, the compiler can auto-generate the equality operator and use it as a fallback for the inequality operator. The auto-generated equality operator applies value equality. To be more precise, the compiler-generated equality operator performs a lexicographical comparison. Lexicographical comparison means that all base classes are compared left to right and all nonstatic members of the class in their declaration order.

I have to add two important points:

Honestly, the example works as expected but does not seem right.

 equalityReferenceValue

Two dates with identical values should be regarded as equal but not two men. The equality of two men should be based on their identity and not on their name and age.

 

Rainer D 6 P2 540x540Modernes C++ Mentoring

Be part of my mentoring programs:

 

 

 

 

Do you want to stay informed about my mentoring programs: Subscribe via E-Mail.

Value Objects

Let me discuss the details about Value Objects.

Properties

Value Equality

After the last chapter, this should be obvious. The equality of a Value Object should be based on its state and not on its identity.

Immutability

A Value Object should not be mutable. This makes Value Objects ideal candidates for concurrency. Changing a Value Object means creating a new one with the modified attributes. This property, that an operation on an immutable object returns a new object has two excellent properties. For conciseness, I use Python once more.

In Python, a string is immutable:

immutable

  1. You simulate modification, by assigning the new value to the old name: s = s.upper(). The original s and the new s have different addresses.
  2. An operation on the string returns a new string. Consequentially, you can chain string operations. In the function domain, this pattern is called a fluent interface. By the way: arithmetic expressions such as (5 +5) * 10 - 20 are based on the fluent interface. Each operation returns a temporary, on which you can apply the next operation. Of course, numbers are Value Objects.

Self-Validation

A Value Object should validate its attributes when created. For simplicity, I skipped this step in my previous Date class.

What are the Pros and Cons of Value Objects:

Pros and Cons

The pros of Value Objects overweight their cons heavily.

Rich Types

You should use rich types instead of built-in types for dealing with primitive values. This has many implications. Let me compare the following two representations of a date:

Date date1(2022, 10, 5);

std::string date2 = "2022 10 5";  

 

  • You cannot create an invalid date Date(2022, 15, 5), because the constructor's job is it to validate the input. This does not hold for the string value "2022 15 5", because someone mixed up the month and the day.
  • Your program is easier to read. It is crystal clear from class Date documentation, what each component stands for.
  • You can overload operators on Date. E.g.: subtracting two dates returns a time duration. A time duration should also be a Value Object.
  • You can extend your Value Objects with user-defined literals for a day, a year, and a month. In this case, it is not necessary because we have them with C++20: std::chrono::duration on cppreference.com.

Performance

Value Objects are immutable. Consequentially, they give the optimizer additional guarantees and can be shared between threads without synchronization.

Proliferation of Classes

Only for the sake of arguments: You may end with too many small classes representing Value Objects.

What's Next?

A Null Object encapsulates a do nothing behavior inside an object. Let me show you in my next post the advantages of Null Objects.

 

 

Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Animus24, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Kris Kafka, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Matthieu Bolt, Stephen Kelley, Kyle Dean, Tusar Palauri, Dmitry Farberov, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, and Rob North.

 

Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, and Slavko Radman.

 

 

My special thanks to Embarcadero CBUIDER STUDIO FINAL ICONS 1024 Small

 

My special thanks to PVS-Studio PVC Logo

 

My special thanks to Tipi.build tipi.build logo

 

My special thanks to Take Up code TakeUpCode 450 60

 

Seminars

I'm happy to give online seminars or face-to-face seminars worldwide. Please call me if you have any questions.

Bookable (Online)

German

Standard Seminars (English/German)

Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.

  • C++ - The Core Language
  • C++ - The Standard Library
  • C++ - Compact
  • C++11 and C++14
  • Concurrency with Modern C++
  • Design Pattern and Architectural Pattern with C++
  • Embedded Programming with Modern C++
  • Generic Programming (Templates) with C++

New

  • Clean Code with Modern C++
  • C++20

Contact Me

Modernes C++,

RainerGrimmDunkelBlauSmall

 

 

 

 

Comments   

0 #1 john 2023-02-10 10:19
Re Man. In practice you'd probably also have a unique id to differentiate between different people with the same name and born on the same day.
Quote

Stay Informed about my Mentoring

 

Mentoring

English Books

Course: Modern C++ Concurrency in Practice

Course: C++ Standard Library including C++14 & C++17

Course: Embedded Programming with Modern C++

Course: Generic Programming (Templates)

Course: C++ Fundamentals for Professionals

Course: The All-in-One Guide to C++20

Course: Master Software Design Patterns and Architecture in C++

Subscribe to the newsletter (+ pdf bundle)

All tags

Blog archive

Source Code

Visitors

Today 2330

Yesterday 4371

Week 38137

Month 168262

All 12056028

Currently are 192 guests and no members online

Kubik-Rubik Joomla! Extensions

Latest comments