Pensamiento orientado a objetos vs pensamiento por procedimientos

Todavía recuerdo la primera vez que entré en contacto con los conceptos de la programación orientada a objetos. Yo estaba navegando en msdn library (vs6) en una sección denominada libros y tropiezo con un libro llamado «Visual basic 6 business objects». Había sólo unos capítulos incluidos pero los encontré increíbles. Habia aprendido y llevaba escribiendo aplicaciones vb6 por un tiempo para entonces pero encontré el vocabulario extraño: «Objeto de negocio», «Encapsulamiento», «Polimorfismo» y así sucesivamente. Inmediatamente me enganchó. Entre más aprendía, más quería empezar a codificar en este modo nuevo y maravilloso. Pero cuando tuve que escribir el código, me pareció tan difícil empezar! La cosa es que la programación orientada a objetos requiere una nueva mentalidad y este cambio toma tiempo.

El problema

Creo que el problema surge porque casi cada desarrollador es expuesto primeramente a la programación por procedimientos y generalmente tarda mucho tiempo para ser expuesto a la programación orientada a objetos. También la enseñanza de la programación orientada a objetos es a menudo muy pobre. Estos 2 hechos combinados con un montón de tutoriales por ahí para aprender lenguajes orientados a objetos que no consisten mas que en ejercicios de programación por procedimientos impone el estilo por procedimientos mas firmemente en la mente de los programadores.

Así que ¿como se ve el código por procedimientos? Hay tantas maneras y formas que en lugar de un ejemplo compartiré algunos indicadores.

  1. Los objetos contienen o sólo datos o sólo métodos
  2. Los objetos muestran datos con el único propósito de ser utilizado por otra parte
  3. Casi la totalidad de la lógica esta en métodos estáticos

De pensamiento por procedimientos a orientado a objetos

Así, la programación por procedimientos se trata de los procedimientos y los datos que pasas a ellos. Empiezas a pensar cuáles son las variables, cómo se representan (estructuras de datos) y qué hacer con ellas mientras que un estilo objeto orientado a objetos te hace pensar en quién hace qué (responsabilidades) y quien trabaja con quien con el fin de completar una tarea (colaboración). El cómo (implementación) es relegado a una etapa posterior. En lugar de pensar sobre los datos y procedimientos, ahora tienes objetos que empaquetan datos y procedimientos para hacer las cosas.

