-
Notifications
You must be signed in to change notification settings - Fork 1
/
OO.qmd
111 lines (61 loc) · 10.3 KB
/
OO.qmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
\index{object-oriented programming}
```{r setup, include = FALSE}
source("common.R")
library(sloop)
```
# Introdución {#sec-oo .unnumbered}
En los cinco capítulos siguientes, aprenderá sobre la **programación orientada a objetos** (POO). POO es un poco más desafiante en R que en otros lenguajes porque:
- Hay múltiples sistemas de POO para elegir. En este libro, me concentraré en los tres que considero más importantes: **S3**, **R6** y **S4**. S3 y S4 son proporcionados por la base R. R6 es proporcionado por el paquete R6 y es similar a las clases de referencia, o **RC** para abreviar, desde la base R.
- Hay desacuerdo sobre la importancia relativa de los sistemas de POO. Creo que S3 es el más importante, seguido de R6, luego S4. Otros creen que S4 es el más importante, seguido de RC, y que S3 debe evitarse. Esto significa que diferentes comunidades R usan diferentes sistemas.
- S3 y S4 utilizan la función POO genérica, que es bastante diferente de la programación orientada a objetos encapsulada utilizada por la mayoría de los lenguajes populares en la actualidad[^oo-1]. Volveremos precisamente a lo que significan esos términos en breve, pero básicamente, aunque las ideas subyacentes de POO son las mismas en todos los idiomas, sus expresiones son bastante diferentes. Esto significa que no puede transferir inmediatamente sus habilidades de POO existentes a R.
[^oo-1]: La excepción es Julia, que también usa la función genérica de POO. En comparación con R, la implementación de Julia está completamente desarrollada y tiene un rendimiento extremo.
En general, en R, la programación funcional es mucho más importante que la programación orientada a objetos, porque normalmente resuelve problemas complejos descomponiéndolos en funciones simples, no en objetos simples. Sin embargo, hay razones importantes para aprender cada uno de los tres sistemas:
- S3 permite que sus funciones devuelvan resultados enriquecidos con una pantalla fácil de usar y componentes internos fáciles de usar para el programador. S3 se usa en toda la base R, por lo que es importante dominarlo si desea extender las funciones base R para trabajar con nuevos tipos de entrada.
- R6 proporciona una forma estandarizada de escapar de la semántica de copia al modificar de R. Esto es particularmente importante si desea modelar objetos que existen independientemente de R. Hoy en día, una necesidad común para R6 es modelar datos que provienen de una API web y dónde los cambios provienen de dentro o fuera de R.
- S4 es un sistema riguroso que lo obliga a pensar cuidadosamente sobre el diseño del programa. Es particularmente adecuado para construir grandes sistemas que evolucionan con el tiempo y recibirá contribuciones de muchos programadores. Es por eso que lo utiliza el proyecto Bioconductor, por lo que otra razón para aprender S4 es equiparlo para contribuir a ese proyecto.
El objetivo de este breve capítulo introductorio es brindarle un vocabulario importante y algunas herramientas para identificar los sistemas de POO en la naturaleza. Luego, los siguientes capítulos se sumergen en los detalles de los sistemas de POO de R:
1. @sec-base-types detalla los tipos básicos que forman la base subyacente a todos los demás sistemas OO.
2. @sec-s3 presenta S3, el sistema OO más simple y más utilizado.
3. @sec-r6 analiza R6, un sistema OO encapsulado creado sobre entornos.
4. @sec-s4 introduce S4, que es similar a S3 pero más formal y más estricto.
5. @sec-oo-tradeoffs compara estos tres sistemas OO principales. Al comprender las ventajas y desventajas de cada sistema, puede apreciar cuándo usar uno u otro.
Este libro se centra en la mecánica de la programación orientada a objetos, no en su uso efectivo, y puede ser un desafío comprenderlo completamente si no ha realizado antes programación orientada a objetos. Quizás se pregunte por qué elegí no proporcionar una cobertura útil más inmediata. Me he centrado en la mecánica aquí porque necesitan estar bien descritas en alguna parte (escribir estos capítulos requirió una cantidad considerable de lectura, exploración y síntesis de mi parte), y usar OOP de manera efectiva es lo suficientemente complejo como para requerir un tratamiento del tamaño de un libro; simplemente no hay suficiente espacio en *R Avanzado* para cubrirlo con la profundidad requerida.
## Sistemas de POO {.unnumbered}
Diferentes personas usan los términos de programación orientada a objetos de diferentes maneras, por lo que esta sección proporciona una descripción general rápida del vocabulario importante. Las explicaciones están necesariamente comprimidas, pero volveremos a estas ideas varias veces.
La razón principal para usar POO es **polimorfismo** (literalmente: muchas formas). El polimorfismo significa que un desarrollador puede considerar la interfaz de una función por separado de su implementación, lo que hace posible usar la misma forma de función para diferentes tipos de entrada. Esto está estrechamente relacionado con la idea de **encapsulación**: el usuario no necesita preocuparse por los detalles de un objeto porque están encapsulados detrás de una interfaz estándar.
Para ser concretos, el polimorfismo es lo que permite que `summary()` produzca diferentes salidas para variables numéricas y factoriales:
```{r}
diamonds <- ggplot2::diamonds
summary(diamonds$carat)
summary(diamonds$cut)
```
Podrías imaginar `summary()` que contiene una serie de declaraciones if-else, pero eso significaría que solo el autor original podría agregar nuevas implementaciones. Un sistema OOP hace posible que cualquier desarrollador amplíe la interfaz con implementaciones para nuevos tipos de entrada.
Para ser más precisos, los sistemas OO llaman al tipo de un objeto su **clase**, y una implementación para una clase específica se llama **método**. En términos generales, una clase define lo que un objeto *es* y los métodos describen lo que ese objeto puede *hacer*. La clase define los **campos**, los datos que posee cada instancia de esa clase. Las clases están organizadas en una jerarquía de modo que si no existe un método para una clase, se usa el método de su padre y se dice que el hijo **hereda** el comportamiento. Por ejemplo, en R, un factor ordenado hereda de un factor regular y un modelo lineal generalizado hereda de un modelo lineal. El proceso de encontrar el método correcto dada una clase se llama **despacho de métodos**.
Hay dos paradigmas principales de programación orientada a objetos que difieren en cómo se relacionan los métodos y las clases. En este libro, tomaremos prestada la terminología de *Extending R* [@extending-R] y llamaremos a estos paradigmas encapsulados y funcionales:
- En la programación orientada a objetos **encapsulada**, los métodos pertenecen a objetos o clases, y las llamadas a métodos normalmente se ven como `object.method(arg1, arg2)`. Esto se denomina encapsulado porque el objeto encapsula tanto los datos (con campos) como el comportamiento (con métodos), y es el paradigma que se encuentra en los lenguajes más populares.
- En la programación orientada a objetos **funcional**, los métodos pertenecen a funciones **genéricas** y las llamadas a métodos se parecen a las llamadas a funciones ordinarias: `generic(object, arg2, arg3)`. Esto se llama funcional porque desde el exterior parece una llamada de función regular, e internamente los componentes también son funciones.
Con esta terminología en la mano, ahora podemos hablar precisamente de los diferentes sistemas OO disponibles en R.
## POO en R {.unnumbered}
Base R proporciona tres sistemas OOP: S3, S4 y clases de referencia (RC):
- **S3** es el primer sistema de POO de R y se describe en *Modelos estadísticos en S* [@white-book]. S3 es una implementación informal de POO funcional y se basa en convenciones comunes en lugar de garantías inquebrantables. Esto hace que sea fácil comenzar, proporcionando una forma económica de resolver muchos problemas simples.
- **S4** es una reescritura formal y rigurosa de S3 y se introdujo en *Programación con datos* [@programming-with-data]. Requiere más trabajo inicial que S3, pero a cambio ofrece más garantías y una mayor encapsulación. S4 se implementa en el paquete base de **métodos**, que siempre se instala con R.
(Quizás se pregunte si existen S1 y S2. No lo hacen: S3 y S4 fueron nombrados de acuerdo con las versiones de S que acompañaban. Las dos primeras versiones de S no tenían ningún marco de POO.)
- **RC** implementa OO encapsulado. Los objetos RC son un tipo especial de objetos S4 que también son **mutables**, es decir, en lugar de usar la semántica habitual de copiar al modificar de R, se pueden modificar en su lugar. Esto los hace más difíciles de razonar, pero les permite resolver problemas que son difíciles de resolver en el estilo OOP funcional de S3 y S4.
Los paquetes CRAN proporcionan otros sistemas de POO:
- **R6** [@R6] implementa OOP encapsulado como RC, pero resuelve algunos problemas importantes. En este libro, aprenderá sobre R6 en lugar de RC, por las razones descritas en la @sec-why-r6.
- **R.oo** [@R.oo] proporciona algo de formalismo además de S3 y hace posible tener objetos mutables de S3.
- **proto** [@proto] implementa otro estilo de programación orientada a objetos basado en la idea de **prototipos**, que desdibujan las distinciones entre clases e instancias de clases (objetos). Me enamoré brevemente de la programación basada en prototipos [@mutatr] y la usé en ggplot2, pero ahora creo que es mejor seguir con los formularios estándar.
Aparte del R6, que es ampliamente utilizado, estos sistemas son principalmente de interés teórico. Tienen sus puntos fuertes, pero pocos usuarios de R los conocen y los entienden, por lo que es difícil que otros los lean y contribuyan a su código.
## sloop {.unnumbered}
Antes de continuar, quiero presentar el paquete sloop:
```{r}
library(sloop)
```
El paquete sloop (piense en "navegar los mares de OOP") proporciona una serie de ayudantes que completan las piezas que faltan en la base R. El primero de ellos es `sloop::otype()`. Hace que sea fácil descifrar el sistema OOP utilizado por un objeto capturado de forma salvaje:
```{r}
otype(1:10)
otype(mtcars)
mle_obj <- stats4::mle(function(x = 1) (x - 2) ^ 2)
otype(mle_obj)
```
Utilice esta función para averiguar qué capítulo leer para entender cómo trabajar con un objeto existente.