<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: g-esman</title>
    <description>The latest articles on Forem by g-esman (@gesman).</description>
    <link>https://forem.com/gesman</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2529313%2F7242e6b2-ff08-416b-9819-13833cb87d86.png</url>
      <title>Forem: g-esman</title>
      <link>https://forem.com/gesman</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/gesman"/>
    <language>en</language>
    <item>
      <title>SOLID: Principio de Abierto/Cerrado</title>
      <dc:creator>g-esman</dc:creator>
      <pubDate>Sun, 22 Dec 2024 16:48:44 +0000</pubDate>
      <link>https://forem.com/gesman/solid-principio-de-abiertocerrado-2aji</link>
      <guid>https://forem.com/gesman/solid-principio-de-abiertocerrado-2aji</guid>
      <description>&lt;p&gt;¡Buenas, buenas! a vos que estás curioseando del otro lado. Bienvenido al segundo post de esta serie sobre SOLID, hoy analizaremos el siguiente principio: Principio de Abierto/Cerrado. ¡¡¡Empecemos!!!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Open/Closed Principle (OCP) - Principio de Abierto/Cerrado&lt;/strong&gt;&lt;br&gt;
La definición de diccionario de este principio dice que un módulo debe estar abierto para extensiones pero cerrada para modificaciones.&lt;br&gt;
¿Qué quiere decir? &lt;br&gt;
Que debería ser capaz de agregar nuevas funcionalidades al código sin necesidad de modificar las existentes. Ejemplo rápido: pensemos en una casa, tengo que poder agregarle nuevas habitaciones (abierto a extensiones) sin tener que demoler o reconstruir las ya existentes (cerrado a modificaciones).&lt;/p&gt;

&lt;p&gt;La importancia de este principio radica en evitar efectos secundarios, ya que al no modificar el código existente se reduce la posibilidad de introducir errores en módulos funcionales, facilitando así el mantenimiento.&lt;/p&gt;

&lt;p&gt;Analicemos un punto clave de cómo se utiliza este principio antes de pasar a un ejemplo.&lt;br&gt;
Polimorfismo: ya lo vimos en el post sobre POO (que si no lo leíste aún, te recomiendo arrancar por &lt;a href="https://dev.to/gesman/poo-entrando-a-un-nuevo-viejo-mundo-1bff"&gt;aquí&lt;/a&gt;), pero hagamos un breve resumen. Se trata de hacer uso de clases padres (clases base, abstractas o interfaces), para tener un contrato esperado y que las clases hijas se encarguen de las implementaciones.&lt;/p&gt;

&lt;p&gt;Ahora sí, un ejemplo donde podría aplicar este principio.&lt;br&gt;
Veamos cómo quedó el código del módulo anterior.&lt;/p&gt;

&lt;p&gt;Primero vamos a tener nuestra base de clases Invoice y nuestro FakeStorage&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class Invoice
{
    public List&amp;lt;InvoiceItem&amp;gt; Items { get; set; } = new List&amp;lt;InvoiceItem&amp;gt;();
}
public class InvoiceItem
{
    public string Name { get; set; }
    public decimal Price { get; set; }
    public int Quantity { get; set; }
}
public class FakeStorage&amp;lt;T&amp;gt;
{
    private ObservableCollection&amp;lt;T&amp;gt; collection;

    public FakeStorage()
    {
        collection = new ObservableCollection&amp;lt;T&amp;gt;();
    }

    public T Add(T item)
    {
        collection.Add(item);
        return item;
    }

