
Un identificador fuertemente tipado es un tipo de dato definido por el usuario que funciona como identificador o clave . Esta es una solución al problema de la "obsesión por los tipos primitivos " , mencionado por Martin Fowler . El tipo de dato debería ser inmutable, si es posible. Es común que las implementaciones gestionen la comprobación de igualdad, la serialización y la vinculación de modelos.
El identificador fuertemente tipado suele encapsular el tipo de dato utilizado como clave primaria en la base de datos, como una cadena de caracteres, un número entero o un identificador único universal (UUID).
Los frameworks web suelen configurarse para vincular propiedades de modelos de vista que son identificadores fuertemente tipados. Los mapeadores objeto-relacionales suelen configurarse con convertidores de valores para mapear datos entre las propiedades de un modelo utilizando tipos de datos de identificadores fuertemente tipados y columnas de bases de datos.
Ejemplos
Ejemplo de cómo pasar un identificador fuertemente tipado a través de las capas de una aplicación.
![]()
DO#
C# tiene registros que proporcionan inmutabilidad y prueba de igualdad. [ 1 ] El registro está sellado para evitar la herencia . [ 2 ] Sobrescribe el método integrado ToString(). [ 3 ]
Esta implementación de ejemplo incluye un método estático que se puede utilizar para inicializar una nueva instancia con un identificador único global (GUID) generado aleatoriamente.
/// <summary> /// Representa un identificador de usuario. /// </summary> /// <param name="Id">El identificador de usuario.</param> public sealed record UserId ( Guid Id ) { /// <summary> /// Inicializa una nueva instancia del registro <see cref="UserId" />. /// </summary> /// <returns>Un nuevo objeto UserId.</returns> public static UserId New () => new ( Guid . NewGuid ());public override string ToString () => Id . ToString (); }C++
C++ tiene estructuras pero no inmutabilidad, por lo que aquí el campo id está marcado como privado con un método llamado getId()para obtener el valor.
importar std ;usando std :: string ;clase UserId { privado : const string id ; público : explicit UserId ( const string & id ) : id { id } {}[[ nodiscard ]] string getId () const noexcept { return id ; }[[ nodiscard ]] bool operator == ( const UserId & rhs ) const noexcept { return id == rhs . getId (); } };Cristal
La biblioteca estándar de Crystal proporciona la macro de registro para crear registros que son estructuras inmutables y permite crear sobrescribir el método integrado to_s. [ 4 ]
Requerir "uuid"# Representa un identificador de usuario. record UserId , id : String do def initialize () @id = UUID . v4 . to_s enddef to_s ( io ) io << id findef self.empty self.new ( UUID.empty.to_s ) end endD
D tienen estructuras inmutables. [ 5 ]
importar std ;/** Representa un identificador de usuario. */ struct UserId inmutable { UUID inmutable id ;/** Inicializa una nueva instancia de la estructura UserId. */ this ( cadena inmutable id ) { this . id = UUID ( id ); }public static UserId create () { return UserId ( randomUUID . toString ()); }cadena toString ( ) { return this.id.toString ( ) ; } }Dardo
Dart tiene clases con sobrecarga de operadores.
import 'package:meta/meta.dart' ;/// Representa un identificador de usuario. @immutable final class UserId { final String id ;/// Inicializa una nueva instancia de la estructura UserId. const UserId ( this . id );@override operator == ( other ) => other is UserId && other . id == id ; @override int get hashCode => id . hashCode ; @override String toString () => id ; }F#
F# te permite crear y sobrescribir los métodos Equals, GetHashCodey ToString.
Sistema abierto/// <summary> /// Representa un identificador de usuario. /// </summary> /// <param name="id">El identificador de usuario.</param> type UserId ( id : Guid ) = member x . id = id static member New () = Guid . NewGuid () static member Empty = Guid . Empty override x . Equals ( b ) = match b with | :? UserId as p - > id = p . id | _ -> false override x . GetHashCode () = hash id override x . ToString () = id . ToString ()Ir
Go tiene estructuras que permiten realizar pruebas de igualdad. Sin embargo, Go no proporciona inmutabilidad.
// Representa un identificador de usuario. type UserId struct { id string }// Crea un nuevo identificador de usuario. func NewUserId ( id string ) UserId { return UserId { id : id } }func ( x UserId ) String () string { return x . id }Genial
Groovy tiene clases de registro que proporcionan inmutabilidad y pruebas de igualdad. [ 6 ]
/** * Representa un identificador de usuario. * * @param id El identificador de usuario. */ record UserId ( String id ) { String toString () { id } }Haskell
Haskell puede crear tipos de datos personalizados definidos por el usuario usando la newtypepalabra clave. [ 7 ] Proporciona pruebas de igualdad usando la Eqclase estándar e impresión usando las clases estándar Ready .Show
-- Representa un identificador de usuario. newtype UserId = UserId String deriving ( Eq , Read , Show )Java
Java tiene registros que proporcionan pruebas de igualdad. [ 8 ] El registro se declara usando la palabra clave modificadora para evitar la herencia. Sobrescribe el método finalintegrado .toString()
import java.util.UUID ;/** * Representa un identificador de usuario. * @param id El identificador de usuario. */ public final record UserId ( UUID id ) { /** * Inicializa una nueva instancia del registro UserId. * @return Un nuevo objeto UserId. */ public static UserId newId () { return new UserId ( UUID . randomUUID ()); }public String toString () { return id . toString (); } }JavaScript
Esta implementación de ejemplo en JavaScript proporciona el toJSONmétodo utilizado por la función JSON.stringify()[ 9 ] para serializar la clase en una cadena simple en lugar de un tipo de datos compuesto . Llama Object.freeze()a para hacer que la instancia sea inmutable. [ 10 ]toString() Sobrescribe el método integrado [ 11 ] y el valueOf()método. [ 12 ]
clase UserId { #id ;constructor ( id ) { if ( id == undefined ) { throw new TypeError ( "El argumento es nulo o indefinido." ); } this . #id = id ; Object . freeze ( this ); }static empty = new this . prototype . constructor ( "00000000-0000-0000-0000-000000000000" );static new () { return new this . prototype . constructor ( crypto . randomUUID ()); }equals ( id ) { return id instanceof this.constructor && this . # id === id.valueOf ( ) ; }toJSON () { return this . #id ; }toString () { return this . #id ; }valueOf () { return this . #id ; } }Julia
Julia tiene tipos de datos compuestos inmutables. [ 13 ]
utilizando UUIDs"Representa un identificador de usuario." struct UserId id :: UUID endBase.string ( userId :: UserId ) = userId.idKotlin
Kotlin tiene "clases en línea". [ 14 ]
/** * Representa un identificador de usuario. * * @property id El identificador de usuario. * @constructor Crea un identificador de usuario. */ @JvmInline public value class UserId ( public val id : String ) { override fun toString () = id }Nim
Los Nim tienen "tipos distintos". [ 15 ] [ 16 ]
## Representa un identificador de usuario. tipo UserId * = cadena distintaPHP
Esta implementación de ejemplo en PHP implementa el __toString()método mágico. [ 17 ] Además, implementa la JsonSerializableinterfaz que utiliza la función integrada json_encodepara serializar la clase en una cadena simple en lugar de un tipo de datos compuesto . [ 18 ] La clase se declara utilizando la finalpalabra clave modificadora para evitar la herencia. [ 19 ] PHP tiene traits como una forma de reutilizar código. [ 20 ]
/** * Representa un identificador de usuario. */ final class UserId implements JsonSerializable { use StronglyTypedIdentifier ; }/** * Proporciona métodos para usar con identificadores fuertemente tipados. */ trait StronglyTypedIdentifier { /** * Inicializa una nueva instancia del objeto UserId. * @param string $id El identificador de usuario. */ public function __construct ( public readonly string $id ) {}/** * Crea un nuevo identificador de usuario. */ public static function new () : self { return new self ( bin2hex ( random_bytes ( 16 ))); }función pública jsonSerialize () : string { return $this -> id ; }función pública __toString () : string { return $this -> id ; } }Pitón
Python tiene clases de datos que proporcionan pruebas de igualdad y se pueden hacer inmutables usando el frozenparámetro. [ 21 ] Sobrescribe el __str__método dunder. [ 22 ]
Esta implementación de ejemplo incluye un método estático que se puede utilizar para inicializar una nueva instancia con un identificador único universal (UUID) generado aleatoriamente.
import uuid from dataclasses import dataclass from uuid import UUID@dataclass ( frozen = True ) class UserId : """Representa un identificador de usuario."""id : UUID@staticmethod def new () -> Self : """Crea un nuevo identificador de usuario.""" return __class__ ( uuid . uuid4 ())def __str__ ( self ) -> str : return str ( self . id )Python también tiene NewTypeque se puede usar para crear nuevos tipos de datos. [ 23 ]
from typing import NewTypeUserId : NewType = NewType ( 'UserId' , int )Raku
RakuStr tiene clases que proporcionan pruebas de igualdad y son inmutables. Sobrescribe el método integrado .
Este ejemplo sobrescribe el método gist predeterminado. [ 24 ] Utiliza roles que se mezclan en clases para que sean reutilizables. [ 25 ]
rol StronglyTypedIdentifier { tiene Str $.id es obligatorio ; multi método nuevo ( Str $id ) { self . bless (: $id ); } multi method gist ( StronglyTypedIdentifier:U: ) { self .^ name } multi method gist ( StronglyTypedIdentifier:D: ) { self . id } método Str () { "$!id" } método vacío () { self . nuevo ( "00000000-0000-0000-0000-000000000000" ) } } La clase UserId hace StronglyTypedIdentifier {} Rubí
Ruby tiene clases de datos que proporcionan pruebas de igualdad y son inmutables. [ 26 ] Sobrescribe el método incorporado to_s.
Esta implementación de ejemplo incluye un método estático que se puede utilizar para inicializar una nueva instancia con un identificador único universal (UUID) generado aleatoriamente.
requerir 'aleatorio seguro'# Representa un identificador de usuario. UserId = Data.define ( : id ) do # Crea un nuevo identificador de usuario . def self.create self.new ( SecureRandom.uuid ) enddef self.empty self.new ( ' 00000000-0000-0000-0000-000000000000 ' ) enddef to_s id fin finÓxido
En Rust esto se puede hacer usando una estructura de tupla que contiene un solo valor. [ 27 ] Esta implementación de ejemplo implementa los rasgos Debug[ 28 ] y PartialEq[ 29 ] . El rasgo proporciona pruebas de igualdad.PartialEq
// Representa un identificador de usuario. #[derive(Debug, PartialEq)] pub struct UserId ( String );Scala
Scala tiene clases de caso que proporcionan inmutabilidad y pruebas de igualdad. [ 30 ] La clase de caso está sellada para evitar la herencia.
import java.util.UUID/** Representa un identificador de usuario. * * @constructor * Crea un nuevo identificador de usuario. * @param id * El identificador de usuario. */ sealed case class UserId ( id : UUID )objeto UserId : /** Inicializa una nueva instancia de la clase UserId. */ def create ( ): UserId = UserId ( UUID.randomUUID ( ))Rápido
Swift tiene el CustomStringConvertibleprotocolo que se puede usar para proporcionar su propia representación para ser utilizada al convertir una instancia a una cadena, [ 31 ] y el Equatableprotocolo que proporciona pruebas de igualdad. [ 32 ]
Fundación de importación/// Representa un identificador de usuario. struct UserId : CustomStringConvertible , Equatable { private let id : UUIDinit ( _ id : UUID ) { self . id = id }var descripción : String { return id.uuidString.lowercased }/// Crea un nuevo identificador de usuario. static func new () -> Self { return Self ( UUID ()) } }Mecanografiado
TypeScript tiene unique symbol[ 33 ] que se pueden usar para crear "tipos de marca".
/** Representa un identificador de usuario. */ export type UserId = string & { readonly __brand : unique symbol };Zig
Zig tiene estructuras [ 34 ] con constantes pero por diseño no tiene sobrecarga de operadores [ 35 ] ni anulación de métodos.
/// Representa un identificador de usuario. const UserId = struct { value : i32 ,/// Inicializa una nueva instancia de la estructura UserId. pub fn init ( value : i32 ) UserId { return UserId { . value = value }; } };Véase también
Referencias
- ↑ "Registros - Referencia de C#" . learn.microsoft.com . Consultado el 23 de enero de 2023 .
- ↑ "modificador sellado - Referencia de C#" . learn.microsoft.com . Consultado el 23 de enero de 2023 .
- ↑ "Método Object.ToString (System)" . learn.microsoft.com . Consultado el 14 de junio de 2023 .
- ↑ "Estructuras - Crystal" . crystal-lang.org . Consultado el 21 de febrero de 2024 .
- ↑ "Estructuras, Uniones - Lenguaje de Programación D" . dlang.org . Consultado el 30 de mayo de 2023 .
- ↑ "El lenguaje de programación Apache Groovy - Orientación a objetos" . groovy-lang.org . Consultado el 24 de diciembre de 2023 .
- ↑ "Newtype - HaskellWiki" . wiki.haskell.org . Consultado el 18 de junio de 2023 .
- ↑ "Clases de registro" . Centro de ayuda de Oracle . Consultado el 24 de enero de 2023 .
- ↑ "JSON.stringify() - JavaScript | MDN" . developer.mozilla.org . Consultado el 23 de enero de 2023 .
- ↑ "Object.freeze() - JavaScript | MDN" . developer.mozilla.org . Consultado el 23 de enero de 2023 .
- ↑ "Object.prototype.toString() - JavaScript | MDN" . developer.mozilla.org . Consultado el 23 de enero de 2023 .
- ↑ "Object.prototype.valueOf() - JavaScript | MDN" . developer.mozilla.org . Consultado el 23 de enero de 2023 .
- ↑ "Tipos · El lenguaje Julia" . docs.julialang.org . Consultado el 30 de mayo de 2023 .
- ↑ "Clases en línea | Kotlin" . Ayuda de Kotlin . Consultado el 23 de enero de 2023 .
- ↑ "Manual de Nim" . nim-lang.org . Consultado el 4 de agosto de 2023 .
- ↑ "Nim by Example - Distinct Types" . nim-by-example.github.io . Consultado el 4 de agosto de 2023 .
- ↑ "PHP: Métodos mágicos - Manual" . www.php.net . Consultado el 23 de enero de 2023 .
- ↑ "PHP: JsonSerializable::jsonSerialize - Manual" . www.php.net . Consultado el 23 de enero de 2023 .
- ↑ "PHP: Final Keyword - Manual" . www.php.net . Consultado el 23 de enero de 2023 .
- ↑ "PHP: Traits - Manual" . www.php.net . Consultado el 2 de mayo de 2023 .
- ↑ "dataclasses — Clases de datos" . Documentación de Python . Python Software Foundation . Consultado el 23 de enero de 2023 .
- ↑ "3. Modelo de datos" . Documentación de Python . Python Software Foundation . Consultado el 12 de junio de 2023 .
- ↑ "typing — Soporte para sugerencias de tipo" . Documentación de Python . Python Software Foundation . Consultado el 17 de junio de 2023 .
- ↑ "Clases y objetos | Documentación de Raku" . docs.raku.org . Consultado el 30 de diciembre de 2025 .
- ↑ "Orientación a objetos | Documentación de Raku" . docs.raku.org . Consultado el 30 de diciembre de 2025 .
- ↑ "Datos de clase - Documentación para Ruby 3.3" . docs.ruby-lang.org . Consultado el 6 de febrero de 2023 .
- ↑ "Nuevo modismo de tipos - Rust mediante ejemplos" . doc.rust-lang.org . Consultado el 18 de junio de 2023 .
- ↑ "Depuración en std::fmt - Rust" . doc.rust-lang.org . Consultado el 23 de enero de 2023 .
- ↑ "PartialEq en std::cmp - Rust" . doc.rust-lang.org . Consultado el 23 de enero de 2023 .
- ↑ "Clases de caso" . Documentación de Scala . Consultado el 15 de mayo de 2023 .
- ↑ "CustomStringConvertible" . Documentación para desarrolladores de Apple . Consultado el 5 de mayo de 2023 .
- ↑ "Documentación" . docs.swift.org . Consultado el 4 de mayo de 2023 .
- ↑ "Documentación - Símbolos" . www.typescriptlang.org . Consultado el 16 de mayo de 2026 .
- ↑ "Estructuras | zig.guide" . zig.guide . 20 de abril de 2024. Consultado el 15 de octubre de 2024 .
- ↑ "Documentación - El lenguaje de programación Zig" . Consultado el 15 de octubre de 2024 .
Enlaces externos
- https://wiki.c2.com/?PrimitiveObsession
- Tipos de datos
- patrones de diseño de software