Ahora viene la parte difícil, la mayoría de las veces debes exponer sólo los métodos, no datos (propiedades). Si estás familiarizado con un lenguaje orientado a objetos (como java o  C#) intenta escribir un programa sin el uso de propiedades. No expongas datos solo para que alguien mas los utilice. Pide ayuda no datos (encapsulamiento). Esto llevará naturalmente a objetos que tienen datos y los métodos para manipular esos datos. Esto es bueno. Así que en lugar de escribir mailer.send(mail) ahora vas a escribir mail.sendThrough(mailer). Y mailer puede tener algo que parece mailer.send(recipient,sender,subject,body). Este sutil cambio tiene un gran impacto en el código. Pruebalo y deja de escribir pascal en Java 😉

TDD: El contexto es el rey

¿Así que quieres empezar a hacer TDD eh? has leído todos los blogs sobre el tema, visto todos los videos en la web y ahora estás listo para comenzar. Abres tu editor de código de elección y comienzas un nuevo proyecto y un nuevo proyecto de prueba. Y te quedas mirando. Sabes lo que tienes que hacer: escribir una prueba, hacerla fracasar, escribir el código más simple para hacerla pasar, refactorizar el código para que se vea bien. Y volvemos a empezar. El punto es, que realmente no sabes qué probar. O cómo saber qué probar. ¿Todos hemos estado ahi con la cara en blanco y un solo pensamiento: por dónde empiezo?

El principal problema es la falta de experiencia. Realmente no puedo pensar en otra cosa. Supongo que no es un problema cuando se está haciendo TDD con alguien que ya tiene experiencia en esto, pero si este no es tu caso no te preocupes, sólo tienes que hacer una cosa: empezar.

Supongo que es diferente para todos pero quiero compartir algunas de mis propias prácticas personales sobre este asunto. Espero que esto te ayuda a empezar.

  1. Definir las acciones del sistema.
  2. Definir los objetos de negocio en base a cada caso de uso.
  3. Escribir la primera prueba para un objeto de negocio.
  4. Crear interfaces para cualquier dependencia no definida todavía.

Definir las acciones del sistema

Uno de los errores más comunes que he visto son la falta de alcance y contexto. Sé lo que voy a decir suena tonto pero realmente, realmente, necesitas sentarte a averiguar lo que el sistema va a hacer antes de escribir algo de código. He visto tantas personas fracasando en esto!! Mi técnica favorita para esto es usar diagramas de casos de uso UML. Para empezar realmente no me enfoco demasiado en los detalles, solo quiero tener una idea general del alcance del sistema y los usuarios involucrados. Un diagrama de casos de uso permite ver cómo los actores van a interactuar con el sistema y las acciones específicas que un rol/personaje puede hacer con él. Cuando se trabaja en un entorno ágil he encontrado que éstos casos de uso corresponden muy bien con historias de usuario e incluso con epics si utilizas scrum. Recuerda que esta es una vista de alto nivel del sistema. No te enredes demasiado con los detalles. Aún mejor, no pienses en los detalles para nada. Todavía no.

Definir los objetos de negocio en a por base del caso de uso

Una vez que has definido los casos de usos para el sistema, selecciona uno. Ahora puedes entrar en los detalles (mas o menos). Aquí yo suelo utilizar un diagrama de interacción UML para definir la forma en que los objetos cooperarán para lograr los objetivos del caso de uso. Resiste la tentación de comenzar a dibujar los detalles de implementación: ahora lo único que te importa son las cosas que solicitas hacer al objeto y lo que esperas recibir de él. En otras palabras las entradas y salidas de lo objetos. Recuerda que estamos hablando de objetos de negocio, a menos que es necesaria para el flujo principal, no pongas ningún objeto relacionado con la infraestructura, es decir objetos que encapsulan llamadas a base de datos, servicios web y otras cosas que no tienen lógica del negocio y que solo están destinados a ser utilizados por otros objetos.

Escribir la primera prueba para un objeto de negocio

Al fin podemos codificar! Primero seleccione el caso de uso en el que deseas enfocarte. Ya? Ahora crea un archivo para contener las pruebas relacionadas con uno de los objetos de negocios. He adoptado algunas practicas del movimiento BDD y generalmente agrego la palabra «specs» (de specifications) al nombre del archivo. Así que vamos a decir que tengo un objeto de negocio llamado Cuenta, el archivo de prueba sería nombrado CuentaSpecs. Y digamos que este objeto particular tiene un método llamado Retirar, entonces quizás crearia algunas pruebas llamadas DebeRestarLosFondosAlRetirar, DebeFallarElRetiroSiNoTieneFondosSuficientes y así sucesivamente. Nota que en todas mis pruebas comencé con la palabra «debería». Es otra practica adoptada de BDD. Me gusta porque es más fácil expresar cual es el comportamiento esperado. Al final hay que recordar que no estas escribiendo pruebas, estas diseñando código. Por lo menos la implementacion de los métodos en los objetos. Por cierto, no te preocupes si decides hacer las cosas de una manera diferente al diagrama de interacción, esto es parte del show también y sucede muy a menudo que una buena idea resulta para ser demasiado compleja en el momento de codificarla. No tengas miedo a probar varios enfoques.

Crear interfaces para cualquier dependencia no definida todavía

Si al mismo tiempo de codificar descubres una dependencia a un objeto aún no creado, define una interfaz. No te quiebres la cabeza tratando de crear un objeto para proporcionar el servicio que necesitas, esto probablemente sea implementado por otro objeto mas adelante. Ademas de que esta fuera del alcance de la prueba. Así que define la interfaz con los métodos que necesitas y usala. No te sientes mal si tienes interfaces con solo 1 o 2 métodos, está bien. También es un efecto secundario a TDD y uno bueno:) (busca en google el principio de segregación de interfaz). De todos modos ahora necesitas un objeto que implemente la interfaz para usarlo en tu prueba. Puedes escribir uno. O mejor aun puedes usar mocks. Te recomiendo irte por esta ultima opción. No deberías perder tiempo escribiendo objetos que están fuera del alcance de lo que estas probando. Realmente no agrega valor. Busca en la red alguno de los muchos frameworks de mocking disponibles para la plataforma que estas usando. Experimenta hasta que encuentres uno con el que te sientas cómodo.

Algunas últimas palabras (y advertencias)

El propósito de todo lo que he cubierto aquí es para ayudarte a empezar. Conforme vayas obteniendo experiencias puedes omitir algunos de los pasos descritos aquí. Tal vez empieces a crear pruebas que representan el caso de uso conjunto en lugar de utilizar un diagrama de secuencia (yo lo he hecho). Pero si estas iniciando, te recomiendo seguir el procedimiento que he descrito arriba. Esto te dará algo de valor para empezar practicar TDD: contexto. ¿Qué probar? ¿qué no probar? ¿dónde empezar? que todo depende del contexto. Una vez establecido, las respuestas a estas preguntas fluirán naturalmente. El resto del procedimiento ya lo aprendiste de los libros, podcasts y post como éste: verde, rojo, refactorizar. También tengo una advertencia: no trates de escribir todas las pruebas desde el principio. No pienses en repasar todos casos de uso tratando de identificar cada prueba de antemano. Esta es una mala idea. La razón es simple: están diseñando código no escribiendo pruebas. Las pruebas son sólo una herramienta de diseño, como pluma y papel. Y al diseñar el código, hay varias decisiones que se tienen que hacer cuando algo inesperado surge en el código. También algunas pruebas darán lugar a la creación de otras.

Así que ahí lo tienen. Como siempre los comentarios son bienvenidos. Si tienes algún tip, sugerencia o una alternativa, me encantaría leerlo!

Lo básico parte 3: Herencia

Uno de los pilares de la programación orientada a objetos, es la herencia o la capacidad de crear jerarquías de objetos que van refinando o modificando cierto comportamiento.  Por ejemplo, imaginemos una empresa que vende artículos para el hogar. Imaginemos que vende lavadoras, estufas y licuadoras. Para motivar a sus vendedores, han puesto un sistema de comisiones que funciona de la siguiente manera:

Todos los vendedores tienen una cuota de ventas. Si el 25% de las ventas son por lavadoras, el vendedor obtiene una comisión del 15% de su salario. Si logra al menos el 50% de su comisión con la venta de licuadoras, entonces recibe una comisión del 30% de su salario. Por ultimo si logra el 25% de su cuota vendiendo estufas obtiene una comisión del 20% de su salario.

