JavaRush /Blog Java /Random-ES /Pausa para el café #90. 4 pilares de la programación orie...

Pausa para el café #90. 4 pilares de la programación orientada a objetos

Publicado en el grupo Random-ES
Fuente: The Geek Asian Echemos un vistazo a los cuatro fundamentos de la programación orientada a objetos y tratemos de comprender cómo funcionan. La programación orientada a objetos (POO) es uno de los principales paradigmas de programación. Puede ser fácil y sencillo o, por el contrario, muy complejo. Todo depende de cómo decidas desarrollar tu aplicación. Pausa para el café #90.  4 pilares de la programación orientada a objetos - 1Hay 4 pilares de la programación orientada a objetos:
  1. Encapsulación.
  2. Herencia.
  3. Abstracción.
  4. Polimorfismo.
Ahora discutiremos cada uno de ellos con una breve explicación y un ejemplo de código real.

1. Encapsulación

Todos hemos estudiado la encapsulación para ocultar elementos de datos y permitir a los usuarios acceder a los datos mediante métodos públicos. A estos los llamamos captadores y definidores. Ahora olvidémonos de esto y busquemos una definición más simple. La encapsulación es un método para impedir que el usuario cambie directamente miembros de datos o variables de clase para mantener la integridad de los datos. Cómo hacemos esto? Restringimos el acceso a las variables cambiando el modificador de acceso a privado y exponiendo métodos públicos que se pueden utilizar para acceder a los datos. Veamos ejemplos específicos a continuación. Esto nos ayudará a comprender cómo podemos utilizar la encapsulación para mantener la integridad de los datos. Sin encapsulación:
/**
 * @author thegeekyasian.com
 */
public class Account {

  public double balance;

  public static void main(String[] args) {

  	Account theGeekyAsianAccount = new Account();

  	theGeekyAsianAccount.balance = -54;
  }
}
En el fragmento de código anterior, el método main() accede directamente a la variable de saldo . Esto permite al usuario establecer cualquier valor doble para la variable de saldo de la clase Cuenta . Podemos perder la integridad de los datos al permitir que cualquiera establezca el saldo en cualquier número no válido, como -54 en este caso. Con encapsulación:
/**
 * @author thegeekyasian.com
 */
public class Account {

  private double balance;

  public void setBalance(double balance) {

    if(balance >= 0) { // Validating input data in order to maintain data integrity
	  this.balance = balance;
    }

    throw new IllegalArgumentException("Balance cannot be less than zero (0)");
  }

  public static void main(String[] args) {

  	Account theGeekyAsianAccount = new Account();

  	theGeekyAsianAccount.setBalance(1); // Valid input - Allowed
  	theGeekyAsianAccount.setBalance(-55); // Stops user and throws exception
  }
}
En este código, restringimos el acceso a la variable de saldo y agregamos un método setBalance() que permite a los usuarios establecer el valor del saldo para Cuenta . El configurador verifica el valor proporcionado antes de asignarlo a la variable. Si el valor es menor que cero, se lanza una excepción. Esto garantiza que la integridad de los datos no se vea comprometida. Después de explicar los ejemplos anteriores, espero que quede claro el valor de la encapsulación como uno de los cuatro pilares de la programación orientada a objetos.

2. Herencia

La herencia es un método para obtener propiedades de otra clase que comparten características comunes. Esto nos permite aumentar la reutilización y reducir la duplicación de código. El método también tiene el principio de interacción padre-hijo, cuando un elemento hijo hereda las propiedades de su padre. Profundicemos en dos ejemplos rápidos y veamos cómo la herencia hace que el código sea más simple y reutilizable. Sin herencia:
/**
 * @author thegeekyasian
 */
public class Rectangle {

  private int width;
  private int height;

  public Rectangle(int width, int height) {
	this.width = width;
	this.height = height;
  }

  public int getArea() {
	return width * height;
  }
}

public class Square {

  private int width; // Duplicate property, also used in class Rectangle

