A strongly typed identifier is user-defined data type which serves as an identifier or key that is strongly typed. This is a solution to the "primitive obsession" code smell as mentioned by Martin Fowler. The data type should preferably be immutable if possible. It is common for implementations to handle equality testing, serialization and model binding.
The strongly typed identifier commonly wraps the data type used as the primary key in the database, such as a string, an integer or universally unique identifier (UUID).
Web frameworks can often be configured to model bind properties on view models that are strongly typed identifiers. Object–relational mappers can often be configured with value converters to map data between the properties on a model using strongly typed identifier data types and database columns.
Passing a strongly typed identifier throughout the layers of an example application.
C# have records which provide immutability and equality testing. [1] The record is sealed to prevent inheritance. [2] It overrides the built-in ToString()
method. [3]
This example implementation includes a static method which can be used to initialize a new instance with a randomly generated globally unique identifier (GUID).
/// <summary>/// Represents a user identifier./// </summary>/// <param name="Id">The user identifier.</param>publicsealedrecordUserId(GuidId){/// <summary>/// Initializes a new instance of the <see cref="UserId" /> record./// </summary>/// <returns>A new UserId object.</returns>publicstaticUserIdNew()=>new(Guid.NewGuid());publicoverridestringToString()=>Id.ToString();}
C++ have structs but not immutability so here the id field is marked as private with a method named value()
to get the value.
structUserId{UserId(conststring_id){id=_id;}stringvalue()const{returnid;}booloperator==(constUserId&rhs)const{returnvalue()==rhs.value();}private:stringid;};ostream&operator<<(ostream&os,constUserId&id){returnos<<id.value()<<std::endl;}
Crystal's standard library provides the record macro for creating records which are immutable structs and lets you create override the built-in to_s
method. [4]
require"uuid"# Represents a user identifier.recordUserId,id:Stringdodefinitialize()@id=UUID.v4.to_senddefto_s(io)io<<idenddefself.emptyself.new(UUID.empty.to_s)endend
importstd;/** Represents a user identifier. */immutablestructUserId{immutableUUIDid;/** Initializes a new instance of the UserId struct. */this(immutablestringid){this.id=UUID(id);}publicstaticUserIdcreate(){returnUserId(randomUUID.toString());}stringtoString(){returnthis.id.toString();}}
Dart have classes with operator overloading.
import'package:meta/meta.dart';/// Represents a user identifier.@immutablefinalclassUserId{finalStringid;/// Initializes a new instance of the UserId struct.constUserId(this.id);@overrideoperator==(other)=>otherisUserId&&other.id==id;@overrideintgethashCode=>id.hashCode;@overrideStringtoString()=>id;}
F# lets you create override the Equals
, GetHashCode
and ToString
methods.
openSystem/// <summary>/// Represents a user identifier./// </summary>/// <param name="id">The user identifier.</param>typeUserId(id:Guid)=memberx.id=idstaticmemberNew()=Guid.NewGuid()staticmemberEmpty=Guid.Emptyoverridex.Equals(b)=matchbwith|:?UserIdasp->id=p.id|_->falseoverridex.GetHashCode()=hashidoverridex.ToString()=id.ToString()
Go have structs which provide equality testing. Go however does not provide immutability.
// Represents a user identifier.typeUserIdstruct{idstring}// Creates a new user identifier.funcNewUserId(idstring)UserId{returnUserId{id:id}}func(xUserId)String()string{returnx.id}
Groovy have record classes which provide immutability and equality testing. [6]
/** * Represents a user identifier. * * @param id The user identifier. */recordUserId(Stringid){StringtoString(){id}}
Haskell can create user-defined custom data types using the newtype
keyword. [7] It provides equality testing using the Eq
standard class and printing using the Read
and Show
standard classes.
-- Represents a user identifier.newtypeUserId=UserIdStringderiving(Eq,Read,Show)
Java have records which provide equality testing. [8] The record is declared using the final
modifier keyword to prevent inheritance. It overrides the built-in toString()
method.
importjava.util.UUID;/** * Represents a user identifier. * @param id The user identifier. */publicfinalrecordUserId(UUIDid){/** * Initializes a new instance of the UserId record. * @return A new UserId object. */publicstaticUserIdnewId(){returnnewUserId(UUID.randomUUID());}publicStringtoString(){returnid.toString();}}
This JavaScript example implementation provides the toJSON
method used by the JSON.stringify()
[9] function to serialize the class into a simple string instead of a composite data type. It calls Object.freeze()
to make the instance immutable. [10] It overrides the built-in toString()
method [11] and the valueOf()
method. [12]
classUserId{#id;constructor(id){if(id==undefined){thrownewTypeError("Argument is null or undefined.");}this.#id=id;Object.freeze(this);}staticempty=newthis.prototype.constructor("00000000-0000-0000-0000-000000000000");staticnew(){returnnewthis.prototype.constructor(crypto.randomUUID());}equals(id){returnidinstanceofthis.constructor&&this.#id===id.valueOf();}toJSON(){returnthis.#id;}toString(){returnthis.#id;}valueOf(){returnthis.#id;}}
Julia have immutable composite data types. [13]
usingUUIDs"Represents a user identifier."structUserIdid::UUIDendBase.string(userId::UserId)=userId.id
Kotlin have "inline classes". [14]
/** * Represents a user identifier. * * @property id The user identifier. * @constructor Creates a user identifier. */@JvmInlinepublicvalueclassUserId(publicvalid:String){overridefuntoString()=id}
Nim have "distinct types". [15] [16]
## Represents a user identifier.typeUserId*=distinctstring
This PHP example implementation implements the __toString()
magic method. [17] Furthermore, it implements the JsonSerializable
interface which is used by the built-in json_encode
function to serialize the class into a simple string instead of a composite data type. [18] The class is declared using the final
modifier keyword to prevent inheritance. [19] PHP has traits as a way to re-use code. [20]
/** * Represents a user identifier. */finalclassUserIdimplementsJsonSerializable{useStronglyTypedIdentifier;}/** * Provides methods for use with strongly typed identifiers. */traitStronglyTypedIdentifier{/** * Initializes a new instance of the UserId object. * @param string $id The user identifier. */publicfunction__construct(publicreadonlystring$id){}/** * Creates a new user identifier. */publicstaticfunctionnew():self{returnnewself(bin2hex(random_bytes(16)));}publicfunctionjsonSerialize():string{return$this->id;}publicfunction__toString():string{return$this->id;}}
Python has data classes which provides equality testing and can be made immutable using the frozen
parameter. [21] It overrides the __str__
dunder method. [22]
This example implementation includes a static method which can be used to initialize a new instance with a randomly generated universally unique identifier (UUID).
fromdataclassesimportdataclassimportuuid@dataclass(frozen=True)classUserId:"""Represents a user identifier."""id:uuid.UUID@staticmethoddefnew()->Self:"""Create a new user identifier."""return__class__(uuid.uuid4())def__str__(self):returnstr(self.id)
Python also has NewType
which can be used to create new data types. [23]
fromtypingimportNewTypeUserId=NewType('UserId',int)
Ruby have data classes which provides equality testing and are immutable. [24] It overrides the built-in to_s
method.
This example implementation includes a static method which can be used to initialize a new instance with a randomly generated universally unique identifier (UUID).
require'securerandom'# Represents a user identifier.UserId=Data.define(:id)do# Create a new user identifier.defself.createself.new(SecureRandom.uuid)enddefself.emptyself.new('00000000-0000-0000-0000-000000000000')enddefto_sidendend
In Rust this can be done using a tuple struct containing a single value. [25] This example implementation implements the Debug
[26] and the PartialEq
[27] traits. The PartialEq
trait provides equality testing.
// Represents a user identifier.#[derive(Debug, PartialEq)]pubstructUserId(String);
Scala have case classes which provide immutability and equality testing. [28] The case class is sealed to prevent inheritance.
importjava.util.UUID/** Represents a user identifier. * * @constructor * Create a new user identifier. * @param id * The user identifier. */sealedcaseclassUserId(id:UUID)objectUserId:/** Initializes a new instance of the UserId class. */defcreate():UserId=UserId(UUID.randomUUID())
Swift have the CustomStringConvertible
protocol which can be used to provide its own representation to be used when converting an instance to a string, [29] and the Equatable
protocol which provides equality testing. [30]
importFoundation/// Represents a user identifier.structUserId:CustomStringConvertible,Equatable{privateletid:UUIDinit(_id:UUID){self.id=id}vardescription:String{returnid.uuidString.lowercased}/// Creates a new user identifier.staticfuncnew()->Self{returnSelf(UUID())}}
Zig have structs [31] with constants but by design does not have operator overloading [32] and method overriding.
/// Represents a user identifier.constUserId=struct{value:i32,/// Initializes a new instance of the UserId struct.pubfninit(value:i32)UserId{returnUserId{.value=value};}};
In computer programming, lazy initialization is the tactic of delaying the creation of an object, the calculation of a value, or some other expensive process until the first time it is needed. It is a kind of lazy evaluation that refers specifically to the instantiation of objects or other resources.
In object-oriented programming, the decorator pattern is a design pattern that allows behavior to be added to an individual object, dynamically, without affecting the behavior of other instances of the same class. The decorator pattern is often useful for adhering to the Single Responsibility Principle, as it allows functionality to be divided between classes with unique areas of concern as well as to the Open-Closed Principle, by allowing the functionality of a class to be extended without being modified. Decorator use can be more efficient than subclassing, because an object's behavior can be augmented without defining an entirely new object.
In object-oriented (OO) and functional programming, an immutable object is an object whose state cannot be modified after it is created. This is in contrast to a mutable object, which can be modified after it is created. In some cases, an object is considered immutable even if some internally used attributes change, but the object's state appears unchanging from an external point of view. For example, an object that uses memoization to cache the results of expensive computations could still be considered an immutable object.
In object-oriented programming languages, a mixin is a class that contains methods for use by other classes without having to be the parent class of those other classes. How those other classes gain access to the mixin's methods depends on the language. Mixins are sometimes described as being "included" rather than "inherited".
In computer programming, a function object is a construct allowing an object to be invoked or called as if it were an ordinary function, usually with the same syntax. In some languages, particularly C++, function objects are often called functors.
In class-based, object-oriented programming, a constructor is a special type of function called to create an object. It prepares the new object for use, often accepting arguments that the constructor uses to set required member variables.
In computer science, a mutator method is a method used to control changes to a variable. They are also widely known as setter methods. Often a setter is accompanied by a getter, which returns the value of the private member variable. They are also known collectively as accessors.
In some programming languages, const is a type qualifier that indicates that the data is read-only. While this can be used to declare constants, const in the C family of languages differs from similar constructs in other languages in that it is part of the type, and thus has complicated behavior when combined with pointers, references, composite data types, and type-checking. In other languages, the data is not in a single memory location, but copied at compile time for each use. Languages which use it include C, C++, D, JavaScript, Julia, and Rust.
The One Definition Rule (ODR) is an important rule of the C++ programming language that prescribes that classes/structs and non-inline functions cannot have more than one definition in the entire program and template and types cannot have more than one definition by translation unit. It is defined in the ISO C++ Standard 2003, at section 3.2. Some other programming languages have similar but differently defined rules towards the same objective.
In the Java programming language, the final
keyword is used in several contexts to define an entity that can only be assigned once.
In computer programming, the term hooking covers a range of techniques used to alter or augment the behaviour of an operating system, of applications, or of other software components by intercepting function calls or messages or events passed between software components. Code that handles such intercepted function calls, events or messages is called a hook.
The syntax of JavaScript is the set of rules that define a correctly structured JavaScript program.
A class in C++ is a user-defined type or data structure declared with any of the keywords class
, struct
or union
that has data and functions as its members whose access is governed by the three access specifiers private, protected or public. By default access to members of a C++ class declared with the keyword class
is private. The private members are not accessible outside the class; they can be accessed only through member functions of the class. The public members form an interface to the class and are accessible outside the class.
C++11 is a version of a joint technical standard, ISO/IEC 14882, by the International Organization for Standardization (ISO) and International Electrotechnical Commission (IEC), for the C++ programming language. C++11 replaced the prior version of the C++ standard, named C++03, and was later replaced by C++14. The name follows the tradition of naming language versions by the publication year of the specification, though it was formerly named C++0x because it was expected to be published before 2010.
In computer science, a value object is a small object that represents a simple entity whose equality is not based on identity: i.e. two value objects are equal when they have the same value, not necessarily being the same object.
In software engineering, a fluent interface is an object-oriented API whose design relies extensively on method chaining. Its goal is to increase code legibility by creating a domain-specific language (DSL). The term was coined in 2005 by Eric Evans and Martin Fowler.
In object-oriented computer programming, a null object is an object with no referenced value or with defined neutral (null) behavior. The null object design pattern, which describes the uses of such objects and their behavior, was first published as "Void Value" and later in the Pattern Languages of Program Design book series as "Null Object".
This article describes the syntax of the C# programming language. The features described are compatible with .NET Framework and Mono.
In computer programming, variadic templates are templates that take a variable number of arguments.
In computer programming, a constant is a value that is not altered by the program during normal execution. When associated with an identifier, a constant is said to be "named," although the terms "constant" and "named constant" are often used interchangeably. This is contrasted with a variable, which is an identifier with a value that can be changed during normal execution. To simplify, constants' values remains, while the values of variables varies, hence both their names.