Ahora, supongamos que estos vendedores están distribuidos en zonas norte, este y sur. Los vendedores de la zona sur tienen una comisión adicional de 10% si exceden la cuota de ventas.

Podríamos representarlo de la siguiente manera:

Herencia

Entonces tenemos una clase CalculadoraComisiones. Los objetos de esta clase contienen toda las reglas para calcular las comisiones de cualquier vendedor. Una forma de implementar esto podría ser algo como:

public interface Vendedor
{
   bool CumplePorcentajeVenta(double porcentaje, string producto);
}


public class CalculadoraComisiones
{
  public virtual decimal CalcularComisiones(Vendedor vendedor)
  {
	  decimal comision = 0;
	  comision = comisionPorLavadoras(vendedor);
	  comision += comisionPorLicuadoras(vendedor);
	  comision += comisionPorEstufas(vendedor);
	  
	  return comision;
  }
  ...
}

Sin embargo para los vendedores de la zona sur, tenemos una regla adicional a las de todos los demás.

public class CalculadoraComisionesZonaSur:CalculadoraComisiones
{
   override CalcularComisiones(Vendedor vendedor)
   {
	   decimal comision = base.CalcularComisiones(vendedor);
	   comision + = comisionPorSobreVenta(vendedor);
	   return comision;
   }
   ...
}

Listo. Solo hay que utilizar el objeto correcto para calcular las comisiones.

void main(){
   List<Vendedor> vendedores = obtenerVendedores("Norte");
   vendedores.AddRange(obtenerVendedores("Este"));
   
  List<Vendedor>vendedoresSur = obtenerVendedores("Sur");
 

  var calculadora = new CalculadoraComisiones();
  for each(var vendedor in vendedores)
 {
    decimal comision = calculadora.CalcularComision(vendedor);
    Console.WriteLine(comision);
 }

  calculadora = new CalculadoraComisionesZonaSur();
 for each(var vendedor in vendedoresSur)
 {
  decimal comisiones = calculadora.CalcularComision(vendedor);
  Console.WriteLine(comision);
 }
}

Sencillo. Definitivamente este código deja mucho que desear pero ya lo iremos ajustando mas adelante.

Ahora quisiera señalar que no hay ninguna propiedad en el diagrama o en el código.
Hice esto deliberadamente para hacer énfasis en un principio fundamental: Se hereda el comportamiento, no los datos. En otras palabras usamos herencia cuando tenemos un objeto que tiene un método que queremos ajustar un poco a nuestras necesidades, no cuando queremos reutilizar datos. Eso es un error muy común. Viene de la mentalidad utilizada en las bases de datos relacionales, en la que se trata de reducir la duplicación de datos. En el caso de la programación orientada a objetos, se trata de reducir la duplicación de métodos. No tengan miedo de repetir datos en diferentes objetos.

Pues eso es todo por hoy, la próxima vez veremos el significado de la frase «favorecer composición sobre herencia» y como eso puede ayudarnos a mejorar el código de este ejemplo. Hasta entonces.

 

Hackeando un sistema de seguridad intercom para abrir la puerta con un teléfono

Cómo comenzó…

La oficina que actualmente trabajo en el se encuentra en un edificio que tiene un sistema de interfon. Cuando cualquiera de nosotros quiere entrar, basta con pulsar un botón y esperar que alguien responda y presione un botón en el auricular para abrir la puerta.

El problema se presenta cuando no hay nadie dentro (o esta en el baño).

Así surgió la idea de crear un servicio para abrir la puerta con el teléfono. ¿Cómo lo hacemos?

El algoritmo es algo como esto:
1) averiguar cómo funciona el botón de auricular
2) crear un circuito para hackear la señal del auricular
3) Codificar un servicio para activar el circuito
4) Hacer una aplicación de teléfono para comunicarse con el servicio

Así que vamos!

Descubra cómo funciona el auricular

Afortunadamente, en el trabajo contamos con gente muy hábil. Así que le pedí uno de ellos, Javier, echarle un vistazo al aparato. Resulta que la puerta funciona con un dispositivo magnético. El botón interrumpe el poder que mantiene el imán energizado. Lo hace tocando 2 pequeñas placas. No podía ser más sencillo.

Crear un circuito para hackear la señal

Usando un protoboard, Javier monto un circuito que consistió en circuit1un relé y un arduino (junto con un ethernet shield). La idea era conectar algunos cables en las placas, y usar el relé para cerrar el circuito. Para activar el relé, tendríamos que enviar que una instrucción al Arduino que estaria conectado al módem. Así que todo lo que tenemos que hacer es conectarnos a la red inalámbrica y enviar un comando a la dirección de ip de Arduino.

Codificar un servicio para activar el circuito

Ya que no tengo ninguna experiencia significativa codificando en un Arduino (aunque había codificado algo para un microcontrolador rabbit antes) me base en uno de los demos para un servidor web. Después de algunos ajustes y pruebas, termine con algo como:

#include <SPI.h>
#include <Ethernet.h>

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = {
  0x60, 0xA5, 0xEA, 0x0E, 0x07, 0x10
};
IPAddress ip(200, 200, 200, 200);

EthernetServer server(80);

void setup() {

  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  pinMode(7,OUTPUT);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }


  // start the Ethernet connection and the server:
  Ethernet.begin(mac, ip);
  server.begin();
  Serial.print("server is at ");
  Serial.println(Ethernet.localIP());
}