  public Square(int width) {
	this.width = width;
  }

  public int getArea() { // Duplicate method, similar to the class Rectangle
	return this.width * this.width;
  }
}
Las dos clases similares comparten las propiedades de ancho y el método getArea() . Podemos aumentar la reutilización del código haciendo una pequeña refactorización donde la clase Cuadrado termina heredando de la clase Rectángulo . Con herencia:
/**
 * @author thegeekyasian
 */
public class Rectangle {

  private int width;
  private int height;

  public Rectangle(int width, int height) {
	this.width = width;
	this.height = height;
  }

  public int getArea() {
	return width * height;
  }
}

public class Square extends Rectangle {

  public Square(int width) {
	super(width, width); // A rectangle with the same height as width is a square
  }
}
Simplemente extendiendo la clase Rectángulo , obtenemos la clase Cuadrado como un tipo Rectángulo . Esto significa que hereda todas las propiedades comunes a Cuadrado y Rectángulo . En los ejemplos anteriores, vemos cómo la herencia juega un papel importante a la hora de hacer que el código sea reutilizable. También permite que una clase herede el comportamiento de su clase padre.

3. Abstracción

La abstracción es una técnica que consiste en presentar sólo detalles esenciales al usuario ocultando detalles innecesarios o irrelevantes de un objeto. Ayuda a reducir la complejidad operativa por parte del usuario. La abstracción nos permite proporcionar una interfaz simple al usuario sin solicitar detalles complejos para realizar una acción. En pocas palabras, le brinda al usuario la capacidad de conducir un automóvil sin necesidad de comprender exactamente cómo funciona el motor. Veamos primero un ejemplo y luego analicemos cómo nos ayuda la abstracción.
/**
* @author thegeekyasian.com
*/
public class Car {

  public void lock() {}
  public void unlock() {}

  public void startCar() {

	checkFuel();
	checkBattery();
	whatHappensWhenTheCarStarts();
  }

  private void checkFuel() {
	// Check fuel level
  }

  private void checkBattery() {
	// Check car battery
  }

  private void whatHappensWhenTheCarStarts() {
	// Magic happens here
  }
}
En el código anterior, los métodos lock() , unlock() y startCar() son públicos y el resto son privados para la clase. Hemos facilitado al usuario "conducir el coche". Por supuesto, podría verificar manualmente checkFuel() y checkBattery() antes de arrancar el auto con startCar() , pero eso simplemente complicaría el proceso. Con el código anterior, todo lo que el usuario debe hacer es usar startCar() y la clase se encargará del resto. Esto es lo que llamamos abstracción.

4. Polimorfismo

El último y más importante de los cuatro pilares de la programación orientada a objetos es el polimorfismo. Polimorfismo significa "muchas formas". Como su nombre indica, es una función que permite realizar una acción de múltiples o diferentes formas. Cuando hablamos de polimorfismo, no hay mucho que discutir a menos que hablemos de sus tipos. Hay dos tipos de polimorfismo:
  1. Sobrecarga de métodos: polimorfismo estático (enlace estático).
  2. Anulación de método: polimorfismo dinámico (enlace dinámico).
Analicemos cada uno de estos tipos y veamos cuál es la diferencia entre ellos.

Sobrecarga de métodos: polimorfismo estático:

La sobrecarga de métodos o polimorfismo estático, también conocida como enlace estático o enlace en tiempo de compilación, es un tipo en el que las llamadas a métodos se determinan en tiempo de compilación. La sobrecarga de métodos nos permite tener múltiples métodos con el mismo nombre, con diferentes tipos de datos de parámetros, o diferentes números de parámetros, o ambos. Pero la pregunta es, ¿por qué es útil la sobrecarga de métodos (o polimorfismo estático)? Veamos los ejemplos siguientes para comprender mejor la sobrecarga de métodos. Sin sobrecarga de métodos:
/**
* @author thegeekyasian.com
*/
public class Number {

