Translate

Mostrando las entradas con la etiqueta POO. Mostrar todas las entradas
Mostrando las entradas con la etiqueta POO. Mostrar todas las entradas

sábado, 5 de septiembre de 2026

¿Qué tan lejos está la OOP tradicional del pensamiento de Alan Kay?


Kay no dijo que “C++ no sea OOP” en una definición formal; de hecho, en 1997 dijo que cuando acuñó object-oriented no tenía C++ en mente. Y en 2003 definió su propia concepción de OOP poniendo en el centro messaging, encapsulamiento del estado/proceso y extreme late-binding.

Cuando pensamos en Programación Orientada a Objetos, probablemente pensamos en algo así:


Clase

  ↓

Objeto

  ↓

Encapsulamiento

  ↓

Herencia

  ↓

Polimorfismo


Pensamos en class, extends, interfaces, métodos virtuales, getters, setters, jerarquías de tipos...

Y probablemente pensemos en lenguajes como C++, Java o C#.


Pero hay algo curioso: Esta no era exactamente la idea de Orientación a Objetos que tenía Alan Kay.


Y esto es especialmente interesante porque Alan Kay fue quien acuñó el término “object-oriented programming” mientras trabajaba en las ideas que terminarían dando lugar a Smalltalk. 

Entonces vale la pena hacer una pregunta incómoda: ¿Qué tan lejos está la OOP que aprendimos de la OOP que Kay tenía en mente?


La enseñanza tradicional suele comenzar con:

Encapsulamiento

Abstracción

Herencia

Polimorfismo


Y después aparecen las clases:


class Dog extends Animal {

    @Override

    void speak() {

        System.out.println("Woof");

    }

}


Esto funciona perfectamente como modelo de programación.

Pero observemos algo:ninguna de estas ideas aparece como el centro de la definición que Kay dio de OOP.


En 2003, cuando le preguntaron qué significaba para él object-oriented programming, respondió:

 “OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things.”


Para Kay, el núcleo estaba en: messaging; mantener localmente el estado y proceso; proteger y ocultar ese estado; extreme late-binding. 


No aparece: herencia; clases; jerarquías; interfaces; getters y setters.

Y esto no es una casualidad.


El objeto de Kay no es simplemente una estructura con métodos

Esta diferencia es fundamental.

Es común imaginar objetos como: datos + funciones asociadas a esos datos.

Kay tenía otra metáfora.


Pensaba los objetos como células biológicas o como computadoras individuales conectadas a una red: entidades independientes que poseen su propio estado y se comunican mediante mensajes.


Lo importante no es solamente qué objetos existen.

Lo importante es: cómo se comunican.


De hecho, Kay llegó a decir que lamentaba haber puesto tanto énfasis en la palabra objects, porque podía hacer que la gente se concentrara en la parte menos importante. Para él, “the big idea is messaging”. 


Esta diferencia puede parecer sutil, pero es enorme.

En la forma habitual de pensar:


dog.speak();


tendemos a pensar: “Estoy invocando el método speak() de Dog.”


En el modelo de mensajes:

dog ← speak


la idea es más parecida a: “Le envío a este objeto el mensaje speak.”


No necesito conocer cómo responde.

Puede:

  • ejecutar código local;
  • cambiar su estado;
  • delegar;
  • enviar otro mensaje;
  • crear otro objeto;
  • incluso responder de una manera que yo no conocía cuando escribí el código.


El mensaje establece una frontera de comunicación.

Y ahí aparece el late binding.

En la OOP tradicional podemos tener:


Animal* animal = new Dog();

animal->speak();


y gracias a virtual, el método concreto se resuelve dinámicamente.

Eso ya es late binding.

Pero para Kay la idea debía llevarse mucho más lejos.


No se trata simplemente de decidir dinámicamente qué implementación de un método ejecutar.

Se trata de construir sistemas donde las partes estén desacopladas mediante mensajes y donde podamos cambiar las implementaciones sin que los demás componentes necesiten conocer esos detalles.


El mensaje define el contrato de comunicación.


La implementación queda detrás de esa frontera.

Acá aparece uno de los puntos más interesantes de toda esta discusión.


La OOP tradicional suele presentar:


        Animal

       /      \

     Dog      Cat


como algo central.


Pero la herencia no es necesaria en la definición de Kay.

Incluso en la historia temprana de estas ideas, la herencia no era necesariamente el punto de partida. El propio Kay explicó que hubo distintas líneas derivadas de Simula: una relacionada con el camino biológico/de redes que él siguió y otra relacionada con los abstract data types, que tuvo mucha más influencia posteriormente. 

Es decir: La OOP que terminó popularizándose estuvo fuertemente influenciada por el modelo de clases y tipos, pero ese no era necesariamente el centro de la visión original de Kay.


Entonces apareció C++. Y acá podemos entender por qué C++ es tan importante en esta historia.

C++ hizo que la orientación a objetos se volviera masiva.

Pero la OOP de C++ está fuertemente expresada mediante:


classes

inheritance

virtual functions

types

access control


Es un modelo muy poderoso.

Pero no es exactamente la metáfora de:


computadoras independientes

        ↕

     mensajes

        ↕

computadoras independientes


que inspiraba a Kay.


Por eso la pregunta no debería ser: ¿C++ está mal?

No.


La pregunta es: ¿C++ popularizó una interpretación particular de la orientación a objetos que terminó siendo confundida con la orientación a objetos en general?

Y probablemente esta sea una de las razones por las que hoy discutimos tanto si Rust, Go, JavaScript o incluso C son “orientados a objetos”.

Estamos utilizando una definición que mezcla diferentes tradiciones.


¿Y Smalltalk? Acá está la parte paradójica.

El lenguaje que nació de las ideas de Kay fue Smalltalk.

Y, sin embargo, incluso Smalltalk terminó desarrollando un fuerte énfasis en clases.