void loop() {
 
 // listen for incoming clients
  EthernetClient client = server.available();
  if (client) {
    Serial.println("new client");
    
// an http request ends with a blank line
    boolean currentLineIsBlank = true;
    String currentLine = "";
    String command = "";
    
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        currentLine = currentLine + c;
        
        // if you've gotten to the end of the line (received a newline
        // character) and the line is blank, the http request has ended,
        // so you can send a reply
        
        if (c == '\n' && currentLineIsBlank && command == "some command") {

          digitalWrite(7, HIGH);  
          delay(1000);              
          digitalWrite(7, LOW);    
          //delay(1000);  
          
          // send a standard http response header
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("Connection: close");  // the connection will be closed after completion of the response
          client.println();
          client.println("<!DOCTYPE HTML>");
          client.println("<html>");            
          client.println(command);
          client.println("</html>");
          command = "";
          break;
        }
        
        // you're starting a new line 
        if (c == '\n') {                                 
          currentLineIsBlank = true;
          Serial.println("current line = " + currentLine);
          currentLine = "";
        } 
       // you've gotten a character on the current line 
       else if (c != '\r') {                          
          currentLineIsBlank = false;
        } 

       //get the command
       else if (c == '\r' and currentLine.startsWith("GET")){ 
          int start = currentLine.indexOf('/')+1;
          int endAt = currentLine.indexOf(' ',start);
          command = currentLine.substring(start, endAt);         
        }
      }
    }

    // give the web browser time to receive the data
    delay(1);

    // close the connection:
    client.stop();
    Serial.println("client disconnected");
    Ethernet.maintain();
  }
}

La idea era proporcionar una API web que respondiera la petición a una URL concreta. A continuación extraemos la última parte de la url solicitada y utilizamos esto como comando para activar el relé y por lo tanto, abrir la puerta. Cualquier persona con la dirección ip y la URL correcta puede activar el relé, siempre y cuando sea la misma subred que el Arduino.

Código de una aplicación de teléfono para comunicarse con el servicio

Por lo tanto, Josue (otro compañero de trabajo) sugirió que puesto que había creado la web también podría crear una aplicación de teléfono para consumirla. Ya que la mayoría de las personas de la oficina tienen un teléfono android, pensé que sería una buena oportunidad para probar la plataforma. Tengo cero conocimiento del modelo de programación Android, asi que Josue sugirió que le echara un vistazo a Xamarin. Resulta ser bastante sencilla. Todavía tengo que aprender más de la plataforma…

Aquí está el código:

[Activity(Label = "Jarvis", MainLauncher = true, Icon = "@drawable/icon")]
    public class MainActivity : Activity
    {
        private string url = "http://200.200.200.200/";

        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);

            // Set our view from the "main" layout resource
            SetContentView(Resource.Layout.Main);

            // Get our button from the layout resource,
            // and attach an event to it
            Button button = FindViewById<Button>(Resource.Id.MyButton);
            TextView text = FindViewById<TextView>(Resource.Id.textView1);
            
            button.Click += async(sender,e) =>
            {
                try
                {
                    await send(jarvisCommands.openDoor);
                    text.Text += "Get in! \n\r";
                }
                catch (Exception ex)
                {
                    text.Text += ex.Message + "\n\r" + Resources.GetString(Resource.String.Error) + "\n\r";
                }

            };
        }

        private async Task send(jarvisCommands command)
        {
                HttpWebRequest request = (HttpWebRequest) HttpWebRequest.Create(new Uri(url + command.ToString()));
                request.ContentType = "application/text";
                request.Method = "GET";
                await request.GetResponseAsync();
         
        }

        enum jarvisCommands { openDoor}

Cosas aprendidas:
1) la necesidad es la madre de la inventiva
2) puedes crear cosas interesantes cuando estás rodeado de gente interesante
3) si sabes Java, C++, C# o cualquier otro idioma entre llaves, ya eres capaz de escribir algo para el Arduino
4) conceptualmente el codigo en la plataforma Android es sólo una implementación de un bus de mensajes

Creo que esto es todo. Hasta el próximo post! (Voy a escribir a menudo, en serio)

Lo basico parte 2

Haciendo que las cosas sucedan

La cuestión principal cuando se trata de OOD es: ¿Qué hace esto? Los objetos son sobre todo de hacer las cosas. Tienen algunos datos para hacer esta cosas, pero eso es todo. He visto cantidad de códigos donde los objetos no son más que contenedores de datos. No es OOP. Puede que estén utilizando un lenguaje de programación orientada a objetos, pero no están haciendo programación orientada a objetos. Ahora, no pretendo juzgar a nadie. Solía escribir código como ese. Esta es una de las razones que estoy compartiendo esto contigo. Los objetos hacen cosas. Deben tener una razón de existir. Un papel que cumplir.

Si prestaste atención al post anterior, definí un VirtualPet en términos de lo que hace. Duerme, juega y come. No empiezo con cosas como el nombre o la edad. Seguro que ésos se pueden utilizar para identificar un objeto determinado pero ¿que es tan bueno sobre la identificación de un objeto inútil?

Al diseñar una base de datos se inicia con datos. Al diseñar un modelo de objetos hay que empezar con el comportamiento. Parece haber un montón de gente que no sabe o no entiende esta diferencia sutil, lo que conduce a un montón de código difícil de mantener. Han oído hablar de patrones de diseño y tratan de aplicarlos a su código pseudo-orientado a objetos, a menudo resultando en complejidad innecesaria. Yo mismo he pasado por esto, preguntándome qué era tan grande de estos denominados patrones. ¿Te ha sucedido?