  public void sumInt(int a, int b) {
	System.out.println("Sum: " + (a + b));
  }

  public void sumDouble(double a, double b) {
	System.out.println("Sum: " + (a + b));
  }

  public static void main(String[] args) {

	Number number = new Number();

	number.sumInt(1, 2);
	number.sumDouble(1.8, 2.5);
  }
}
En el ejemplo anterior, creamos dos métodos con nombres diferentes, solo para sumar dos tipos diferentes de números. Si continuamos con una implementación similar, tendremos múltiples métodos con diferentes nombres. Esto reducirá la calidad y disponibilidad del código. Para mejorar esto, podemos usar la sobrecarga de métodos usando el mismo nombre para diferentes métodos. Esto permitirá al usuario tener una opción como punto de entrada para sumar diferentes tipos de números. La sobrecarga de métodos funciona cuando dos o más métodos tienen el mismo nombre pero parámetros diferentes. El tipo de devolución puede ser el mismo o diferente. Pero si dos métodos tienen el mismo nombre, los mismos parámetros, pero diferentes tipos de retorno, esto provocará una sobrecarga y un error de compilación. Con sobrecarga de métodos:
/**
* @author thegeekyasian.com
*/
public class Number {

  public void sum(int a, int b) {
	System.out.println("Sum: " + (a + b));
  }

  public void sum(double a, double b) {
	System.out.println("Sum: " + (a + b));
  }

  public static void main(String[] args) {

	Number number = new Number();

	number.sum(1, 2);
	number.sum(1.8, 2.5);
  }
}
En el mismo código, con algunos cambios menores, pudimos sobrecargar ambos métodos, haciendo que los nombres fueran iguales para ambos. El usuario ahora puede especificar sus tipos de datos específicos como parámetros del método. Luego realizará una acción basada en el tipo de datos que se proporciona. Este enlace de método se realiza en tiempo de compilación porque el compilador sabe qué método se llamará con el tipo de parámetro especificado. Por eso lo llamamos enlace en tiempo de compilación.

Anulación de método: polimorfismo dinámico:

A diferencia de la sobrecarga de métodos, la anulación de métodos le permite tener exactamente la misma firma que varios métodos, pero deben estar en varias clases diferentes. La pregunta es, ¿qué tiene de especial? Estas clases tienen una relación IS-A, es decir, deben heredar unas de otras. En otras palabras, en la anulación de métodos o polimorfismo dinámico, los métodos se procesan dinámicamente en tiempo de ejecución cuando se llama al método. Esto se hace en base a la referencia al objeto con el que se inicializa. A continuación se muestra un pequeño ejemplo de anulación de método:
/**
* @author thegeekyasian.com
*/
public class Animal {

  public void walk() {
	System.out.println("Animal walks");
  }
}

public class Cat extends Animal {

  @Override
  public void walk() {
	System.out.println("Cat walks");
  }
}

public class Dog extends Animal {

  @Override
  public void walk() {
	System.out.println("Dog walks");
  }
}

public class Main {

  public static void main(String[] args) {

	Animal animal = new Animal();
	animal.walk(); // Animal walks

	Cat cat = new Cat();
	cat.walk(); // Cat walks

	Dog dog = new Dog();
	dog.walk(); // Dog walks

	Animal animalCat = new Cat(); // Dynamic Polymorphism
	animalCat.walk(); // Cat walks

	Animal animalDog = new Dog(); // Dynamic Polymorphism
	animalDog.walk(); //Dog walks
  }
}
En este ejemplo principal, asignamos dinámicamente objetos de tipo "Perro" y "Gato" al tipo "Animal". Esto nos permite llamar dinámicamente al método walk() en instancias referenciadas en tiempo de ejecución. Podemos hacer esto usando la anulación de métodos (o polimorfismo dinámico). Con esto concluye nuestra breve discusión sobre los cuatro pilares de la programación orientada a objetos y espero que le resulte útil.
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION