En la programación orientada a objetos , una clase define los aspectos compartidos de los objetos creados a partir de ella. Las capacidades de una clase difieren entre lenguajes de programación , pero generalmente los aspectos compartidos consisten en estado ( variables ) y comportamiento ( métodos ) que están asociados con un objeto en particular o con todos los objetos de esa clase. [1] [2]
El estado del objeto puede diferir entre cada instancia de la clase, mientras que el estado de la clase es compartido por todas ellas. Los métodos del objeto incluyen acceso al estado del objeto (a través de un parámetro implícito o explícito que hace referencia al objeto), mientras que los métodos de la clase no lo hacen.
Si el lenguaje admite la herencia , se puede definir una clase en base a otra clase con todo su estado y comportamiento, más un estado y comportamiento adicionales que especialicen aún más la clase. La clase especializada es una subclase y la clase en la que se basa es su superclase .
Como instancia de una clase, un objeto se construye a partir de una clase mediante la creación de instancias . Se asigna e inicializa memoria para el estado del objeto y se proporciona una referencia al objeto al código que lo consume. El objeto se puede utilizar hasta que se destruye: se desasigna su memoria de estado.
La mayoría de los lenguajes permiten una lógica personalizada en los eventos del ciclo de vida a través de un constructor y un destructor .
Un objeto expresa el tipo de datos como una interfaz: el tipo de cada variable miembro y la firma de cada función miembro (método). Una clase define una implementación de una interfaz, y la instanciación de la clase da como resultado un objeto que expone la implementación a través de la interfaz. [3] En términos de la teoría de tipos, una clase es una implementación (una estructura de datos concreta y una colección de subrutinas), mientras que un tipo es una interfaz . Diferentes clases (concretas) pueden producir objetos del mismo tipo (abstracto) (según el sistema de tipos). Por ejemplo, el tipo (interfaz) Stack podría implementarse mediante SmallStack , que es rápido para pilas pequeñas pero escala mal, y ScalableStack, que escala bien pero tiene una alta sobrecarga para pilas pequeñas.
Una clase contiene descripciones de campos de datos (o propiedades , campos , miembros de datos o atributos ). Por lo general, se trata de tipos de campos y nombres que se asociarán con variables de estado en el momento de la ejecución del programa; estas variables de estado pertenecen a la clase o a instancias específicas de la clase. En la mayoría de los lenguajes, la estructura definida por la clase determina la disposición de la memoria utilizada por sus instancias. Otras implementaciones son posibles: por ejemplo, los objetos en Python utilizan contenedores asociativos de clave-valor. [4]
Algunos lenguajes de programación, como Eiffel, admiten la especificación de invariantes como parte de la definición de la clase y las hacen cumplir mediante el sistema de tipos. La encapsulación del estado es necesaria para poder hacer cumplir las invariantes de la clase.
El comportamiento de una clase o sus instancias se define mediante métodos . Los métodos son subrutinas con la capacidad de operar sobre objetos o clases. Estas operaciones pueden alterar el estado de un objeto o simplemente proporcionar formas de acceder a él. [5] Existen muchos tipos de métodos, pero el soporte para ellos varía según el lenguaje. Algunos tipos de métodos son creados e invocados por el código del programador, mientras que otros métodos especiales (como constructores, destructores y operadores de conversión) son creados e invocados por el código generado por el compilador. Un lenguaje también puede permitir al programador definir e invocar estos métodos especiales. [6] [7]
Cada clase implementa (o realiza ) una interfaz al proporcionar estructura y comportamiento. La estructura consta de datos y estado, y el comportamiento consta de código que especifica cómo se implementan los métodos. [8] Existe una distinción entre la definición de una interfaz y la implementación de esa interfaz; sin embargo, esta línea es borrosa en muchos lenguajes de programación porque las declaraciones de clase definen e implementan una interfaz. Sin embargo, algunos lenguajes proporcionan características que separan la interfaz y la implementación. Por ejemplo, una clase abstracta puede definir una interfaz sin proporcionar una implementación.
Los lenguajes que admiten la herencia de clases también permiten que las clases hereden interfaces de las clases de las que se derivan.
Por ejemplo, si la "clase A" hereda de la "clase B" y si la "clase B" implementa la interfaz "interfaz B", entonces la "clase A" también hereda la funcionalidad (declaración de constantes y métodos) proporcionada por la "interfaz B".
En los lenguajes que admiten especificadores de acceso, se considera que la interfaz de una clase es el conjunto de miembros públicos de la clase, incluidos tanto los métodos como los atributos (a través de métodos getter y setter implícitos ); los miembros privados o las estructuras de datos internas no están destinados a ser dependientes del código externo y, por lo tanto, no son parte de la interfaz.
La metodología de programación orientada a objetos dicta que las operaciones de cualquier interfaz de una clase deben ser independientes entre sí. El resultado es un diseño en capas en el que los clientes de una interfaz utilizan los métodos declarados en la interfaz. Una interfaz no impone requisitos para que los clientes invoquen las operaciones de una interfaz en un orden determinado. Este enfoque tiene la ventaja de que el código del cliente puede asumir que las operaciones de una interfaz están disponibles para su uso siempre que el cliente tenga acceso al objeto. [9] [ cita requerida ]
Los botones de la parte frontal de su televisor son la interfaz entre usted y el cableado eléctrico que se encuentra al otro lado de la carcasa de plástico. Presiona el botón de encendido para encender y apagar el televisor. En este ejemplo, su televisor en particular es la instancia, cada método está representado por un botón y todos los botones juntos componen la interfaz (otros televisores que sean del mismo modelo que el suyo tendrían la misma interfaz). En su forma más común, una interfaz es una especificación de un grupo de métodos relacionados sin ninguna implementación asociada de los métodos.
Un televisor también tiene una gran cantidad de atributos , como el tamaño y si admite colores, que juntos forman su estructura. Una clase representa la descripción completa de un televisor, incluidos sus atributos (estructura) y botones (interfaz).
Obtener el número total de televisores fabricados podría ser un método estático de la clase television. Este método está asociado con la clase, pero está fuera del dominio de cada instancia de la clase. Un método estático que encuentra una instancia particular del conjunto de todos los objetos televisiones es otro ejemplo.
El siguiente es un conjunto común de especificadores de acceso : [10]
Aunque muchos lenguajes orientados a objetos admiten los especificadores de acceso anteriores, su semántica puede diferir.
El diseño orientado a objetos utiliza los especificadores de acceso junto con un diseño cuidadoso de las implementaciones de métodos públicos para hacer cumplir las invariantes de clase (restricciones sobre el estado de los objetos). Un uso común de los especificadores de acceso es separar los datos internos de una clase de su interfaz: la estructura interna se vuelve privada, mientras que los métodos de acceso públicos se pueden utilizar para inspeccionar o alterar dichos datos privados.
Los especificadores de acceso no necesariamente controlan la visibilidad , ya que incluso los miembros privados pueden ser visibles para el código externo del cliente. En algunos lenguajes, se puede hacer referencia a un miembro inaccesible pero visible en tiempo de ejecución (por ejemplo, mediante un puntero devuelto desde una función miembro), pero el verificador de tipos evitará un intento de usarlo haciendo referencia al nombre del miembro desde el código del cliente. [11]
Los distintos lenguajes de programación orientados a objetos imponen la accesibilidad y visibilidad de los miembros en distintos grados y, dependiendo del sistema de tipos y las políticas de compilación del lenguaje, se aplican en tiempo de compilación o de ejecución . Por ejemplo, el lenguaje Java no permite que se compile el código de cliente que accede a los datos privados de una clase. [12] En el lenguaje C++ , los métodos privados son visibles, pero no accesibles en la interfaz; sin embargo, se pueden hacer invisibles declarando explícitamente clases completamente abstractas que representan las interfaces de la clase. [13]
Algunos idiomas cuentan con otros esquemas de accesibilidad:
En teoría, una superclase es un superconjunto de sus subclases. Por ejemplo, GraphicObject podría ser una superclase de Rectangle y Ellipse , mientras que Square sería una subclase de Rectangle . Todas estas son relaciones de subconjuntos también en la teoría de conjuntos, es decir, todos los cuadrados son rectángulos, pero no todos los rectángulos son cuadrados.
Un error conceptual común es confundir una parte de una relación con una subclase. Por ejemplo, un automóvil y un camión son dos tipos de vehículos y sería adecuado modelarlos como subclases de una clase de vehículo. Sin embargo, sería un error modelar las partes del automóvil como relaciones de subclase. Por ejemplo, un automóvil se compone de un motor y una carrocería, pero no sería adecuado modelar un motor o una carrocería como una subclase de un automóvil.
En el modelado orientado a objetos, este tipo de relaciones se modelan normalmente como propiedades de objeto. En este ejemplo, la clase Car tendría una propiedad llamada parts . parts se tipificaría para contener una colección de objetos, como instancias de Body , Engine , Tires , etc. Los lenguajes de modelado de objetos como UML incluyen capacidades para modelar varios aspectos de "part of" y otros tipos de relaciones: datos como la cardinalidad de los objetos, restricciones sobre los valores de entrada y salida, etc. Esta información puede ser utilizada por las herramientas de desarrollo para generar código adicional además de las definiciones de datos básicas para los objetos, como la comprobación de errores en los métodos get y set . [16]
Una pregunta importante al modelar e implementar un sistema de clases de objetos es si una clase puede tener una o más superclases. En el mundo real con conjuntos reales, sería raro encontrar conjuntos que no se intersecten con más de otro conjunto. Sin embargo, mientras que algunos sistemas como Flavors y CLOS proporcionan una capacidad para que más de un padre lo haga en tiempo de ejecución, introduce una complejidad que muchos en la comunidad orientada a objetos consideran antitética a los objetivos de usar clases de objetos en primer lugar. Comprender qué clase será responsable de manejar un mensaje puede volverse complejo cuando se trata con más de una superclase. Si se usa descuidadamente, esta característica puede introducir algo de la misma complejidad y ambigüedad del sistema que las clases fueron diseñadas para evitar. [17]
La mayoría de los lenguajes orientados a objetos modernos, como Smalltalk y Java, requieren una herencia única en tiempo de ejecución. En estos lenguajes, la herencia múltiple puede ser útil para el modelado, pero no para la implementación.
Sin embargo, los objetos de las aplicaciones web semánticas tienen múltiples superclases. La volatilidad de Internet requiere este nivel de flexibilidad y los estándares tecnológicos como el lenguaje de ontología web (OWL) están diseñados para respaldarlo.
Un problema similar es si la jerarquía de clases se puede modificar en tiempo de ejecución. Lenguajes como Flavors, CLOS y Smalltalk admiten esta característica como parte de sus protocolos de meta-objetos . Dado que las clases son en sí mismas objetos de primera clase, es posible hacer que alteren dinámicamente su estructura enviándoles los mensajes apropiados. Otros lenguajes que se centran más en la tipificación fuerte, como Java y C++, no permiten que la jerarquía de clases se modifique en tiempo de ejecución. Los objetos de la web semántica tienen la capacidad de realizar cambios en las clases en tiempo de ejecución. La lógica es similar a la justificación para permitir múltiples superclases, que Internet es tan dinámica y flexible que se requieren cambios dinámicos en la jerarquía para gestionar esta volatilidad. [18]
Aunque muchos lenguajes basados en clases admiten la herencia, esta no es un aspecto intrínseco de las clases. Un lenguaje basado en objetos (por ejemplo, Visual Basic clásico ) admite clases, pero no admite la herencia.
Un lenguaje de programación puede admitir varias características de relación de clases.
Las clases pueden estar compuestas de otras clases, estableciendo así una relación compositiva entre la clase contenedora y sus clases integradas. La relación compositiva entre clases también se conoce comúnmente como relación tiene-un . [19] Por ejemplo, una clase "Auto" podría estar compuesta de y contener una clase "Motor". Por lo tanto, un Auto tiene un Motor. Un aspecto de la composición es la contención, que es el encierro de instancias de componentes por la instancia que las tiene. Si un objeto contenedor contiene instancias de componentes por valor, los componentes y su objeto contenedor tienen una duración de vida similar . Si los componentes están contenidos por referencia, es posible que no tengan una duración de vida similar. [20] Por ejemplo, en Objective-C 2.0:
@interface Coche : NSObject@property NSString * nombre ; @property Engine * motor @property NSArray * neumáticos ; @fin
Esta clase Car tiene una instancia de NSString (un objeto de cadena ), Engine y NSArray (un objeto de matriz).
Las clases se pueden derivar de una o más clases existentes, estableciendo así una relación jerárquica entre las clases derivadas ( clases base , clases padre osuperclases ) y la clase derivada (clase hijaosubclase). La relación de la clase derivada con las clases derivadas se conoce comúnmente como una relación es- un.[21]Por ejemplo, una clase 'Botón' podría derivarse de una clase 'Control'. Por lo tanto, un Botónes unControl. Los miembros estructurales y de comportamiento de las clases padre sonheredadospor la clase hija. Las clases derivadas pueden definir miembros estructurales adicionales (campos de datos) y miembros de comportamiento (métodos) además de los queheredany, por lo tanto, sonespecializacionesde sus superclases. Además, las clases derivadas puedenanularlos métodos heredados si el lenguaje lo permite.
No todos los lenguajes admiten la herencia múltiple. Por ejemplo, Java permite que una clase implemente múltiples interfaces, pero solo herede de una clase. [22] Si se permite la herencia múltiple, la jerarquía es un grafo acíclico dirigido (o DAG para abreviar), de lo contrario es un árbol . La jerarquía tiene clases como nodos y relaciones de herencia como enlaces. Las clases en el mismo nivel tienen más probabilidades de estar asociadas que las clases en diferentes niveles. Los niveles de esta jerarquía se denominan capas o niveles de abstracción.
Ejemplo (código Objective-C 2.0 simplificado, del SDK de iPhone):
@interfaz UIResponder : NSObject //... @interfaz UIView : UIResponder //... @interfaz UIScrollView : UIView //... @interfaz UITableView : UIScrollView //...
En este ejemplo, un UITableView es un UIScrollView es un UIView es un UIResponder es un NSObject.
En el análisis orientado a objetos y en el lenguaje de modelado unificado (UML), una asociación entre dos clases representa una colaboración entre las clases o sus instancias correspondientes. Las asociaciones tienen dirección; por ejemplo, una asociación bidireccional entre dos clases indica que ambas clases son conscientes de su relación. [23] Las asociaciones pueden etiquetarse según su nombre o propósito. [24]
Un rol de asociación se asigna al final de una asociación y describe el rol de la clase correspondiente. Por ejemplo, un rol de "suscriptor" describe la forma en que las instancias de la clase "Persona" participan en una asociación "se suscribe a" con la clase "Revista". Además, una "Revista" tiene el rol de "revista suscrita" en la misma asociación. La multiplicidad del rol de asociación describe cuántas instancias corresponden a cada instancia de la otra clase de la asociación. Las multiplicidades comunes son "0..1", "1..1", "1..*" y "0..*", donde "*" especifica cualquier número de instancias. [23]
Hay muchas categorías de clases, algunas de las cuales se superponen.
En un lenguaje que admite la herencia, una clase abstracta , o clase base abstracta ( ABC ), es una clase que no se puede instanciar directamente. Por el contrario, una clase concreta es una clase que se puede instanciar directamente. La instanciación de una clase abstracta solo puede ocurrir indirectamente, a través de una subclase concreta .
Una clase abstracta se etiqueta como tal explícitamente o puede simplemente especificar métodos abstractos (o métodos virtuales ). Una clase abstracta puede proporcionar implementaciones de algunos métodos y también puede especificar métodos virtuales a través de firmas que deben ser implementados por descendientes directos o indirectos de la clase abstracta. Antes de que se pueda instanciar una clase derivada de una clase abstracta, todos los métodos abstractos de sus clases padre deben ser implementados por alguna clase en la cadena de derivación. [25]
La mayoría de los lenguajes de programación orientados a objetos permiten al programador especificar qué clases se consideran abstractas y no permiten que se creen instancias de ellas. Por ejemplo, en Java , C# y PHP , se utiliza la palabra clave abstract . [26] [27] En C++ , una clase abstracta es una clase que tiene al menos un método abstracto dado por la sintaxis apropiada en ese lenguaje (una función virtual pura en el lenguaje de C++). [25]
En C++, una clase que consta únicamente de métodos virtuales puros se denomina clase base abstracta pura (o ABC pura ) y los usuarios del lenguaje también la conocen como interfaz . [13] Otros lenguajes, en particular Java y C#, admiten una variante de clases abstractas denominada interfaz mediante una palabra clave en el lenguaje. En estos lenguajes, no se permite la herencia múltiple , pero una clase puede implementar múltiples interfaces. Una clase de este tipo solo puede contener métodos abstractos de acceso público. [22] [28] [29]
En algunos lenguajes, las clases se pueden declarar en ámbitos distintos del ámbito global. Existen varios tipos de clases de este tipo.
Una clase interna es una clase definida dentro de otra clase. La relación entre una clase interna y su clase contenedora también puede tratarse como otro tipo de asociación de clases. Una clase interna normalmente no está asociada con instancias de la clase contenedora ni instanciada junto con su clase contenedora. Dependiendo del lenguaje, puede o no ser posible hacer referencia a la clase desde fuera de la clase contenedora. Un concepto relacionado es el de tipos internos , también conocido como tipo de datos interno o tipo anidado , que es una generalización del concepto de clases internas. C++ es un ejemplo de un lenguaje que admite tanto clases internas como tipos internos (a través de declaraciones typedef ). [30] [31]
Una clase local es una clase definida dentro de un procedimiento o función. Dicha estructura limita las referencias al nombre de la clase dentro del ámbito donde se declara la clase. Dependiendo de las reglas semánticas del lenguaje, puede haber restricciones adicionales en las clases locales en comparación con las no locales. Una restricción común es no permitir que los métodos de clase local accedan a las variables locales de la función que las contiene. Por ejemplo, en C++, una clase local puede hacer referencia a variables estáticas declaradas dentro de su función que las contiene, pero no puede acceder a las variables automáticas de la función . [32]
Una metaclase es una clase en la que las instancias son clases. [33] Una metaclase describe una estructura común de una colección de clases y puede implementar un patrón de diseño o describir tipos particulares de clases. Las metaclases se utilizan a menudo para describir marcos . [34]
En algunos lenguajes, como Python , Ruby o Smalltalk , una clase también es un objeto; por lo tanto, cada clase es una instancia de una metaclase única que está integrada en el lenguaje. [4] [35] [36] El Common Lisp Object System (CLOS) proporciona protocolos de metaobjetos (MOP) para implementar esas clases y metaclases. [37]
Una clase sellada no puede ser objeto de subclasificación. Básicamente es lo opuesto a una clase abstracta , que debe derivarse para poder usarse. Una clase sellada es implícitamente concreta .
Una clase declarada como sellada a través de la palabra clave sealed
en C# o final
en Java o PHP. [38] [39] [40]
Por ejemplo, la clase de Java String
está marcada como final . [41]
Las clases selladas pueden permitir que un compilador realice optimizaciones que no están disponibles para las clases que pueden subclasificarse. [42]
Una clase abierta puede modificarse. Normalmente, los clientes no pueden modificar un programa ejecutable . Los desarrolladores pueden modificar algunas clases, pero normalmente no pueden modificar las estándar o integradas. En Ruby , todas las clases son abiertas. En Python , las clases se pueden crear en tiempo de ejecución y todas se pueden modificar después. [43] Las categorías Objective-C permiten al programador añadir métodos a una clase existente sin necesidad de volver a compilar esa clase o incluso tener acceso a su código fuente.
Algunos lenguajes tienen soporte especial para mixins , aunque, en cualquier lenguaje con herencia múltiple, un mixin es simplemente una clase que no representa una relación de tipo "es-un-tipo-de". Los mixins se usan normalmente para agregar los mismos métodos a múltiples clases; por ejemplo, una clase UnicodeConversionMixin podría proporcionar un método llamado unicode_to_ascii cuando se incluye en clases FileReader y WebPageScraper que no comparten un padre común.
En los lenguajes que admiten esta característica, una clase parcial es una clase cuya definición puede dividirse en varias partes, dentro de un único archivo de código fuente o en varios archivos. [44] Las partes se fusionan en el momento de la compilación, lo que hace que la salida del compilador sea la misma que para una clase no parcial.
La principal motivación para la introducción de clases parciales es facilitar la implementación de generadores de código , como diseñadores visuales . [44] De lo contrario, es un desafío o compromiso desarrollar generadores de código que puedan administrar el código generado cuando se intercala dentro del código escrito por el desarrollador. Usando clases parciales, un generador de código puede procesar un archivo separado o una clase parcial de grano grueso dentro de un archivo, y por lo tanto se alivia de interponer intrincadamente el código generado a través de un análisis extenso, lo que aumenta la eficiencia del compilador y elimina el riesgo potencial de corromper el código del desarrollador. En una implementación simple de clases parciales, el compilador puede realizar una fase de precompilación donde "unifica" todas las partes de una clase parcial. Luego, la compilación puede continuar como de costumbre.
Otros beneficios y efectos de la característica de clase parcial incluyen:
Las clases parciales existen en Smalltalk con el nombre de extensiones de clase desde hace mucho tiempo. Con la llegada de .NET framework 2 , Microsoft introdujo las clases parciales, compatibles tanto con C# 2.0 como con Visual Basic 2005. WinRT también admite clases parciales.
Las clases no instanciables permiten a los programadores agrupar campos y métodos por clase a los que se puede acceder en tiempo de ejecución sin una instancia de la clase. De hecho, la instanciación está prohibida para este tipo de clase.
Por ejemplo, en C#, una clase marcada como "estática" no se puede instanciar, solo puede tener miembros estáticos (campos, métodos, otros), no puede tener constructores de instancias y está sellada . [45]
Una clase sin nombre o una clase anónima no está vinculada a un nombre o identificador al momento de la definición. [46] [47] Esto es análogo a las funciones con nombre y sin nombre .
Los beneficios de organizar el software en clases de objetos se dividen en tres categorías: [48]
Las clases de objetos facilitan el desarrollo rápido porque reducen la brecha semántica entre el código y los usuarios. Los analistas de sistemas pueden hablar con los desarrolladores y los usuarios utilizando esencialmente el mismo vocabulario, hablando de cuentas, clientes, facturas, etc. Las clases de objetos a menudo facilitan el desarrollo rápido porque la mayoría de los entornos orientados a objetos vienen con potentes herramientas de depuración y prueba. Las instancias de clases se pueden inspeccionar en tiempo de ejecución para verificar que el sistema está funcionando como se espera. Además, en lugar de obtener volcados de memoria central, la mayoría de los entornos orientados a objetos tienen capacidades de depuración interpretadas para que el desarrollador pueda analizar exactamente dónde ocurrió el error en el programa y pueda ver qué métodos fueron llamados con qué argumentos y con qué argumentos. [49]
Las clases de objetos facilitan el mantenimiento mediante la encapsulación. Cuando los desarrolladores necesitan cambiar el comportamiento de un objeto, pueden localizar el cambio únicamente en ese objeto y sus componentes. Esto reduce la posibilidad de que se produzcan efectos secundarios no deseados a raíz de las mejoras de mantenimiento.
La reutilización de software es también una de las principales ventajas de utilizar clases de objetos. Las clases facilitan la reutilización a través de la herencia y las interfaces. Cuando se requiere un nuevo comportamiento, a menudo se puede lograr creando una nueva clase y haciendo que esa clase herede los comportamientos y datos predeterminados de su superclase y luego adaptando algún aspecto del comportamiento o los datos en consecuencia. La reutilización a través de interfaces (también conocidas como métodos) ocurre cuando otro objeto desea invocar (en lugar de crear un nuevo tipo de) alguna clase de objeto. Este método de reutilización elimina muchos de los errores comunes que pueden aparecer en el software cuando un programa reutiliza el código de otro. [50]
Como tipo de datos, una clase se considera generalmente como una construcción de tiempo de compilación. [51] Un lenguaje o biblioteca también puede soportar metaobjetos prototipo o de fábrica que representan información de tiempo de ejecución sobre clases, o incluso representar metadatos que proporcionan acceso a facilidades de programación reflexiva (reflexión) y capacidad para manipular formatos de estructura de datos en tiempo de ejecución. Muchos lenguajes distinguen este tipo de información de tipo de tiempo de ejecución sobre clases de una clase sobre la base de que la información no es necesaria en tiempo de ejecución. Algunos lenguajes dinámicos no hacen distinciones estrictas entre construcciones de tiempo de ejecución y de tiempo de compilación y, por lo tanto, pueden no distinguir entre metaobjetos y clases.
Por ejemplo, si Humano es un metaobjeto que representa la clase Persona, entonces se pueden crear instancias de la clase Persona utilizando las facilidades del metaobjeto Humano .
A diferencia de la creación de un objeto a partir de una clase, algunos contextos de programación admiten la creación de objetos copiando (clonando) un objeto prototipo . [52]
Como sucede con los módulos, las clases participan de la naturaleza dinámica de Python: se crean en tiempo de ejecución y se pueden modificar después de su creación.
Existen factores limitantes fundamentales de la cognición humana; podemos abordar estas limitaciones mediante el uso de la descomposición, la abstracción y la jerarquía.