Encontrar al hombre adecuado para el trabajo

Una buena parte de la vida de un desarrollador se va en entender y aprender conceptos. Esto es porque muy a menudo los objetos representan conceptos de la vida real. Más aún, el mismo concepto puede tener un significado diferente en un contexto diferente. Un número de teléfono de casa puede ser un dato opcional en un sistema de gestión de clientes pero puede ser una pieza fundamental en un sistema de entrega de pizza;).

Crear un sistema flexible usando OOP consiste en gran parte en la identificación de los conceptos clave relacionados con un dominio y las responsabilidades relacionadas con ellos en un contexto determinado.

Así que vamos a hacer un pequeño ejercicio.

Supongamos que estamos haciendo un sistema bancario. Estamos asignados al siguiente caso:

Cobrar un cheque.
El Banco recibe un cheque de un cliente. 
El cliente puede solicitar retirar el efectivo o depositarlo a otra cuenta. 

Depositar en una cuenta
El cliente deposita una cantidad a una cuenta.

El enfoque orientado a datos

Así que aquí lo tenemos. ¿Vamos a pensarlo por un segundo sería? ¿por dónde empezamos? ¿Cuáles son los principales conceptos aquí? vamos a ver tiene que haber un cliente ¿correcto? quiero decir el cliente siempre comienza todo, trae el cheque, depositar el dinero, debemos tener uno ¿verdad? ¿verdad? Debe tener un nombre, tal vez una dirección, ¡seguramente un número de cuenta! Puede parecer algo así como:

imagen

Validar el modelo

Muy bien ¿qué sigue? bueno lo siguiente es preguntar: ¿Qué hace esto? ¿Qué hace el cliente? ¿Cuál es la Raison d’être? adelante tomate tu tiempo. Piensa en ello.

Así que imagínate que estoy en la misma habitación con el autor del diseño anterior, aquí esta cómo seria esta conversación (probablemente):

-así que ¿qué hace al cliente?
-bien obviamente cada cuenta tiene un dueño. Sería el cliente.
-sí, pero ¿qué hace?
-Mmm... ¿Qué dices? (frunciendo el ceño mientras piensa) eso sería... ayudar a encontrar una cuenta.
-Siempre podría utilizar el número de cuenta
-sí, pero no todo el mundo lo sabe. Caramba yo no sé mi código postal!
-muy bien, te entiendo. Así que el cliente ayuda a encontrar la cuenta correcta.
-Así es.
¿-Pero entonces, no tendría sentido añadir el cliente como un atributo de la cuenta?
-No creo que sea una buena idea.
-¿por qué no?
-porque son cosas diferentes. Diferentes conceptos.
-Así que estás diciendo que cada vez que quiero encontrar una cuenta ¿primero tengo que buscar el objeto de cliente?
-sí...
- y entonces supongo que yo llamaría un método llamado GetAccount()
-no necesariamente. Sólo tienes que ir a la propiedad de CustomerAccount
-por lo que el cliente no hace nada. Es solo un marcador.
-si hace! te permite acceder a la cuenta!
-… Pensando en sonrisa

El significado de «hacer»

Como se puede ver en nuestro debate ficticio, el problema radica en el significado de probablemente una de las palabras más usadas. Para ayudarnos a entender lo que «hacer» significa en este contexto permitanme reformular la pregunta: ¿Cuál es la misión de este objeto?

¿Qué hace un cartero? ¿Cuál es su misión? sería entregar correo. Él no te da su bolsa para que busques su correo. Y no importa si utiliza una bicicleta o un elefante mientras tu recibas tu correo. Así que «hacer» en este sentido define el propósito final del objeto.  En el argot OOP llamamos a esto la responsabilidad del objeto.

OK, ahora que aclaramos lo que queremos decir con la pregunta volvamos a nuestro pequeño ejercicio. ¿en que estabamos? ¡ Ah! ¿Qué hace el cliente? la respuesta corta es: nada. No entraré en la respuesta largaGuiño de sonrisa.

Continuemos con el objeto cheque (check en el diagrama). ¿Qué hace el cheque? por lo que hemos leído anteriormente recibe dinero de una cuenta. Regresaremos a él más adelante.

OK, el siguiente es el objeto cuenta. ¿Qué hace la cuenta? ¿mmm… podríamos decir que mantiene un saldo? ¿tiene sentido para ti?

Hasta el momento tenemos lo siguiente:

Objeto ¿Qué hace?
cliente nada
cheque obtener dinero de una cuenta
cuenta mantiene un saldo

La lente a través de la que vemos

El tipo de análisis «¿Qué hace esto?» no es nuevo. Está implícita en la técnica de tarjetas CRC de Kent Beck, así como en TDD. Lo importante aquí es que tenemos objetos que hacen cosas. Ahora manos a la obra Guiño de sonrisa

Lo primero que viene a la mente es el objeto cliente. Realmente no hace nada. Cualquier objeto que no hace nada en el modelo, sólo lo hacen más difícil de entender, así que vamos a deshacernos de ese cliente… (Es decir el objeto, no ese tipo presumido de mente cerrada de tu último proyecto! Apuesto a que estás pensando en él/ella ahora Sonrisa enojada)

Woah! ¿ningún objeto cliente? ¡pero es un modelo de banca el que estamos haciendo aquí! ¡tiene que existir un objeto cliente!

Bueno, veras, un modelo de objetos muestra sólo lo que es importante para un contexto específico. Solo pone de relieve lo que es importante para un caso de uso específico. Tal vez el siguiente ejemplo hará esto claro.

Optimizado-mundo-mapa-terreno

Tengo 2 mapas.  Se podría decir que ambos son modelos del mundo. La diferencia radica en lo que ellos están tratando de transmitir. El primero muestra las zonas horarias. El segundo pone énfasis en el terreno.

Así que como veras, un modelo de objetos es como un mapa. No muestra la realidad con todo detalle, solo lo relevante para el resolver el asunto pendiente. Llamamos a este proceso de identificación de los objetos importantes y acciones pertinentes, el proceso de abstracción. Y los objetos resultantes son llamados abstracciones, puesto que representan las cosas importantes en el dominio. OK, ahora demostrado mi punto, espero que me entiendas cuando digo que el cliente no es relevante para este modelo. Al menos no en este momento.

Cavando más profundo

Ahora que ya tenemos objetos y responsabilidades asignadas a ellos, vamos a averiguar cómo les llevan a cabo. Voy a empezar con el caso de uso «depósito en cuenta» ya que al parecer es el más sencillo (siempre empiezo con los casos de uso más sencillos). Este caso me dicen que el objeto cuenta tiene una operación de depósito que añade una cantidad a su saldo actual. Fácil.

Objeto ¿Qué hace? ¿Cómo lo hace?
cuenta mantiene un saldo Depositar(monto)

Ahora a «cobrar un cheque». Así que tenemos un método «cambiar» en el objeto cheque y necesita una cuenta de donde retirar. Pero el objeto de la cuenta no tiene una forma de retirar de sí mismo así que tenemos que crear una.

Objeto ¿Qué hace? ¿Cómo hace?
cheque obtener dinero de una cuenta Cambiar()
cuenta mantiene un saldo Depositar(monto)
Retirar(monto)

El cliente también puede depositar el cheque a otra cuenta. Efectivamente se trata de una operación de transferencia. ¿Donde debemos poner esto? ¿Quien tiene esta responsabilidad? para responder a esa pregunta tenemos que entender los efectos de esta operación. ¿Qué va a pasar después de la operación se ha completado? Si todo va bien una cuenta tendrá un decremento en su saldo mientras que en otra habrá incrementado. ¿Y quién es responsable de mantener un saldo? ¡la cuenta en si! no es un objeto ServicioCuenta o un objeto Transferencia sino que es la cuenta quien tiene esta responsabilidad. Así que vamos a crear un método transferencia en el objeto cuenta y un método CambiarEnCuenta en el objeto cheque.

Objeto ¿Qué hace? ¿Cómo hace?
Echale un vistazo obtener dinero de una cuenta Cambiar()
CambiarEnCuenta (cuenta)
cuenta mantiene un equilibrio Depositar(monto)
Retirar(monto)
Transferir(monto,cuenta)

Así que ahí lo tienen. Ambos casos con sólo 2 clases simples. Seguramente que tienes una idea bastante clara de cómo codificar esto ahora. Puede ser algo similar a:


class Cuenta
{
    decimal _saldo= 0;

    public bool depositar(decimal monto)
    {
        //reglas de negocio    
        _saldo += monto; 
        return true;
    }

    public bool Retirar (decimal monto)
    {
        //reglas de negocio   
        _saldo -= monto; 
        return true;
    }

    publiv bool Transferencia (decimal monto, cuenta aCuenta)
    {
        //reglas de negocio   
        aCuenta.Depositar(monto);
        Retirar(monto);
        return true;
    }
}

class Cheque
{
    decimal _monto= 0;
    Cuenta _cuentaOrigen;

    Control público (decimal monto, Cuenta cuenta)
    {
        _monto= cantidad;
        _cuentaOrigen= cuenta;
    }

    public bool Cambiar()
    {
        //reglas de negocio
        return _cuentaOrigen.retirar(_monto);
    }

    public bool CambiarEnCuenta(Cuenta cuenta)
    {
        //reglas de negocio
        return _cuentaOrigen.Transferir (monto: _monto, aCuenta: cuenta);
    }
}

Tarea: Volver a cualquiera de tus proyectos anteriores e identificar por lo menos 2 casos de uso, sus principales conceptos y si hay objetos que representan cada uno. Luego encontrar cuales son sus responsabilidades y, si no son claras, definirlas. Por último, identificar las operaciones necesarias para llevar responsabilidades del objeto. Compara y escribe tu comentario más abajo. Me avisas si este cambio simplifica el código actual o no (una comparacion simple es suficiente).

Nos vemos en el próximo post.

Lo básico

Palabras vacias

Si estas leyendo esto, probablemente ya habias escuchado algo de la programacion orientada a objetos. Es solo que nunca has entendido lo que quiere decir. ¿Para que sirve la herencia? y ¿que rayos significa polimorfismo? La razón por la que tanta gente batalla tanto al tratar de programar en el paradigma orientado a objetos es por que para ellos, estas aun son palabras vacías.

Luchando con la soledad

Yo era un niño cuando vi un tamagotchi por primera vez. Es una mascota virtual que vive dentro de un aparato del tamaño de un llavero. Ya que siempre quise uno pero nunca lo tuve, vamos a construir nuestra propia mascota virtual para que nos acompañe en esas largas y frías noches de trabajo. Pero primero, tenemos que definir que es una mascota virtual.

Un estuche de monerías virtual

Ok, para este ejercicio digamos que una mascota virtual come, duerme y juega. Todo depende del humor en que este que podria ser enojado, cansado o aburrido. Eso significa que nuestro amiguito no va a jugar si esta hambriento.

Asi que es hora de un poco de diversion. Tu mision, si deseas aceptarla, es crear un enum que represente el humor de esta creatura. Despues codifica sus acciones como de acuerdo a la especificacion. En sus marcas, listos, fuera!

using System.IO;
class VirtualPet
{
  public static VirtualPet CreateTiredPet(TextWriter output)
  {
      return new VirtualPet();
  }
  
  public static VirtualPet CreateHungryPet(TextWriter output)
  {
      return new VirtualPet();
  }
  
  public static VirtualPet CreateBoredPet(TextWriter output)
  {
      return new VirtualPet();
  }
  
  public void Eat(){}
    
  public void Play(){}
    
  public void Sleep(){}
}
class VirtualPetSpecs
{
    static TextWriter writer;
    
    public static void mustPlayOnlyIfBored ()
    {
        Console.WriteLine ("Must Play Only If Bored:");
        writer = new StringWriter();
        VirtualPet bobby = VirtualPet.CreateBoredPet(writer);
        bobby.Play();
        if (writer.ToString () == "playing time")
        Console.WriteLine("pass");
        else
        Console.WriteLine("fail");
    }
    
    public static void mustEatOnlyIfHungry ()
    {
        Console.WriteLine ("Must Eat Only If Hungry");
        writer = new StringWriter();
        VirtualPet bobby = VirtualPet.CreateHungryPet(writer);
        bobby.Eat();
        if (writer.ToString () == "yummy")
        Console.WriteLine("pass");
        else
        Console.WriteLine("fail");
    }
    
    public static void mustSleepOnlyIfTired()
    {
        Console.WriteLine ("Must Sleep Only If Tired");
        writer = new StringWriter();
        VirtualPet bobby = VirtualPet.CreateTiredPet(writer);
        bobby.Sleep();
        if (writer.ToString () == "zzzz...")
        Console.WriteLine("pass");
        else
        Console.WriteLine("fail");
    }
    
}
VirtualPetSpecs.mustPlayOnlyIfBored();
VirtualPetSpecs.mustEatOnlyIfHungry();
VirtualPetSpecs.mustSleepOnlyIfTired();

 

El efecto onda

Ok, ahora que lo hiciste funcionar vamos a modificarlo. Copia el codigo que escribiste y resetea todo usando el boton inferior izquierdo. Pega tu codigo de nuevo y modificalo para que use un diccionario en lugar de un enum. Adelante, yo te espero.
Ahora que has terminado dime, ¿que tan dificil fue eso? ¿de verdad fue muy diferente? ¿crees que podrias usar una lista en lugar del dccionario? ¿crees que podrias hacerlo funcionar con puras cadenas de texto? ¿seria eso muy dificil? ¿Se te ocurre alguna manera de almacenar esos estados en un archivo de configuracion? ¿o que tal en un web service? ¿podrias hacerlo?
¿Si hicieras alguno de estos cambios, como afectarias al objeto VirtualPetSpecs? ¿Por que?

La razon por la que este codigo es tan ‘cambiable’ es por que solo estas cambiando la manera en que hace las cosas pero no que son esas cosas. Hablando en el idioma OOP, estas cambiando la implementacion, no las responsabilidades. Ahora, imagina este codigo:

 

 static void mustPlayOnlyIfBored ()
{
  Console.WriteLine ("Must Play Only If Bored:");
  VirtualPet bobby = new VirtualPet ();
  bobby.mood = Mood.Bored;
  bobby.Play();
  if (bobby.mood == Mood.Tired)
   Console.WriteLine("pass");
  else
   Console.WriteLine("fail");
}

Intenta usar un diccionario, archivo de configuracion o web service. Es mas, trata de cambiar el humor a hambriento (Hungry). O crear un algoritmo que cambie el humor al azar. ¿Puedes hacer eso sin modificar la especificacion?
El problema con este codigo es que VirtualPet esta mostrando demasiado de la manera en que hace las cosas (detalles de implementacion en el idioma OOP). Esto conduce a que el codigo que usa este objeto dependa de los detalles de implementacion del mismo (acoplamiento en el idioma OOP) de ahi que sea imposible modificar este objeto sin modificar el codigo que lo usa. Yo lo llamo el efecto onda, ya que un cambio tiende a propagarse a otras partes del sistema. La unica manera de evitar el efecto onda es escondiendo los detalles de la implementacion por completo. Esto se llama encapsulamiento. Si, asi es: es lo mismo que te dijeron en tu clase de java o c++. Encapsulamiento no es nada mas que la forma de decir «¡esconde los detalles de implementacion!» en el idioma OOP. Y esto abre la puerta para un monton de cosas interesantes.

No te pierdas la parte 2 😉

P.D. Tarea: toma una copia de algun proyecto y empieza a buscar partes donde el codigo depende de los detalles de implementacion de algun objeto. Analiza el efecto onda y como modificar el codigo para prevenirlo. Pon tu respuesta en los comentarios..

Matriz de arquitectura de aplicaciones