Kay llegó a aclarar que Smalltalk no debía entenderse simplemente como su sintaxis, su class library, ni siquiera como clases. Para él, el núcleo era el messaging.


Esto nos deja una idea bastante provocadora: Incluso el lenguaje que creó para explorar la orientación a objetos terminó siendo interpretado desde una perspectiva más centrada en clases de la que Kay consideraba fundamental.


Entonces, ¿qué es realmente OOP? Quizás después de todo este recorrido deberíamos volver a la pregunta original.


¿Es esto?

class

inheritance

encapsulation

polymorphism


¿O es esto?

objects

     ↕

 messages

     ↕

objects


Kay parece inclinarse claramente por la segunda visión.

Su definición de OOP es deliberadamente pequeña: messaging, encapsulamiento del estado/proceso y late binding extremo. 


Y esto cambia nuestra manera de mirar los lenguajes.

Un lenguaje puede tener clases y herencia y, aun así, utilizar una visión de objetos bastante diferente.


Y otro lenguaje puede no tener herencia y, sin embargo, compartir algunas ideas importantes con la orientación a objetos.


Por eso quizás la pregunta: “¿Rust es orientado a objetos?”


es menos interesante que: “¿Qué modelo de objetos utiliza Rust?”


Y lo mismo podemos preguntar de Go, C++, Java, C#, JavaScript o Smalltalk.


Tal vez la mayor diferencia entre la OOP tradicional y la visión de Kay sea esta:

La OOP tradicional nos enseñó a pensar en objetos como entidades que tienen datos y métodos.

Kay quería que pensáramos en objetos como pequeñas entidades autónomas que se comunican.


Una pone el foco en: ¿qué es este objeto?

La otra en ¿cómo se comunica este objeto con el resto del sistema?

Y esa diferencia no es menor.

Porque quizás la esencia de la orientación a objetos nunca estuvo en construir buenas jerarquías de clases.

Quizás estuvo, desde el principio, en diseñar buenas redes de comunicación entre entidades independientes.

Y si esto es así, entonces la pregunta que queda abierta es todavía más interesante:


¿Cuánto de la OOP que enseñamos hoy es realmente orientación a objetos y cuánto es simplemente programación basada en clases?


domingo, 30 de agosto de 2026

¿La herencia es realmente un pilar de la Orientación a Objetos?


En el post anterior cuestionábamos una idea bastante instalada: que un lenguaje necesita clases, herencia, encapsulamiento y polimorfismo para ser considerado orientado a objetos.


Si entendemos la orientación a objetos como una forma de modelar sistemas mediante objetos que encapsulan comportamiento y colaboran entre sí, aparece una pregunta inevitable: ¿La herencia es realmente necesaria?

La respuesta corta es: no.

Y dos lenguajes modernos lo muestran muy bien: Go y Rust.

Si tenemos:

Dog

Cat

Horse


podemos identificar características comunes y construir una abstracción:


            Animal

       /          \           \

     Dog      Cat    Horse

              

La herencia de clases es una de las formas tradicionales de representar esa relación:


Animal

  ↑

Dog


Pero la relación conceptual: “Dog puede comportarse como Animal”, no implica necesariamente que tengamos que utilizar herencia.

Podemos expresar la misma idea de otras maneras.

Y ahí aparecen los traits de Rust y las interfaces de Go.


Go no tiene clases ni herencia de clases.

Sin embargo, podemos definir una abstracción:


type Animal interface {

    Speak()

}


Y después diferentes tipos pueden satisfacer esa interfaz:


type Dog struct{}


func (Dog) Speak() {

    fmt.Println("Woof")

}


type Cat struct{}


func (Cat) Speak() {

    fmt.Println("Meow")

}


Podemos trabajar con la abstracción:


func makeSpeak(animal Animal) {

    animal.Speak()

}


Y utilizar:

makeSpeak(Dog{})

makeSpeak(Cat{})


No existe: Dog extends Animal


Pero sí existe algo conceptualmente importante:

Dog ───────► Animal

Cat ───────► Animal


Los tipos concretos satisfacen un contrato común.

Y esto permite polimorfismo.

La diferencia fundamental es que Go utiliza composición e interfaces, en lugar de una jerarquía de clases.


Rust sigue una idea similar.

Podemos definir:


trait Animal {

    fn speak(&self);

}


y después implementar ese comportamiento:


struct Dog;


impl Animal for Dog {

    fn speak(&self) {

        println!("Woof");

    }

}


Otro tipo puede hacer lo mismo:


struct Cat;


impl Animal for Cat {

    fn speak(&self) {

        println!("Meow");

    }

}


Podemos utilizar el trait como abstracción:


fn make_speak(animal: &dyn Animal) {

    animal.speak();

}


Y nuevamente tenemos polimorfismo:

        Animal

        /    \

      Dog    Cat


pero sin una jerarquía de herencia.

El mecanismo es diferente.

Entonces, ¿qué reemplaza a la herencia? En realidad, la pregunta está ligeramente mal formulada.

No necesitamos reemplazar la herencia.

Necesitamos resolver los problemas que tradicionalmente intentábamos resolver mediante herencia.


Por ejemplo:

Abstracción


Animal

 └── speak()


Go lo expresa mediante interfaces.

Rust lo expresa mediante traits.


Polimorfismo


Animal

  ↑

Dog

Cat


Ambos lenguajes permiten que diferentes tipos proporcionen el mismo comportamiento.


Reutilización

En lugar de crear jerarquías:


Animal

 ├── Dog

 │    └── BigDog

 │         └── SuperBigDog


podemos utilizar composición.


En Rust:

struct Engine;


struct Car {

    engine: Engine,

}


El Car no necesita ser un Engine. Simplemente tiene un Engine.

Esta diferencia parece pequeña, pero conceptualmente es enorme:

Herencia expresa “es un”. Composición expresa “tiene un”.


Y muchos diseños orientados a objetos pueden construirse perfectamente utilizando composición.

¿Entonces la herencia no sirve?

Por supuesto que sí.


La herencia puede ser muy útil cuando realmente existe una relación de especialización que queremos representar. El problema aparece cuando empezamos a utilizarla simplemente para:

  • reutilizar código;
  • compartir estado;
  • evitar duplicación;
  • construir jerarquías artificiales.


Por eso una buena regla de diseño suele ser: Prefer composition over inheritance.

No porque la herencia sea mala, sino porque reutilizar implementación y modelar una relación de subtipo son problemas diferentes.


Acá aparece una diferencia conceptual que muchas veces queda escondida.

Podemos tener:


Generalización

       │

       ▼

   Abstracción


y podemos representar esa generalización mediante: Herencia

pero también mediante:

  • Interfaces
  • Traits
  • Composición
  • Protocolos
  • Duck typing


Por lo tanto: La generalización es un concepto del modelo; la herencia es una de las herramientas que un lenguaje puede proporcionar para expresarla.

La abstracción no pertenece exclusivamente a las clases.


Entonces, ¿Go y Rust son orientados a objetos?

Acá la respuesta vuelve a depender de nuestra definición.


Si definimos OOP como: “programar utilizando clases y herencia”

entonces Go y Rust no son lenguajes orientados a objetos.


Pero esa definición es demasiado restrictiva.

Si consideramos fundamentales conceptos como:

  • encapsulamiento;
  • objetos con estado y comportamiento;
  • abstracción;
  • polimorfismo;
  • composición;
  • colaboración entre entidades;


entonces Go y Rust pueden expresar muchos diseños que reconocemos como orientados a objetos, aunque utilicen mecanismos diferentes.


Y esto nos lleva nuevamente a una conclusión importante: No deberíamos confundir orientación a objetos con el modelo de clases y herencia que popularizaron C++ y Java.


La herencia de clases es una implementación posible de determinadas ideas de OOP.

No necesariamente es la esencia de OOP.


¿Y Smalltalk?

Esto nos devuelve al origen.

En Smalltalk, la idea central de la orientación a objetos no era: clases + herencia + polimorfismo


sino algo mucho más cercano a: objetos + mensajes + comportamiento


Desde esa perspectiva, resulta menos sorprendente que lenguajes modernos puedan adoptar algunas ideas de OOP sin adoptar necesariamente las clases y la herencia tradicional.


Quizás entonces deberíamos cambiar la pregunta.

En lugar de: ¿Este lenguaje tiene herencia? preguntar: ¿Qué modelo utiliza el lenguaje para representar objetos, comportamiento, abstracción y colaboración?


Porque la ausencia de herencia no implica necesariamente la ausencia de orientación a objetos.

Y quizás esa sea la conclusión más importante:

La herencia es una herramienta para implementar determinadas relaciones entre objetos. La orientación a objetos es un modelo mucho más amplio que la herencia.


En definitiva, no necesitamos herencia para pensar en objetos.


Y tampoco necesitamos clases.

Pero entonces aparece una pregunta todavía más interesante: ¿Necesitamos siquiera clases para hacer Orientación a Objetos?


miércoles, 19 de agosto de 2026

¿Qué significa realmente que un lenguaje sea orientado a objetos?

 Cuando aprendemos programación orientada a objetos, solemos encontrarnos con una lista conocida:

  • Encapsulamiento
  • Abstracción
  • Herencia
  • Polimorfismo


Los famosos cuatro pilares de la orientación a objetos.

Pero hay una pregunta previa que pocas veces nos hacemos: ¿Qué significa realmente que un lenguaje sea orientado a objetos?

Porque tener clases, métodos o herencia no necesariamente alcanza para responderla.

Una primera aproximación podría ser: un lenguaje orientado a objetos es aquel que permite definir clases y crear objetos.

El problema es que esta definición es demasiado débil.

Si fuera suficiente con tener clases, entonces cualquier lenguaje que incorpore una construcción class sería automáticamente orientado a objetos.

Y sabemos que no es tan sencillo.

En realidad, una clase es solamente una forma de representar una abstracción.

Podemos partir de un conjunto de objetos concretos:


Dog

Cat

Horse


y abstraer aquello que tienen en común:


               Animal

            /       |            \

     Dog      Cat        Horse



La abstracción nos permite pasar de casos particulares a una representación general.

Y podemos volver a abstraer:


Dog

Cat

Horse

   ↓

Animal

   ↓

LivingBeing


Por eso, si entendemos la abstracción como un proceso de generalización, la clasificación de objetos y la construcción de jerarquías son manifestaciones de una misma actividad conceptual.

La clase no es la orientación a objetos.

Es una herramienta para representar determinadas abstracciones.

Entonces, ¿qué papel juegan los objetos?

Acá aparece una idea mucho más interesante.

La orientación a objetos no consiste simplemente en dividir un programa en estructuras llamadas clases.

Consiste en modelar el sistema alrededor de objetos que encapsulan estado y comportamiento y que colaboran entre sí.


Un objeto no es solamente un conjunto de datos.


Tiene:

  • identidad;
  • estado;
  • comportamiento.


Y, fundamentalmente, puede interactuar con otros objetos.

En la visión clásica de Smalltalk, esta interacción se expresa mediante mensajes.

Esto es importante porque cambia nuestra perspectiva: El centro de la orientación a objetos no es necesariamente la clase. Es el objeto y la colaboración entre objetos.

Las clases son una de las formas que tenemos de definir cómo se construyen esos objetos.


¿Y el polimorfismo? El polimorfismo aparece cuando podemos trabajar con diferentes objetos a través de una abstracción común.


Por ejemplo:

class Animal {

public:

    virtual void speak() = 0;

    virtual ~Animal() = default;

};


class Dog : public Animal {

public:

    void speak() override {

        std::cout << "Woof\n";

    }

};


class Cat : public Animal {

public:

    void speak() override {

        std::cout << "Meow\n";

    }

};



Podemos tener:

std::vector<std::unique_ptr<Animal>> animals;

animals.push_back(std::make_unique<Dog>());

animals.push_back(std::make_unique<Cat>());


for (auto& animal : animals) {

    animal->speak();

}


El código que utiliza Animal no necesita conocer la implementación concreta.


La abstracción establece el comportamiento que nos interesa:


Animal

 └── speak()


y el polimorfismo permite que diferentes objetos proporcionen diferentes implementaciones de ese comportamiento.


Por lo tanto:La abstracción define una perspectiva común; el polimorfismo permite trabajar con diferentes realizaciones de esa perspectiva.


¿Y C++? C++ es un caso particularmente interesante.


C++ tiene:

  • clases;
  • encapsulamiento;
  • herencia;
  • polimorfismo;
  • métodos virtuales;
  • objetos.


Entonces es perfectamente posible escribir programas con un diseño fuertemente orientado a objetos.

Pero C++ no es exclusivamente orientado a objetos.


También podemos escribir:


int sum(int a, int b) {

    return a + b;

}


usar templates:


template<typename T>

T max(T a, T b) {

    return a > b ? a : b;

}


usar funciones libres, algoritmos genéricos, programación procedural, metaprogramación, RAII, etc.


Por eso suele ser más preciso decir que C++ es un lenguaje multiparadigma que soporta programación orientada a objetos.


Y esta distinción es importante.


¿Entonces C++ es o no es orientado a objetos? La respuesta depende de qué estemos preguntando.


Si preguntamos: ¿C++ permite programación orientada a objetos?

La respuesta es claramente sí.


Si preguntamos: ¿Todo programa C++ es un programa orientado a objetos?

La respuesta es no.


Y si preguntamos: ¿C++ define qué significa orientación a objetos?

La respuesta también es no.


C++ implementa una determinada interpretación de la orientación a objetos.

Y esto nos lleva a una distinción que considero fundamental:Un paradigma no es una lista de características que un lenguaje debe implementar. Es una determinada forma de modelar y resolver problemas.


¿Y los cuatro pilares?

Desde esta perspectiva, los cuatro pilares tradicionales empiezan a verse de otra manera.

Encapsulamiento es una estrategia para mantener estado y comportamiento bajo el control del objeto.

Abstracción es un proceso conceptual mediante el cual identificamos lo esencial y generalizamos.

Herencia es un mecanismo para expresar relaciones de especialización/generalización.

Polimorfismo permite trabajar con diferentes implementaciones a través de una abstracción común.


Pero no están necesariamente en el mismo nivel conceptual.

La abstracción es una actividad de modelado.

La herencia es un mecanismo del lenguaje.

El polimorfismo es una propiedad que permite sustituir diferentes implementaciones.

Y el encapsulamiento es una estrategia para organizar el estado y comportamiento.


Por eso, quizás deberíamos dejar de pensar en los cuatro como cuatro piezas independientes que, juntas, mágicamente producen OOP. Entonces, ¿qué hace que algo sea orientado a objetos?

No existe una frontera universalmente aceptada.

Pero podemos pensar en una serie de ideas centrales:


       ORIENTACIÓN A OBJETOS


                                    Objetos

                                         │

             ┌─────────┴─────────┐

             │                                                      │

       Estado propio                             Comportamiento

             │                                                       │

             └─────────┬─────────┘

                                          │

                              Encapsulamiento

                                          │

                                 Colaboración

                                          │

                                    Mensajes

                                          │

                                 Polimorfismo

                                          │

                                  Abstracciones


Las clases, la herencia, las interfaces, los traits o los métodos son mecanismos concretos que distintos lenguajes utilizan para expresar estas ideas.

Por eso podemos encontrar lenguajes con clases que no ponen realmente a los objetos en el centro, y lenguajes que permiten muchos de los principios de OOP sin tener clases ni herencia tradicional.


Y ahí aparece una pregunta todavía más interesante: Si podemos tener encapsulamiento, abstracción y polimorfismo sin herencia de clases, ¿la herencia es realmente un pilar de la orientación a objetos?

Esa es precisamente la pregunta que vale la pena explorar después.


domingo, 16 de agosto de 2026

¿Y si la abstracción fuera el verdadero hilo conductor de la Orientación a Objetos?


Cuando aprendemos Orientación a Objetos, normalmente aparecen los famosos cuatro pilares:

  • Encapsulamiento
  • Abstracción
  • Herencia
  • Polimorfismo

Pero hay una pregunta interesante: ¿Qué entendemos realmente por abstracción?

Si entendemos la abstracción como un proceso de generalización, aparece una relación mucho más profunda entre varios de estos conceptos.


Imaginemos un conjunto de objetos:


🐕 Dog

🐕 Dog

🐈 Cat

🐈 Cat

🐎 Horse


Observamos características y comportamientos comunes y realizamos una generalización:

Dog ─┐

Dog ─┤

Cat ─┼──► Animal

Cat ─┤

Horse┘


Estamos clasificando y abstrayendo.

Identificamos qué tienen en común distintos objetos y construimos una representación general.

En un modelo basado en clases, esa abstracción puede representarse mediante una clase:


abstract class Animal {

    abstract void makeSound();

}


Esto puede verse como un proceso de clasificación:

> objetos concretos → categoría común → clase.


Pero podemos volver a generalizar.

Tenemos:

Dog

Cat

Horse

   ↓

Animal


y podemos observar que Animal comparte propiedades con otras entidades:


Animal

Plant

   ↓

LivingBeing


Otra vez estamos abstrayendo.

Por lo tanto, la abstracción no necesariamente ocurre una sola vez. Podemos construir niveles sucesivos de generalización:


objetos

   ↓

clases

   ↓

superclases

   ↓

abstracciones más generales


La herencia puede verse como el mecanismo que representa una relación de generalización/especialización:


          Animal

         /      \

       Dog      Cat


Animal representa una abstracción más general.

Dog y Cat son especializaciones de esa abstracción.


Por eso podemos pensar: La abstracción permite identificar lo general; la herencia permite expresar estructuralmente esa generalización en el modelo.


No son lo mismo, pero están profundamente relacionadas.

El polimorfismo aparece cuando podemos utilizar esas especializaciones a través de la abstracción común:


List<Animal> animals = ...;


for (Animal animal : animals) {

    animal.makeSound();

}


No necesitamos saber si estamos tratando con un Dog, un Cat o un Horse.

Trabajamos contra la abstracción Animal.

Entonces podemos visualizarlo así:


                                 ABSTRACCIÓN

                                             │

              ┌──────────┴──────────┐

              │                                                            │

        CLASIFICACIÓN                      GENERALIZACIÓN

              │                                                          │

        objetos → clases                        clase → superclase

                                              │

                                       HERENCIA

                                              │

                                  POLIMORFISMO


Entonces, ¿son realmente cuatro pilares independientes?

Quizás no.


Si entendemos abstracción como generalización, entonces la abstracción participa en dos procesos fundamentales del modelado orientado a objetos:

Clasificación: objetos → clase


y generalización


clase → superclase


La herencia es una forma de representar esta última relación, mientras que el polimorfismo permite utilizar diferentes especializaciones mediante una abstracción común.


Esto también nos obliga a revisar una definición muy común: “Abstracción es ocultar los detalles de implementación.”


Eso describe una consecuencia o una técnica de abstracción, pero quizás no su esencia.


Una definición más profunda podría ser: Abstraer es identificar aquello que permanece común entre diferentes casos y construir una representación general que nos permita razonar sobre ellos.

Y desde esa perspectiva, la abstracción no es simplemente uno de los cuatro pilares de OOP: es uno de los procesos fundamentales mediante los cuales construimos el modelo orientado a objetos.

Es decir, la abstracción se utiliza para generalizar un conjunto de objetos en una clase (tomando el comportamiento y atributos comunes) y utilizamos la misma técnica para generalizar un conjunto de clases en clases más generales que nos permitan reutilizar código.  

Y si tomamos esta filosofía, lenguajes como Go o Rust son orientados a objetos, por más que no tengan herencia de forma común (como Java o C#) tienen un mecanismo de abstracción con interfaces. 

Qué opinan?



sábado, 11 de julio de 2026

Polimorfismo vs Pattern Matching


Durante los últimos años aparecieron innumerables artículos anunciando la muerte del polimorfismo. La llegada de sealed classes, union types y pattern matching hizo que muchos comenzaran a tratar la orientación a objetos como una técnica del pasado.

Sin embargo, las críticas al polimorfismo no son nuevas. Tampoco las críticas al pattern matching. De hecho, muchas de ellas fueron formuladas por las personas que diseñaron estos paradigmas mucho antes de que existieran Java, Kotlin o Rust.

Vale la pena volver a esas discusiones:


Agregar operaciones es difícil

Philip Wadler formuló en 1998 el famoso Expression Problem.

Su observación sigue siendo una de las críticas más fuertes al polimorfismo: los sistemas orientados a objetos hacen muy sencillo incorporar nuevos tipos, pero extremadamente costoso agregar nuevas operaciones.

Cada nueva operación obliga a modificar toda la jerarquía de clases.

Desde esta perspectiva, el pattern matching invierte completamente el problema: agregar una operación suele consistir simplemente en escribir una nueva función.

No es casualidad que muchos desarrolladores funcionales vean al patrón Visitor como una consecuencia directa de esta limitación.


El comportamiento queda disperso

Martin Odersky ha defendido repetidamente el pattern matching como una herramienta para expresar algoritmos completos en un único lugar.

Cuando la lógica está distribuida entre decenas de implementaciones distintas, comprender una operación completa implica recorrer buena parte del proyecto.

Con pattern matching, el algoritmo aparece concentrado y resulta mucho más sencillo razonar sobre él.

La pregunta implícita es incómoda para la orientación a objetos: ¿Queremos organizar el código alrededor de los datos o alrededor de las operaciones?


El despacho dinámico oculta demasiado

Otra crítica frecuente desde el mundo funcional es que el polimorfismo sacrifica legibilidad en favor de la extensibilidad.

Una llamada aparentemente inocente puede terminar ejecutando cualquiera de decenas de implementaciones.

El flujo del programa deja de ser evidente y pasa a depender de la jerarquía de tipos.

Para muchos, el pattern matching hace exactamente lo contrario: vuelve explícitas todas las alternativas posibles.


Pattern Matching rompe el encapsulamiento

Si hubiera que elegir una crítica histórica, probablemente sea la de Alan Kay.

Kay insistió durante décadas en que los objetos nunca fueron concebidos como estructuras de datos con comportamiento, sino como entidades que colaboran mediante el envío de mensajes.

Desde esa mirada, inspeccionar explícitamente el estado de un objeto para decidir qué hacer significa abandonar el modelo de objetos y volver a programar sobre representaciones internas.

No es una cuestión sintáctica.

Es un cambio de filosofía.


Cada nuevo tipo obliga a revisar todos los matches

Bertrand Meyer, creador de Eiffel y autor del principio Open/Closed, probablemente encontraría aquí su principal objeción.

Cuando aparece un nuevo subtipo, todos los matches relevantes deben ser revisados.

La lógica que antes estaba distribuida vuelve a concentrarse en múltiples lugares.

El costo del cambio deja de estar en agregar operaciones y pasa a estar en agregar variantes del modelo.

Exactamente el problema inverso al planteado por Wadler.


Un match de 500 líneas sigue siendo un switch de 500 líneas

Quizás sea la crítica más práctica.

El pattern matching no garantiza buen diseño.

Nada impide escribir enormes bloques de decisiones difíciles de mantener.

Cambiar la sintaxis de switch por match no elimina automáticamente el acoplamiento ni mejora la modularidad.

Muchas veces simplemente produce un switch más elegante.


Entonces... ¿quién tiene razón?

Probablemente ambos.

Philip Wadler demostró que la orientación a objetos favorece la extensión por tipos.

Bertrand Meyer mostró que esa misma decisión permite mantener el sistema cerrado frente a modificaciones cuando aparecen nuevos comportamientos internos.

Alan Kay defendió durante toda su carrera que los objetos deberían ocultar completamente su representación.

Martin Odersky respondió que muchas veces entender un algoritmo completo es más importante que esconder la representación de los datos.


No son respuestas contradictorias.

Responden preguntas diferentes.


Cada cierto tiempo la industria intenta declarar un ganador.

Hace veinte años parecía que todo debía resolverse con herencia y polimorfismo.

Hoy pareciera que todo debería resolverse con pattern matching.

La realidad es bastante menos interesante para quienes buscan una respuesta definitiva: ninguno reemplaza al otro.


Uno optimiza la evolución del sistema cuando aparecen nuevos tipos.

El otro optimiza la evolución cuando aparecen nuevas operaciones.


No existe una técnica superior.


Existe una pregunta mucho más importante:

¿Qué es lo que cambia con mayor frecuencia en tu dominio?


Porque esa respuesta, mucho más que el paradigma elegido, suele determinar cuál de las dos herramientas encaja mejor.

sábado, 10 de mayo de 2025

Clases padres, clases hijas… ¿y las madres qué?


La programación orientada a objetos (POO) nos trajo muchas cosas lindas: encapsulación, herencia, polimorfismo, y sobre todo, la posibilidad de inventarnos familias disfuncionales de clases sin necesidad de pasar por terapia.

Pero hay algo que siempre nos hizo ruido:

  • ¿Por qué hablamos de clases padre y clases hijas? 
  • ¿Dónde quedaron las madres, los tíos, las primas, o la abuela que todo lo sabe?

Lo más raro es que una clase padre puede ser hija de otra clase. Rarisisimo...

Todo empieza con el inglés. En POO se habla de parent class para referirse a la clase de la cual heredan otras. Y parent, significa “padre o madre”.

Pero claro, en español, por alguna razón misteriosa que seguro involucra a la Real Academia, siempre traducimos parent class como “clase padre”, y no “clase madre” o “clase progenitor/a” (aunque esa última suena a trámites en ANSES).

Entonces… ¿por qué decimos “clase hija”? Acá la gramática mete la cuchara. Como la palabra “clase” es femenina, cuando hablamos de su descendencia lógica usamos “hija” para que concuerde: La clase padre tiene muchas clases hijas.

¿Y si dijéramos “clase madre”?

¡Podemos! No hay ninguna ley que lo impida. De hecho, si queremos romper esquemas y escribir:


class Mamífero // clase madre

class Perro extends Mamífero // clase hija


...nadie de Scala te va a venir a buscar. Al contrario, tal vez sumes puntos con tus profes de literatura.

Eso sí, el término "clase madre" no es tan común, así que si lo usás, preparate para explicar o educar. (O poner una nota al pie tipo “uso madre porque soy inclusivo/a y rebelde”).

La POO no distingue género, pero el lenguaje humano sí. Y en nuestra necesidad de ponerle nombre a todo, terminamos replicando convenciones culturales sin cuestionarlas.

¿Querés decir clase madre? ¡Decilo!

¿Preferís clase base? ¡También está bien!

Lo importante es que tus clases compilen… y que no traumen a sus hijas.



domingo, 12 de junio de 2016

Programación Orientada a objetos desde la perspectiva de su creador


Muchos mitos existen sobre la programación orientada a objetos. Una de las cosas que he escuchado diferentes versiones es sobre su creación. Yo prefiero pensar que todo ocurrió en el Centro de Investigación de Palo Alto (PARC - Palo Alto Research Center) de Xerox en 1970.

Alan Kay es el creador de la programación orientada a objetos. Es decir si utilizas poo debes conocer a esta persona.

Kay entró a trabajar en el Centro de Investigación de Palo Alto (PARC - Palo Alto Research Center) de Xerox en 1970. En los setenta fue uno de los miembros principales del centro, desarrollando prototipos de estaciones de trabajo en red, usando el lenguaje de programación Smalltalk. Smalltalk es un lenguaje de poo puro. En Smalltalk todo es un objeto, es de tipado dinámico y con clausuras. Deberían leer sobre este lenguaje.

Kay, junto a algunos compañeros en PARC y otros predecesores del Norwegian Computing Centre, es uno de los padres de la Programación Orientada a Objetos. Creó el Dynabook que definió la base de los ordenadores portátiles y Tablet PC actuales, también es considerado por algunos como el arquitecto de los sistemas modernos de ventanas interfaz gráfica de usuario.

Unas de las frases que más me gustan:

“I invented the term object oriented, and I can tell you that C++ wasn't what I had in mind.” —Alan Kay. (Yo inventé el término "orientado a objetos", y te puedo asegurar que C++ no era en lo que estaba pensando)

Dejo link: http://www.vpri.org



domingo, 17 de junio de 2012

Encontrar los objetos apropiados


Cuando diseñamos, lo más dificil es encontrar objetos indicados que representen la realidad pero a la vez den flexibilidad a nuestros diseños como dice el libro Patrones de Diseño de Erich Gamma:

"Lo más complicado del diseño orientado a objetos es descomponer un sistema en objetos. La tarea es difícil porque entran en juego muchos factores: encapsulación, granularidad, dependencia, flexibilidad, rendimiento, evolución, reutilización, etc, etc. Todo ellos influyen en la descomposición, muchas veces de forma opuesta.

...

Muchos objetos del un diseño proceden del modelo del análisis. Pero los diseños orientados a objetos suelen acabar teniendo clases que no tiene su equivalente en el mundo real. Algunas de ellas son clases de bajo nivel como los arrays. Otras son de mucho más alto nivel... El modelado estricto del mundo real conduce a un sistema que refleja las realidades presentes pero no necesariamente las futuras. Las abstracciones que surgen durante el diseño son fundamentales para lograr un diseño flexible. "


Herencia frente a composición

Estoy leyendo Patrones de Diseño de Erich Gamma. En este libro discute sobre como es mejor reutilizar funcionalidad si por medio de la herencia o la composición. La idea de este post no es transcribir del libro sino hacer un resumen y analizar cada punto.

Vamos primero con la composición, la composición es una técnica en la cual descomponemos objetos para reutilizar funcionalidad. De esta forma componiendo objetos podemos obtener funcionalidad más compleja y completa.

Ventajas:

  • Reutilización de caja negra, los detalles internos de los objetos no son visibles.
  • La dependencia de objetos se puede definir dinamicamente si utilizamos interfaces. Disminuyendo el acoplamiento y aumentando la reutilización. 
  • Ayuda a mantener cada clase encapsulada y centrada en una sola tarea. De esta manera, nuestra clases y jerarquías de clases permanecerán pequeñas y sera menos probable que se conviertan en monstruos inmanejables. 
Desventajas:
  • Requiere más diseño (que tampoco es tan malo)
El mecanismo de herencia lo conocemos bien y también tiene sus ventajas y desventajas


Ventaja:

  • Es facil de usar lo provee los lenguajes.
  • Es más fácil modificar la implementación que esta siendo reutilizada. 
Desventaja:
  • Reutilización de caja blanca, los hijos pueden modificar variables internas del padre (si están protegidas o publicas), la herencia suele decirse rompe el encapsulamiento. 
  • Existe más acoplamiento ya que cualquier cambio en el padre obligara a cambiar la subclase. 
Según lo dicho podemos afirmar:

"Favorecer la composición de objetos frente a la herencia de clases."


Idealmente, solo crearíamos nuevos componentes para lograr la reutilización. Deberíamos ser capases de conseguir toda la funcionalidad que necesitamos simplemente ensamblando componentes existentes a través de la composición de objetos. Sin embargo, rara vez este es el caso, por lo tanto reutilizar mediante herencia hace más fácil construir nuevos componentes que puedan ser combinados con los antiguos. La herencia y la composición trabajan juntas. Pero abusar de la herencia trae consigo diseños más complejos y utilizar composición hace más fácil y reutilizable el diseño. 

sábado, 4 de junio de 2011

Equals


Cuando creamos una clase es una buena práctica definir el método equals, dado que este representa con que campos se identifica esta clase; es decir dos objetos van a ser iguales si son iguales las propiedades usadas en el equals.

El EqualsBuilder de apache common nos puede hacer la vida más fácil a la hora de hacer los equals. Pero a la vez tenemos comprobaciones repetitivas que se pueden factorizar usando herencia.

Veamos un ejemplo de equals:
@Override
public boolean equals(Object obj) {
if (obj == null)
return false;
if (obj == this)
return true;
if (!obj.getClass().isAssignableFrom(getClass()))
return false;

Author otherAuthor = (Author) obj;

return new EqualsBuilder().append(this.user, otherAuthor.getUser())
.isEquals();
}


Si hacemos otro equals vamos a hacer algo muy similar. Una forma de factorizar las lineas que se repiten es mediante herencia podemos hacer una clase padre de todas las clases del modelo que haga lo siguiente:

@Override
public boolean equals(Object obj) {
if (obj == null)
return false;
if (obj == this)
return true;
if (!obj.getClass().isAssignableFrom(getClass()))
return false;

return businnessEquals(obj);
}

public abstract boolean businnessEquals(Object obj);


Y businnessEquals sea abstracto y obligue a todas las clases del modelo a implementarlo. Todos los objetos que hereden de la clase abstracta deben implementar bussinnessEquals pero se olvidan de hacer las validaciones repetitivas.

@Override
public boolean businnessEquals(Object obj) {
Author otherAuthor = (Author) obj;

return new EqualsBuilder().append(this.user, otherAuthor.getUser())
.isEquals();
}



Les gusto la solución? Se les ocurre otra?

viernes, 23 de julio de 2010

Lenguaje de programación modernos

Un poco amarillista el titulo del post pero en los últimos años nos vimos bombardeados por nuevos lenguajes de programación como ruby, goovy, scala, ioke, python, etc. Y la verdad que están muy buenos.


Todo empezó con el bum de ruby, lenguaje script basado en smalltalk, que con railes parecía como que iba a conquistar el mundo. Pero otras plataformas no tardaron en copiar los beneficios del lenguaje para su plataforma así nació jruby, jython groovy (en java) y iron ruby y iron python (en .net).


Luego nació algo muy interesante la mezcla de paradigmas en particular la programación funcional con la programación orientada objetos, dando muy buenos lenguajes como scala, ioke o clojure en java y F# en .net.


En el blog se hablo de algunos de estos lenguajes:

http://emanuelpeg.blogspot.com/search/label/Clojure

http://emanuelpeg.blogspot.com/search/label/Ioke

http://emanuelpeg.blogspot.com/search/label/jython

http://emanuelpeg.blogspot.com/search/label/jRuby

http://emanuelpeg.blogspot.com/search/label/Lua

http://emanuelpeg.blogspot.com/search/label/Scala


Me quede pensando que esto esta buenísimo, la convivencia de diferentes paradigmas en un lenguaje, es raro que no se allá todavía inventado un lenguaje orientado a objeto y lógico como un prolog OO. Un ejemplo de esto es logtalk

¿Alguien conoce otro lenguaje multiparadigma?¿Alguien usa algun lenguaje multiparadigma?


sábado, 5 de junio de 2010

Frases

"Los programas deben ser escritos para que la gente los lea y sólo incidentalmente, para que las máquinas los ejecuten."

---Abelson / Sussman

"Mucho del software hoy en día se parece a una pirámide egipcia: con millones de ladrillos apilados uno encima del otro, sin integridad estructural y hecho por pura fuerza bruta y miles de esclavos."

--Alan Kay

"Cualquier tonto puede escribir código que un ordenador entiende. Los buenos programadores escriben código que los humanos pueden entender."

--Martin Fowler

"Hay dos formas de diseñar software: la primera es hacerlo tan simple que obviamente no hay deficiencias y la segunda es hacerlo tan complicado que no hay deficiencias obvias. La primera forma es mucho más difícil."

--C.A.R. Hoare

"Si deseas empezar y desarrollar algo grandioso, no necesitas millones de dólares de capitalización. Necesitas suficiente pizza y Diet Coke en la nevera, una PC barata y trabajo y dedicación para realizar tu idea."

---John Carmack

La mejor forma de predecir el futuro es inventarlo.

--Alan Kay

El software y las catedrales se parecen mucho. Primero lo construimos, después rezamos.

–-Anónimo


domingo, 26 de julio de 2009

this, self, Me y Yo!!!

this en un puntero a el mismo objeto. Bueno puntero en c++, para ser más generico es una referencia a si mismo, a nuestro mismo objeto. Por ejemplo:

this.hacerAlgo()

Es lo mismo que decir yo hago algo.
Este puntero a si mismo es muy útil. Ya que en un lenguaje no totalmente orientado a objeto. Como se que estoy llamando un método que esta dios sabe donde esta, o es un método que esta dentro de la clase. Con this queda mucho más claro.

Bueno this es en C++, java, php, javascript; es decir todos los derivados de c++.
self es en object pascal, simula, smalltalk y ruby.
Me es en visual Basic.

La verdad creo que es la primera (y va a ser la única) vez que diga que la gente de vb hizo bien las cosas. Me me parece más intuitivo, bueno self también esta bueno pero son 4 caracteres, mucho para escribir. Me es el ganador sin duda, intuitivo y corto.

Auque no entiendo porque ningún lenguaje uso I, es intuitivo y corto. Si programáramos en castellano seria “yo” que a mi por ser de lengua española me gusta más.
Por ejemplo podría escribir:

yo.voyAHacerAlgo();

Y ustedes, ¿que piensan cual es la palabra que les gusta más para referirse a nuestro mismo objeto? ¿this, self o Me?

viernes, 24 de julio de 2009

Inyección de dependencias


Originalmente, La inyección de dependencia se la llamaba de otra forma: inversión de control (IoC). Pero en un artículo escrito por Martin Fowler preguntaba qué aspecto del control se estaba invirtiendo. Llego a la conclusión de que la adquisición de la dependencia lo que se invertía. Basándose en esa revelación, acuño la frase “inyección de dependencia”, un término que describe mejor lo que ocurre.

Pero ahora bien ¿ qué es la inyección de dependencia? La wikipedia nos dice algo así: http://es.wikipedia.org/wiki/Inyección_de_dependencias

Cualquier aplicación no trivial está formada por dos o más clases que colaboran entre sí para realizar alguna lógica. Tradicionalmente cada objeto se hacía cargo de obtener sus propias referencias a los objetos a los cuales colaboraba (sus dependencias). Esto lleva a código acoplado y difícil de probar.

Cuando se aplica inyección de dependencia le decimos a una entidad externa que provea las dependencias a los objetos. Esto nos resuelve el problema del acoplamiento.

El acoplamiento es un mal necesario ya que sin él los objetos no podrían interactuar para resolver problemas, pero cuan menor sea el acoplamiento es más reutilizable, comprobable y flexible.

La ventaja clave de inyección de dependencia es el acoplamiento débil. Si un objeto solo conoce sus dependencias mediante su interfaz (no su implementación o como fueron definidos) entonces la dependencia puede intercambiarse con una implementación diferente sin que el objeto dependiente sepa la diferencia.

Por ejemplo si defino una interfaz la implementación de la misma pude ser un objeto plano, web service, objeto remoto, etc.

Inyección de dependencia es la llave para minimizar el acoplamiento.



martes, 21 de julio de 2009

POO = Herencia

Cuando pensamos en POO siempre caemos en que cuanto más usemos herencia más orientado objeto esta. Y claramente no es así. Es decir si tenemos este pensamiento estamos comenzando con el pie izquierdo; estamos pensando en "programación orientada a clases". Claro estamos generalizando de ante mano.

Lo ideal (que no quiere decir que en el día a día lo haga) es imaginar el problema con solo objetos. Una representación del problema; como una simulación. Sin interesarnos en clases, datos, etc.

Luego que ya entendimos el problema; pensamos como se comportan los objetos. Hay objetos que se comportan similar y los podemos generalizar entonces tenemos nuestra clase!!!

Hora tenemos un conjunto de clases y vemos que algunas tienen el mismo comportamiento, entonces podemos generalizar en una clase padre.

Claro esta que esto en teoría es muy fácil, el problema es la práctica. Veamos un problema aparentemente fácil, una academia donde tenemos alumnos y profesores. Fácil no? Una clase persona de la cual heredan la clase Alumno y Profesor. Muy fácil !!!

Momento ... y si un alumno también es profesor. Mmm... NO era tan fácil.

Con el modelo presentado hacemos agua, donde estuvo el error el primer paso, NO ENTENDIMOS EL PROBLEMA!!!

Lo escrito no es un descubrimiento filosófico ni un pensamiento que va a cambiar tu vida profesional. Simplemente una reflexión y recordatorio, antes de pensar en herencia y clases; pensemos: ¿entiendo el problema?