    public IEnumerable&amp;lt;T&amp;gt; GetAll()
    {
        return collection;
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;nuestro InvoiceRepository e InovicePrinter&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class InvoiceRepository
{
    private static FakeStorage&amp;lt;Invoice&amp;gt; storage;

    public InvoiceRepository()
    {
        storage = new();
        InitData();
    }
    private void InitData()
    {
        storage.Add(new Invoice
        {
            Items = new List&amp;lt;InvoiceItem&amp;gt;
            {
                new InvoiceItem { Name = "vino", Price = 54, Quantity= 2}
            }
        });
        storage.Add(new Invoice
        {
            Items = new List&amp;lt;InvoiceItem&amp;gt;
            {
                new InvoiceItem { Name = "atun", Price = 80, Quantity= 5}
            }
        });
    }
    public IEnumerable&amp;lt;Invoice&amp;gt; GetAll()
    {
        return storage.GetAll();
    }
}

public class InvoicePrinter
{
    public void Print(decimal total)
    {
        Console.WriteLine($"El monto total de sus facturas es de {total}");
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Y aquí el cambio, ahora tenemos dos InvoiceCalculator, uno para impuestos de Argentina y otro para Impuestos de USA.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class InvoiceCalculatorArgentina
{
    private const decimal iva = 1.21M;
    public decimal GetTotal(IEnumerable&amp;lt;Invoice&amp;gt; invoices)
    {
        decimal total = 0;
        foreach (var invoice in invoices)
        {
            total += invoice.Items.Sum(item =&amp;gt; item.Price * item.Quantity) * iva;
        }
        return total;
    }
}
public class InvoiceCalculatorUSA
{
    private const decimal iva = 1.07M;
    public decimal GetTotal(IEnumerable&amp;lt;Invoice&amp;gt; invoices)
    {
        decimal total = 0;
        foreach (var invoice in invoices)
        {
            total += invoice.Items.Sum(item =&amp;gt; item.Price * item.Quantity) * iva;
        }
        return total;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finalmente, el Program para ver cómo interactuar con nuestro programa. Supongamos que va a imprimir las mismas facturas con el total para USA y el total para Argentina.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Program
{
    static void Main(string[] args)
    {
        PrintInvoices(new List&amp;lt;object&amp;gt;(){
            new InvoiceCalculatorUSA(),
            new InvoiceCalculatorArgentina()
        });
    }

    static void PrintInvoices(List&amp;lt;object&amp;gt; calculators)
    {

        foreach (var calculator in calculators)
        {
            var InvoiceRepository = new InvoiceRepository();
            var InvoicePrinter = new InvoicePrinter();
            IEnumerable&amp;lt;Invoice&amp;gt; invoices = InvoiceRepository.GetAll();
            if (calculator is InvoiceCalculatorUSA)
            {
                var usaCalculator = (InvoiceCalculatorUSA)calculator;
                decimal total = usaCalculator.GetTotal(invoices);
                InvoicePrinter.Print(total);
            }
            else
            {
                var argCalculator = (InvoiceCalculatorArgentina)calculator;
                decimal total = argCalculator.GetTotal(invoices);
                InvoicePrinter.Print(total);
            }
        }

    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hasta aquí todo bien, pero notemos el problema de no aplicar el principio de Open-Close. Si quisiéramos agregar un nuevo país para calcular los totales de esas facturas e imprimirlo, deberíamos agregar la nueva clase, definir su comportamiento, y también modificar el if del program que ya no nos serviría. Deberíamos reemplazarlo por otra estructura, como un switch.&lt;br&gt;
Veamos cómo modificar estas clases para cumplir el principio de open-close.&lt;/p&gt;

&lt;p&gt;Primero creemos una clase abstracta, InvoiceCalculator&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public abstract class InvoiceCalculator
{
    protected virtual decimal Iva =&amp;gt; 1M;
    public decimal GetTotal(IEnumerable&amp;lt;Invoice&amp;gt; invoices)
    {
        decimal total = 0;
        foreach (var invoice in invoices)
        {
            total += invoice.Items.Sum(item =&amp;gt; item.Price * item.Quantity) * Iva;
        }
        return total;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aquí podemos observar lo siguiente: Iva ahora es una propiedad protected y virtual, o sea solo puede accederse a ella desde la clase que la define o desde quienes la hereden.&lt;br&gt;
También colocamos aquí el comportamiento esperado para GetTotal. Como las clases hijas van a compartir el mismo comportamiento, solo se necesita modificar el impuesto y este método se puede mantener inmutable en las clases hijas.&lt;/p&gt;

&lt;p&gt;Ahora veamos cómo implementar esta abstracción en nuestras calculadoras existentes y sumemos una más.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class InvoiceCalculatorUSA : InvoiceCalculator
{
    protected override decimal Iva =&amp;gt; 1.07M;
}
public class InvoiceCalculatorArgentina : InvoiceCalculator
{
    protected override decimal Iva =&amp;gt; 1.21M;
}
public class InvoiceCalculatorCostaRica : InvoiceCalculator
{
    protected override decimal Iva =&amp;gt; 1.13M;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Es mucho más sencillo, solo sobrescribimos el Iva.&lt;/p&gt;

&lt;p&gt;Finalmente, veamos cómo modificar el program para interactuar con nuestro código ya creado y aprovechar la herencia de la abstracción.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Program
{
    static void Main(string[] args)
    {
        PrintInvoices(new List&amp;lt;InvoiceCalculator&amp;gt;(){
            new InvoiceCalculatorUSA(),
            new InvoiceCalculatorArgentina(),
            new InvoiceCalculatorCostaRica()
        });

        Console.ReadKey();
    }

    static void PrintInvoices(List&amp;lt;InvoiceCalculator&amp;gt; calculators)
    {

        foreach (var calculator in calculators)
        {
            var InvoiceRepository = new InvoiceRepository();
            var InvoicePrinter = new InvoicePrinter();
            IEnumerable&amp;lt;Invoice&amp;gt; invoices = InvoiceRepository.GetAll();
            decimal total = calculator.GetTotal(invoices);
            InvoicePrinter.Print(total);
        }

    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;¿Qué pasó aquí? Como ya no necesitamos saber qué tipo de InvoiceCalculator es, podemos deshacernos del if y solamente indicar que realice el método de GetTotal. Luego cuando llegue el turno de cada implementación de InvoiceCalculator, estas sabrán qué hacer, ya que heredan este comportamiento del padre. También, como ahora tienen la misma base, ya podemos especificar que no es una lista de objetos sino una lista de InvoiceCalculator.&lt;br&gt;
Ahora podemos ver cómo nuestro código cumple el principio. Cada vez que necesitemos agregar un nuevo país al cálculo de impuestos en los totales, solo debemos extender el código, creando una nueva clase que herede de InvoiceCalculator, es decir que esta parte del código quedó cerrada a modificaciones pero abierta a extensiones.&lt;/p&gt;

&lt;p&gt;Hemos llegado al final del segundo posteo de esta serie sobre los principios SOLID. En los siguientes artículos exploraremos otro de los principios de SOLID para crear sistemas sólidos, flexibles y robustos. Espero que las explicaciones hayan sido claras, igualmente te invito a dejar tus dudas, sugerencias o ejemplos en los comentarios. Te leo!&lt;/p&gt;

</description>
      <category>solidprinciples</category>
      <category>programming</category>
      <category>development</category>
      <category>cleancoding</category>
    </item>
    <item>
      <title>SOLID: Principio de Responsabilidad Única</title>
      <dc:creator>g-esman</dc:creator>
      <pubDate>Mon, 16 Dec 2024 18:08:19 +0000</pubDate>
      <link>https://forem.com/gesman/solid-principio-de-responsabilidad-unica-g6o</link>
      <guid>https://forem.com/gesman/solid-principio-de-responsabilidad-unica-g6o</guid>
      <description>&lt;p&gt;Buenas, buenas, a vos que estás curioseando del otro lado. Bienvenido al primer post de esta serie donde vamos a estar hablando sobre SOLID (y no, no estoy hablando de Solid Snake). Hoy analizaremos qué es SOLID y veremos el primer principio.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿De qué se trata SOLID?&lt;/strong&gt;&lt;br&gt;
Es una serie de principios de diseño de software, reunidos y presentados por primera vez en conjunto por Robert "Uncle Bob" Martin. Estos principios tienen como objetivo sentar unas bases sobre las cuales podamos crear un código mas fácil de mantener, escalar y entender. Son un "Must" si queremos escribir código limpio.&lt;br&gt;
SOLID es un acrónimo de los 5 principios que representa:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;S&lt;/strong&gt;ingle Responsibility Principle&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;O&lt;/strong&gt;pen-Close Principle&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;L&lt;/strong&gt;iskov Substitution Principle&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I&lt;/strong&gt;nterface Segregation Principle &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;D&lt;/strong&gt;ependency Inversion Principle&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Single Responsibility Principle (SRP) - Principio de Responsabilidad Única&lt;/strong&gt;&lt;br&gt;
La premisa de este principio es que cada módulo debe tener una única responsabilidad, es decir que debe tener una única razón para cambiar.&lt;br&gt;
¿A qué nos referimos con esto?&lt;br&gt;
Supongamos un módulo de que tenga por objetivo imprimir en pantalla el total facturado.&lt;br&gt;
Suena algo único ¿no? Pero internamente puede involucrar procesos de obtención de datos, procesamiento y generación del reporte. &lt;br&gt;
Entonces, se presentan 3 situaciones en las que nuestro módulo podría verse modificado. Si nos preguntamos por las razones que podría tener para cambiar, resulta más intuitiva la separación de responsabilidades,&lt;br&gt;
"cambiar si se modifica el formato en que quiero mostrar el total",&lt;br&gt;
"cambiar si aplica un nuevo impuesto al cálculo del total",&lt;br&gt;
"cambiar si la obtención de facturación lo requiere"&lt;br&gt;
Los factores de cambio son más sencillos de especificar y nos dan ese feedback que nos ayuda a determinar si estamos dando demasiada responsabilidad a un módulo.&lt;/p&gt;

&lt;p&gt;Aplicar este principio hace que las clases sean más fáciles de mantener y probar. Podemos modificarlas sin efectos secundarios, si algo no sale bien solo tenemos que buscar en un módulo. Entonces, aplicando el principio de responsabilidad única (SRP), reducimos el acoplamiento, evitando clases muy estrechamente vinculadas.&lt;/p&gt;

&lt;p&gt;Llevemos a código el ejemplo anterior. Creamos un Invoice que contiene una lista de InvoiceItems y un FakeStorage, para emular luego una interacción con una base de datos.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class Invoice
{
    public List&amp;lt;InvoiceItem&amp;gt; Items { get; set; } = new List&amp;lt;InvoiceItem&amp;gt;();
}
public class InvoiceItem
{
    public string Name { get; set; }
    public decimal Price { get; set; }
    public int Quantity { get; set; }
}
public class FakeStorage&amp;lt;T&amp;gt;
{
    private ObservableCollection&amp;lt;T&amp;gt; collection;

    public FakeStorage()
    {
        collection = new ObservableCollection&amp;lt;T&amp;gt;();
    }

    public T Add(T item)
    {
        collection.Add(item);
        return item;
    }

    public IEnumerable&amp;lt;T&amp;gt; GetAll()
    {
        return collection;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A continuación, con esta base ya resuelta tenemos la clase InvoiceRepository y analicemos cuál es su comportamiento.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class InvoiceRepository
{
    private static FakeStorage&amp;lt;Invoice&amp;gt; storage;

    public InvoiceRepository()
    {
        storage = new();
        InitData();
    }
    private const decimal iva = 1.21M;
    private void InitData()
    {
        storage.Add(new Invoice
        {
            Items = new List&amp;lt;InvoiceItem&amp;gt;
            {
                new InvoiceItem{ Name = "vino", Price = 54, Quantity= 2}
            }
        });
        storage.Add(new Invoice
        {
            Items = new List&amp;lt;InvoiceItem&amp;gt;
            {
                new InvoiceItem{ Name = "atun", Price = 80, Quantity= 5}
            }
        });
    }
    public void ShowTotal()
    {
        var invoices = storage.GetAll();
        decimal total = 0;
        foreach (var invoice in invoices)
        {
            total += invoice.Items.Sum(item =&amp;gt; item.Price * item.Quantity) * iva;
        }
        Console.WriteLine($"Factura generada en PDF. para el total {total}");
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lo primero que hace InvoiceRepository en su constructor es ejecutar InitData, que nos sirve para agregar en nuestro fakeStorage elementos de Invoice; luego, sigue el método ShowTotal, que abarca muchas responsabilidades: desde obtener los invoice, hasta hacer los cálculos e imprimir.Como mencionamos antes, demasiadas razones para cambiar.&lt;/p&gt;

&lt;p&gt;Ahora pasemos a la última parte de este código, el Program donde interactuamos con todo el programa en conjunto.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Program
{
    static void Main(string[] args)
    {
        var repo = new InvoiceRepository();
        repo.ShowTotal()
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ya que detectamos que nuestro InvoiceRepository no cumple con el primer principio de SOLID, toca refactorizarlo. Veamos cómo aplicar este principio de responsabilidad única.&lt;/p&gt;

&lt;p&gt;Empecemos por el InvoiceRepository&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class InvoiceRepository
{
    private static FakeStorage&amp;lt;Invoice&amp;gt; storage;

    public InvoiceRepository()
    {
        storage = new();
        InitData();
    }
    private void InitData()
    {
        storage.Add(new Invoice
        {
            Items = new List&amp;lt;InvoiceItem&amp;gt;
            {
                new InvoiceItem { Name = "vino", Price = 54, Quantity= 2}
            }
        });
        storage.Add(new Invoice
        {
            Items = new List&amp;lt;InvoiceItem&amp;gt;
            {
                new InvoiceItem { Name = "atun", Price = 80, Quantity= 5}
            }
        });
    }
    public IEnumerable&amp;lt;Invoice&amp;gt; GetAll()
    {
        return storage.GetAll();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En esta nueva versión, podemos ver que ahora el InvoiceRepository solo se encarga de conectar nuestro código con los datos de nuestras bases de datos (aquí lo emulamos con el fakeStorage).&lt;/p&gt;

&lt;p&gt;Ahora trasladamos la lógica de calcular los totales a un InvoiceCalculator.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class InvoiceCalculator
{
    private const decimal iva = 1.21M;
    public decimal GetTotal(IEnumerable&amp;lt;Invoice&amp;gt; invoices)
    {
        decimal total = 0;
        foreach (var invoice in invoices)
        {
            total += invoice.Items.Sum(item =&amp;gt; item.Price * item.Quantity) * iva;
        }
        return total;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Como la responsabilidad del cálculo de totales ahora pasó a una nueva clase, si se aplica un nuevo impuesto o descuento, este es el único lugar donde debemos aplicar modificaciones.&lt;/p&gt;

&lt;p&gt;Finalmente, la tercer parte de la lógica: la impresión pasa a estar a cargo de una nueva clase, InvoicePrinter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class InvoicePrinter
{
    public void Print(decimal total)
    {
        Console.WriteLine($"El monto total de sus facturas es de {total}");
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Si se nos pide cambiar la impresión en pantalla por una en pdf o el texto mostrado, ahora tenemos una clase única a ser modificada.&lt;/p&gt;

&lt;p&gt;Para finalizar, modifiquemos el Program para ver cómo interactúa con todas las clases.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Program
{
    static void Main(string[] args)
    {
        var InvoiceRepository = new InvoiceRepository();
        var InvoiceCalculator = new InvoiceCalculator();
        var InvoicePrinter = new InvoicePrinter();
        IEnumerable&amp;lt;Invoice&amp;gt; invoices = InvoiceRepository.GetAll();
        decimal total = InvoiceCalculator.GetTotal(invoices);
        InvoicePrinter.Print(total);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ahora sí tenemos un código mas limpio, modular y escalable, siguiendo el principio de responsabilidad única.&lt;/p&gt;

&lt;p&gt;Hemos llegado al final del primer posteo de esta serie sobre los principios SOLID. Este es solo el comienzo: en los siguientes artículos exploraremos cómo los demás principios se complementan con SRP para crear sistemas sólidos, flexibles y robustos. Espero que las explicaciones hayan sido claras, igualmente te invito a dejar tus dudas, sugerencias o ejemplos en los comentarios. Te leo!&lt;/p&gt;

</description>
      <category>cleancoding</category>
      <category>development</category>
      <category>programming</category>
      <category>solidprinciples</category>
    </item>
    <item>
      <title>POO entrando a un nuevo viejo mundo</title>
      <dc:creator>g-esman</dc:creator>
      <pubDate>Sat, 07 Dec 2024 18:39:44 +0000</pubDate>
      <link>https://forem.com/gesman/poo-entrando-a-un-nuevo-viejo-mundo-1bff</link>
      <guid>https://forem.com/gesman/poo-entrando-a-un-nuevo-viejo-mundo-1bff</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Hola dev, potencial dev o persona curiosa del otro lado! Bienvenido a mi primer Post, en el que hablaremos sobre POO (Programación Orientada a Objetos).&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Me llamo Gastón Esman, me desempeño como desarrollador de software hace mas de 10 años. Este blog fue pensado como un espacio para compartir los conocimientos que adquirí durante este tiempo, espero que sea de tu utilidad!&lt;/p&gt;

&lt;p&gt;¿Estás listo? Vamos desde el principio:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;¿Que es la Programación Orientada a Objetos?&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
POO es un paradigma de programación que apunta a representar conceptos del mundo real como objetos. Un ejemplo rápido: vos y yo somos individuos, "Personas". Si bien somos únicos, a un nivel de abstracción los dos podemos ser representados por una clase genérica, "Persona". En programación las clases sirven como templetes, o planos que se utilizan para crear objetos. En este caso, del templete de "Persona" podemos obtener tanto un objeto llamado Gastón como el tuyo "Lector".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;¿Por qué usar este paradigma?&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
POO ofrece una manera simple de representar conceptos del mundo real, haciendo que los programas sean mas modulares, reusables y escalables.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Clases y Objetos:&lt;/strong&gt;&lt;br&gt;
Las clases son planos que definen comportamiento y atributos, mientras que un objeto es una instancia creada tomando ese plano como referencia.&lt;/p&gt;

&lt;p&gt;Entonces, volvamos a nuestro ejemplo ¿Qué tenemos en común? Somos seres humanos, poseemos un nombre, una edad, y tenemos la habilidad de realizar acciones, como hablar. Entonces podemos abstraernos de mí, Gastón, y de vos, "lector", y crear el plano o clase "Persona".&lt;br&gt;
Veamos un ejemplo para dejarlo mas claro:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class Persona
{
    public string Nombre { get; set; }
    public int Edad { get; set; }
    public void Saludar()
    {
        Console.WriteLine($"Hola, me llamo {Nombre} y tengo {Edad} años.");
    }

}

Persona persona1 = new Persona
{
    Nombre = "Gastón",
    Edad = 32
};
persona1.Saludar(); //output Hola, me llamo Gastón y tengo 32 años.

Persona persona2 = new Persona
{
    Nombre = "Lector",
    Edad = 23
};
persona2 .Saludar(); //output Hola, me llamo Lector y tengo 23 años.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En el ejemplo, Persona es el plano o clase. persona1 y persona2 son objetos instanciados de esa clase.&lt;br&gt;
Cada objeto persona posee su nombre y edad propias, llamados atributos. Además, puede realizar acciones, como Saludar. Estas acciones se denominan comportamientos o, técnicamente hablando, métodos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Encapsulamiento&lt;/strong&gt;&lt;br&gt;
Se trata de esconder los detalles internos de una clase y solo exponer lo absolutamente necesario al resto del programa.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class Persona
{
    private int Documento;
    private string nombre;

    public string GetNombre()
    {
        return nombre;
    }

    public void SetNombre(string nuevoNombre)
    {
        if (!string.IsNullOrEmpty(nuevoNombre))
        {
            nombre= nuevoNombre;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;¿Que vemos en el código?:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Los atributos Nombre y Documento son privados, por lo que ocultamos esta información al resto del programa, sin poder acceder directamente por fuera de la clase, de esta manera protejo su integridad.&lt;/li&gt;
&lt;li&gt;Para interactuar con Nombre proveemos un getter (si pretendemos obtener este valor) y un setter (para modificarlo), asegurándonos que tenemos el control sobre los posibles cambios al atributo.&lt;/li&gt;
&lt;li&gt;Por ejemplo, si alguien quiere dejar sin nombre al objeto "Lector", la validación no se lo permitirá, ya que la única forma es pasar por el setter.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;¿Porque es importante?&lt;/em&gt;&lt;br&gt;
El Encapsulamiento asegura que el estado interno de un objeto no sea accidental o maliciosamente modificado. Ofrece un código más robusto y fácil de mantener.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Herencia&lt;/strong&gt;&lt;br&gt;
Este concepto permite que una clase hijo pueda heredar tanto atributos como comportamiento de una clase padre.&lt;br&gt;
El objetivo principal tras este concepto es reducir la redundancia y crear un código más reusable, ahorrando tiempo al no tener que repetir código innecesariamente.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;¿Cómo es que funciona?&lt;/em&gt;&lt;br&gt;
Clase hija:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hereda atributos y comportamiento de la clase padre.&lt;/li&gt;
&lt;li&gt;Puede extender a la clase padre añadiendo nuevo comportamiento o atributos.&lt;/li&gt;
&lt;li&gt;Tiene la capacidad de sobrescribir total o parcialmente el comportamiento heredado, al proveer una implementación especifica.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class Animal
{
    public string Nombre{ get; set; }

    public virtual void Mover()
    {
        Console.WriteLine("me muevo.");
    }
}

public class Loro : Animal
{
    // nuevo método exclusivo para loro
    public void Hablar(string loQueDijiste)
    {
        Console.WriteLine($"Khaaaa {loQueDijiste} Khaaaa {loQueDijiste}.");
    }

    // Sobrescribir el método Mover
    public override void Mover()
    {
        Console.WriteLine("vuelo.");
    }
}

// Aplicado
Loro poly = new Loro ();
poly.Nombre= "Poly";
poly.Mover(); // Output: vuelo.
poly.Hablar("quiero una galleta"); // Output: Khaaaa quiero una galleta Khaaaa quiero una galleta.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;em&gt;¿Qué pasó acá?&lt;/em&gt;&lt;br&gt;
La clase Loro heredó de la clase Animal.&lt;br&gt;
Como resultado, tiene acceso a la propiedad Nombre y al comportamiento de la clase Animal.&lt;br&gt;
Agregamos en la clase Loro un comportamiento propio, el método Hablar.&lt;br&gt;
Sobrescribimos Mover, para darle una implementación más específica: "volar".&lt;br&gt;
&lt;em&gt;¡Por qué es importante esto?&lt;/em&gt;&lt;br&gt;
La herencia ayuda a modelar una aplicación de forma jerárquica, logrando que sea más fácil de representar relaciones entre entidades, ahorrándonos tiempo y líneas de código, consiguiendo módulos más compactos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Polimorfismo&lt;/strong&gt;&lt;br&gt;
Este concepto provee de gran flexibilidad al código, lo que nos permite trabajar con diferentes tipos de objetos que comparten un mismo padre, sin la necesidad de conocer específicamente a cada clase hija, ya que todos implementan al padre, solo nos tenemos que comunicar con el padre, y los hijos van a saber qué hacer.&lt;/p&gt;

&lt;p&gt;Veamos un ejemplo: sigamos con nuestra clase padre Animal, pero esta vez agregaremos una segunda clase hija además de Loro, la clase Salmon, Usando polimorfismo, no tenemos que entrar en el detalle de la implementación de cada hijo en métodos heredados del padre, como Mover. En su lugar, nos enfocamos en las definiciones del padre y confiamos en que cada hijo manejará de forma apropiada su comportamiento específico.&lt;/p&gt;

&lt;p&gt;Llevémoslo a código:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class Animal
{
    public string Nombre { get; set; }
    public virtual void Mover()
    {
        Console.WriteLine($"Me muevo");
    }
}

public class Loro : Animal
{
    public void Hablar(string loQueDijiste)
    {
        Console.WriteLine($"Khaaaa {loQueDijiste} Khaaaa {loQueDijiste}.");
    }

    public override void Mover()
    {
        Console.WriteLine($"Vuelo.");
    }
}

public class Salmon : Animal
{
    public override void Mover()
    {
        Console.WriteLine($"Nado.");
    }
}

public class AnimalHandler
{
    private readonly Animal _animal;

    public AnimalHandler(Animal animal)
    {
        _animal = animal;
    }

    public void MoverAnimal()
    {
        _animal.Mover();
    }
}


public class Program
{
    public static void Main(string[] args)
    {
        AnimalHandler loroHandler = new AnimalHandler(new Loro());
        AnimalHandler salmonHandler = new AnimalHandler(new Salmon());

        loroHandler.MoverAnimal(); // Output: Vuelo.
        salmonHandler.MoverAnimal();  // Output: Nado.
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;¿Qué paso acá?&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Clase Padre:&lt;br&gt;
La clase Animal define el método Mover, el cual puede ser sobrescrito por los hijos.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Clases Hijas:&lt;br&gt;
Loro sobrescribe Mover para proveer una implementación más específica, "Volar", Salmon hace lo mismo pero su implementación específica es "Nadar".&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Comportamiento del polimorfismo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;La clase AnimalHandler trabaja con un objeto del tipo Animal, pero gracias al polimorfismo, puede manejar cualquier clase hija de Animal (Loro y Salmon para nuestro caso).
Al método MoverAnimal no le importa si trabaja con un Loro o un Salmon, él llama a Mover, y la implementación correspondiente se ejecuta.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Abstracción&lt;/strong&gt;&lt;br&gt;
¡Bienvenido a la caja negra! La abstracción permite poner el foco en qué hace un componente en lugar de cómo lo hace. En programación esto significa que podemos definir una clase abstracta o una interfaz que especifique qué mensajes (métodos) comprenderá la clase, sin importar los detalles de las implementaciones internas.&lt;/p&gt;

&lt;p&gt;La responsabilidad de la implementación, el cómo, recae en las clases que hereden de estas abstracciones, generando un código más limpio, modular y fácil de mantener.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interfaces vs Clases abstractas&lt;/strong&gt;&lt;br&gt;
Ambas son herramientas para definir la estructura de clases potenciales, pero tienen sus propósitos específicos.&lt;br&gt;
Veamos las diferencias:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Interfaces&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Definición: es un contrato que especifica métodos que una clase tiene  obligación de implementar.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Características:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;No posee atributos ni estado.&lt;/li&gt;
&lt;li&gt;Permite herencia múltiple.&lt;/li&gt;
&lt;li&gt;Todos los métodos son implícitamente abstractos y deben ser implementados por las clases que la hereden.&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Caso de uso: cuando solo necesitamos definir un set de acciones sin comportamiento predefinido.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Clase abstracta&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Definición: es una clase que puede tener métodos abstractos (a ser implementados por las clases hijas) y métodos concretos (con un comportamiento predefinido).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Características:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Puede incluir atributos y estados.&lt;/li&gt;
&lt;li&gt;No admite herencia múltiple, solo puede heredar una clase abstracta.&lt;/li&gt;
&lt;li&gt;Sirve de base para clases que tienen comportamiento en común.&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Caso de uso: cuando precisamos no solo establecer un contrato, sino predefinir algún tipo de comportamiento.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public abstract class Animal
{
    public string Nombre { get; set; }
    public abstract void Comer()
    {
        Console.WriteLine($"Estoy comiendo");
    }
}
public interface IComunicacion
{
    public void HacerSonido();
}

public interface IMovimiento 
{
    public void Mover();
}

public class Salmon: Animal, IMovimiento , IComunicacion
{
    public void HacerSonido()
    {
        Console.WriteLine($"Glu glu");
    }

    public void Mover()
    {
        Console.WriteLine($"Nadar.");
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        Salmon salmon = new Salmon ();
        salmon.Comer(); // Output: Estoy comiendo
        salmon.Move(); // Output: Nadar.
        salmon.HacerSonido(); // Output: Glu glu.
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Composición vs Herencia&lt;/strong&gt;&lt;br&gt;
Mientras que la herencia describe una relación de &lt;em&gt;"es-un"&lt;/em&gt;, la composición describe una relación de &lt;em&gt;"tiene-un"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Herencia&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Establece una relación padre-hijo.&lt;/li&gt;
&lt;li&gt;Ejemplo:
Salmon ES UN animal.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Composición&lt;/em&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Describe cómo un objeto es contenido en otro objeto.&lt;/li&gt;
&lt;li&gt;Crea más flexibilidad en el código y, como resultado, menos acoplamiento comparado con la herencia.&lt;/li&gt;
&lt;li&gt;Ejemplo:
Salmon TIENE UNAS escamas.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public abstract class Animal
{
    public string Nombre { get; set; }
    public abstract void Comer()
    {
        Console.WriteLine($"Comer.");
    }
}

public class Escamas
{
    public string Color { get; set; }
}

public class Salmon : Animal
{
    public Escamas Escamas{ get; set; }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Llegamos al final de este posteo. Espero que las explicaciones hayan sido claras, igualmente te invito a dejar tus dudas, sugerencias o ejemplos en los comentarios. Te leo!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>oop</category>
      <category>csharp</category>
      <category>cleancode</category>
    </item>
  </channel>
</rss>