La matriz de arquitectura de aplicaciones es una herramienta que se me ocurrió para explicar a un amigo la relación entre los patrones de diseño, las tecnologías y la arquitectura de un proyecto en el que participe por un breve tiempo. Permite a cualquier desarrollador que tenga cierta experiencia, obtener una idea general de la manera en la que esta estructurada una aplicación.Esto resulta especialmente útil en una época en que los sistemas distribuidos son cada vez mas la regla que la excepción y donde el soporte para múltiples plataformas es un requisito en mas y mas proyectos.

Consiste en 3 columnas, Capa, Tecnología y Patrones de diseño.

Supongamos por ejemplo que estamos participando en un proyecto que sigue la metodología DDD (Domain Driven Design). Nuestra matriz probablemente se vería algo como:

CAPA TECNOLOGÍA PATRONES
Presentación ASP.Net MVC MVC
Aplicación ASP.Net Web API Application Services
Dominio Portable Class Library Aggregate, Entity, Value Object, Repository, Domain Services
Infraestructura WCF, MS SQL 2008, Entity Framework

o quitando la capa de aplicación:

CAPA TECNOLOGÍA PATRONES
Presentación ASP.Net MVC MVC
Dominio Portable Class Library Aggregate, Entity, Value Object, Repository, Domain Services
Infraestructura WCF, MS SQL 2008, Entity Framework

Y allí lo tienen. Fácil ¿no? Solo me resta aclarar que la columna Capa, se refiere a la separación lógica (layer en ingles) y no física (tier en ingles) de una aplicación. Esto quiere decir que todas las capas bien podrían estar en una aplicación winforms.

Tarea: Crea una matriz para algún proyecto en el que hayas trabajado recientemente.

¿Creen que me falto algún aspecto significativo de la arquitectura de una aplicación? ¿Como usarías esta matriz? ¿Que cambios le harías?

Evolucodigo

En el inicio…

Tenía como 14 años cuando empecé a programar. Empece con archivos .bat en ms dos. Mas adelante mi padre me consiguió una copia de visual studio 6 y un amigo me presto un libro para aprender a programar en visual basic 6. Estaba emocionado. Era otro mundo. Pase las vacaciones ese verano en frente de una computadora y me encantó. Y ya entrados en materia descubri access y aprendí un poco de sql. Mas adelante aprendí foxpro 2.6  y como ya tenia una copia de visual foxpro 6, empecé a programar en vfp6 también.

Primer encuentro

Solia leer mucho en ese tiempo. Hasta la fecha. Un día encontré un articulo en MSDN llamado algo asi como vb6 business objects. Cuando empecé a leerlo aparecieron muchas dudas en mi mente: ‘objetos’? ‘Herencia’? ‘Eventos’ me sonaba familiar, pero que rayos era  ‘polimorfismo’? .
Afortunadamente, Rockford Lhotka (el autor) había agregado una introducción a la programacion orientada a objetos. Eso fue suficiente para entender cual era el próximo paso.

La búsqueda

Des afortunadamente no había mucha información sobre el tema en aquel entonces (o al menos yo no tenía acceso a ella), así que solo me quedaba entrar en la universidad para aprender mas. Pero no fue así. Si me hablaron de encapsulamiento, herencia, polimorfismo y todo el mumbo jumbo de términos. Pero nunca aprendí a pensar de una manera orientada a objetos. Sin embargo, la escuela tenía una conexión a Internet decente y así me dí a la tarea de buscar todo lo que pudiera acerca del tema. Me encontré con uml y me pareció interesante…
Y también xml, html (¿que pasa con tanto ML?).
Aun cuando tome un curso en c++ (que era mas como c) y java (del tipo forms over data) todavía me sentía mas cómodo en vb6. Incluso trate de practicar programación orientada a objetos en vb6 pero la falta de soporte para la herencia me dificulto hacer casi cualquier ejercicio encontrado en la red.

Un rayo de esperanza

Alrededor del séptimo semestre tuve la oportunidad de entrar a trabajar en una pequeña empresa de desarrollo de software. Estaba tan emocionado! Al fin! Un lugar para aprender todo lo que había buscado todo este tiempo!  Y si que aprendí! C#, sql server y el .net framework. Y también tenía la oportunidad de trabajar en proyectos reales. Pero aun sentía que me faltaba algo. Me tomo un par de años darme cuenta: estaba programando foxpro en el .net framework. Aun seguía pensando orientado a funciones. Aun cuando estaba usando objetos, no eran mas que contenedores de datos que pasábamos de una función a otra. Así que traté de ver las cosas de otra perspectiva. Esta vez realmente trate de producir código orientado a objetos. Investigue y trate de practicar TDD, DDD, BDD (ahora la moda es DD?) Y poco a poco empecé a lograrlo. Empece a hacer la transición hacia una mentalidad orientada a objetos. Todavía me falta mucho pero sigo avanzando.

Todo está en la mente

Ya que he trabajado en diferentes lugares, desde empresas de desarrollo hasta empresas transnacionales, me he encontrado con todo tipo de código. Así me di cuenta que hay un montón de gente que está en la misma situación que yo hace un par de años. Tienen herramientas mas novedosas pero siguen haciendo lo mismo. Desde entonces me he preguntado como ayudar a estos programadores a iniciar la transición hacia un tren de pensamiento mas orientado a objetos. Este blog es un intento. Y no soy el único. Hay un mundo de información: libros, artículos, cursos, que compartiré aquí. Son cosas que me han funcionado y espero que a ti también. Así que no te vayas, la diversión está por comenzar 😉