CodeGym /Blog Java /Random-ES /Principios de programación orientada a objetos
Autor
Milan Vucic
Programming Tutor at Codementor.io

Principios de programación orientada a objetos

Publicado en el grupo Random-ES
Java es un lenguaje orientado a objetos. Esto significa que necesita escribir programas Java utilizando un paradigma orientado a objetos. Y este paradigma implica el uso de objetos y clases en sus programas. Intentemos usar ejemplos para comprender qué son las clases y los objetos, y cómo aplicar los principios básicos de OOP (abstracción, herencia, polimorfismo y encapsulación) en la práctica.

¿Qué es un objeto?

El mundo en el que vivimos está hecho de objetos. Mirando a nuestro alrededor, podemos ver que estamos rodeados de casas, árboles, automóviles, muebles, platos y computadoras. Todas estas cosas son objetos, y cada uno de ellos tiene un conjunto de características, comportamientos y propósitos específicos. Estamos acostumbrados a los objetos, y siempre los usamos para propósitos muy específicos. Por ejemplo, si necesitamos ir al trabajo, usamos un automóvil. Si queremos comer, usamos platos. Y si queremos descansar, encontramos un cómodo sofá. Los seres humanos están acostumbrados a pensar en términos de objetos para resolver problemas en la vida cotidiana. Esta es una de las razones por las que se utilizan objetos en la programación. Este enfoque se llama programación orientada a objetos. Demos un ejemplo. Imagina que has desarrollado un nuevo teléfono y quieres comenzar la producción en masa. Como desarrollador del teléfono, sabes para qué sirve, cómo funciona y cuáles son sus partes (cuerpo, micrófono, parlante, cables, botones, etc.). Además, solo tú sabes cómo conectar estas partes. Pero no planea hacer los teléfonos personalmente, tiene todo un equipo de trabajadores para hacerlo. Para eliminar la necesidad de explicar repetidamente cómo conectar las partes del teléfono y asegurarse de que todos los teléfonos estén hechos de la misma manera, antes de comenzar a producirlos, debe hacer un dibujo que describa cómo está organizado el teléfono. En OOP, llamamos clase a tal descripción, dibujo, diagrama o plantilla. Forma la base de la creación de objetos cuando el programa se está ejecutando. Una clase es una descripción de objetos de cierto tipo, como una plantilla común que consta de campos, métodos y un constructor. Un objeto es una instancia de una clase.

Abstracción

Ahora pensemos en cómo podemos pasar de un objeto en el mundo real a un objeto en un programa. Usaremos el teléfono como ejemplo. Este medio de comunicación tiene una historia que abarca más de 100 años. El teléfono moderno es un dispositivo mucho más complejo que su predecesor del siglo XIX. Cuando usamos el teléfono, no pensamos en su organización y los procesos que ocurren dentro de él. Simplemente usamos las funciones provistas por los desarrolladores del teléfono: botones o una pantalla táctil para ingresar un número de teléfono y realizar llamadas. Una de las primeras interfaces telefónicas era una manivela que había que girar para hacer una llamada. Por supuesto, esto no era muy conveniente. Pero cumplió su función a la perfección. Si comparas los teléfonos más modernos y los primeros, puede identificar de inmediato las funciones más importantes para el dispositivo de finales del siglo XIX y para el teléfono inteligente moderno. Son la capacidad de hacer llamadas y la capacidad de recibir llamadas. De hecho, esto es lo que hace que el teléfono sea un teléfono y no otra cosa. Ahora acaba de aplicar un principio de programación orientada a objetos: identificar las características e información más importantes de un objeto. Este principio se llama abstracción. En OOP, la abstracción también se puede definir como un método para representar elementos de una tarea del mundo real como objetos en un programa. La abstracción siempre se asocia con la generalización de ciertas propiedades de un objeto, por lo que lo principal es separar la información significativa de la insignificante en el contexto de la tarea en cuestión. Además, puede haber varios niveles de abstracción. Dejar' Intentamos aplicar el principio de abstracción a nuestros teléfonos. Para comenzar, identificaremos los tipos de teléfonos más comunes, desde los primeros hasta los actuales. Por ejemplo, podríamos representarlos en la forma del diagrama de la Figura 1. Principios de programación orientada a objetos - 2Mediante la abstracción, ahora podemos identificar la información general en esta jerarquía de objetos: el objeto abstracto general (teléfono), las características comunes del teléfono (por ejemplo, el año de su creación) y la interfaz común (todos los teléfonos pueden recibir y realizar llamadas). Así es como se ve en Java:

public abstract class AbstractPhone {
    private int year;

    public AbstractPhone(int year) {
        this.year = year;
    }
    public abstract void call(int outgoingNumber);
    public abstract void ring(int incomingNumber);
}
En un programa, podemos crear nuevos tipos de teléfonos usando esta clase abstracta y aplicando otros principios básicos de programación orientada a objetos, que exploraremos a continuación.

Encapsulación

Con la abstracción, identificamos lo que es común para todos los objetos. Pero cada tipo de teléfono es único, de alguna manera diferente de los demás. En un programa, ¿cómo trazamos límites e identificamos esta individualidad? ¿Cómo hacemos para que nadie pueda romper accidental o deliberadamente nuestro teléfono o intentar convertir un modelo en otro? En el mundo real, la respuesta es obvia: debe colocar todas las piezas en una funda de teléfono. Después de todo, si no lo hace, en lugar de dejar todas las partes internas del teléfono y los cables de conexión en el exterior, algún experimentador curioso definitivamente querrá "mejorar" nuestro teléfono. Para evitar tales retoques, se utiliza el principio de encapsulación en el diseño y funcionamiento de un objeto. Este principio establece que los atributos y el comportamiento de un objeto se combinan en una sola clase, el objeto. La implementación interna de s está oculta para el usuario y se proporciona una interfaz pública para trabajar con el objeto. La tarea del programador es determinar cuáles de los atributos y métodos de un objeto deben estar disponibles para el acceso público y cuáles son detalles de implementación internos que deben ser inaccesibles.

Encapsulación y control de acceso

Supongamos que la información sobre un teléfono (su año de producción o el logotipo del fabricante) está grabada en su parte posterior cuando se fabrica. La información (su estado) es específica de este modelo en particular. Podemos decir que el fabricante se aseguró de que esta información fuera inmutable; es poco probable que alguien pensara en eliminar el grabado. En el mundo de Java, una clase describe el estado de los objetos futuros usando campos y su comportamiento se describe usando métodos. El acceso al estado y comportamiento de un objeto se controla mediante modificadores aplicados a campos y métodos: privado, protegido, público y predeterminado. Por ejemplo, decidimos que el año de producción, el nombre del fabricante y uno de los métodos son detalles de implementación internos de la clase y no pueden ser cambiados por otros objetos en el programa. En codigo,

public class SomePhone {

    private int year;
    private String company;
    public SomePhone(int year, String company) {
        this.year = year;
        this.company = company;
    }
private void openConnection(){
    // findSwitch
    // openNewConnection...
}
public void call() {
    openConnection();
    System.out.println("Calling");
}

public void ring() {
    System.out.println("Ring-ring");
}

 }
El modificador privado permite acceder a los campos y métodos de la clase solo dentro de esta clase. Esto significa que es imposible acceder a campos privados desde el exterior, porque no se pueden llamar a los métodos privados. Restringir el acceso al método openConnection también nos permite cambiar libremente la implementación interna del método, ya que se garantiza que el método no será utilizado por otros objetos ni interrumpirá su trabajo. Para trabajar con nuestro objeto, dejamos disponibles los métodos call y ring usando el modificador public. Proporcionar métodos públicos para trabajar con objetos también es parte de la encapsulación, ya que si se denegara el acceso por completo, sería inútil.

Herencia

Echemos otro vistazo al diagrama de teléfonos. Puede ver que es una jerarquía en la que un modelo tiene todas las características de los modelos ubicados más arriba a lo largo de su rama y agrega algunas propias. Por ejemplo, un teléfono inteligente utiliza una red celular para la comunicación (tiene las propiedades de un teléfono celular), es inalámbrico y portátil (tiene las propiedades de un teléfono inalámbrico) y puede recibir y realizar llamadas (tiene las propiedades de un teléfono). Lo que tenemos aquí es la herencia de las propiedades de los objetos. En programación, herencia significa usar clases existentes para definir otras nuevas. Consideremos un ejemplo del uso de la herencia para crear una clase de teléfono inteligente. Todos los teléfonos inalámbricos funcionan con baterías recargables, que tienen una duración determinada. En consecuencia, agregamos esta propiedad a la clase de teléfono inalámbrico:

public abstract class CordlessPhone extends AbstractPhone {

    private int hour;

    public CordlessPhone (int year, int hour) {
        super(year);
        this.hour = hour;
    }
    }
Los teléfonos celulares heredan las propiedades de un teléfono inalámbrico, e implementamos los métodos de llamada y timbre en esta clase:

public class CellPhone extends CordlessPhone {
    public CellPhone(int year, int hour) {
        super(year, hour);
    }

    @Override
    public void call(int outgoingNumber) {
        System.out.println("Calling " + outgoingNumber);
    }

    @Override
    public void ring(int incomingNumber) {
        System.out.println("Incoming call from " + incomingNumber);
    }
}
Y, por último, tenemos la clase de teléfonos inteligentes, que, a diferencia de los teléfonos celulares clásicos, tiene un sistema operativo completo. Puede expandir la funcionalidad de su teléfono inteligente agregando nuevos programas que pueden ejecutarse en su sistema operativo. En código, la clase se puede describir de la siguiente manera:

public class Smartphone extends CellPhone {
    
    private String operationSystem;

    public Smartphone(int year, int hour, String operationSystem) {
        super(year, hour);
        this.operationSystem = operationSystem;
    }
public void install(String program) {
    System.out.println("Installing " + program + " for " + operationSystem);
}

}
Como puede ver, creamos bastante código nuevo para describir la clase Smartphone , pero obtuvimos una nueva clase con una nueva funcionalidad. Este principio de programación orientada a objetos permite reducir significativamente la cantidad de código Java necesario, lo que facilita la vida del programador.

Polimorfismo

A pesar de las diferencias en la apariencia y el diseño de varios tipos de teléfonos, podemos identificar algunos comportamientos comunes: todos pueden recibir y realizar llamadas y todos tienen un conjunto de controles bastante claro y simple. En términos de programación, el principio de abstracción (con el que ya estamos familiarizados) nos permite decir que los objetos de teléfono tienen una interfaz común. Es por eso que las personas pueden usar fácilmente diferentes modelos de teléfonos que tienen los mismos controles (botones mecánicos o una pantalla táctil), sin profundizar en los detalles técnicos del dispositivo. Por lo tanto, usa un teléfono celular constantemente y puede hacer una llamada fácilmente desde el teléfono fijo de su amigo. El principio de OOP que dice que un programa puede usar objetos con una interfaz común sin ninguna información sobre la estructura interna del objeto se llama polimorfismo. Dejar' Imaginemos que necesitamos que nuestro programa describa a un usuario que puede usar cualquier teléfono para llamar a otro usuario. Así es como podemos hacerlo:

public class User {
    private String name;

    public User(String name) {
        this.name = name;
            }

    public void callAnotherUser(int number, AbstractPhone phone){
// And here's polymorphism: using the AbstractPhone type in the code!
        phone.call(number);
    }
}
 }
Ahora describiremos varios tipos de teléfonos. Uno de los primeros teléfonos:

public class ThomasEdisonPhone extends AbstractPhone {

public ThomasEdisonPhone(int year) {
    super(year);
}
    @Override
    public void call(int outgoingNumber) {
        System.out.println("Crank the handle");
        System.out.println("What number would you like to connect to?");
    }

    @Override
    public void ring(int incomingNumber) {
        System.out.println("The phone is ringing");
    }
}
Un teléfono fijo ordinario:

public class Phone extends AbstractPhone {

    public Phone(int year) {
        super(year);
    }

    @Override
    public void call(int outgoingNumber) {
        System.out.println("Calling " + outgoingNumber);
    }

    @Override
    public void ring(int incomingNumber) {
        System.out.println("The phone is ringing");
    }
}
Y, por último, un videoteléfono genial:

public class VideoPhone extends AbstractPhone {

    public VideoPhone(int year) {
        super(year);
    }
    @Override
    public void call(int outgoingNumber) {
        System.out.println("Connecting video call to " + outgoingNumber);
    }
    @Override
    public void ring(int incomingNumber) {
        System.out.println("Incoming video call from " + incomingNumber);
    }
  }
Crearemos objetos en el método main() y probaremos el método callAnotherUser() :

AbstractPhone firstPhone = new ThomasEdisonPhone(1879);
AbstractPhone phone = new Phone(1984);
AbstractPhone videoPhone=new VideoPhone(2018);
User user = new User("Jason");
user.callAnotherUser(224466, firstPhone);
// Crank the handle
// What number would you like to connect to?
user.callAnotherUser(224466, phone);
// Calling 224466
user.callAnotherUser(224466, videoPhone);
// Connecting video call to 224466
Llamar al mismo método en el objeto de usuario produce resultados diferentes. Una implementación específica del método de llamada se selecciona dinámicamente dentro del método callAnotherUser() según el tipo específico de objeto pasado cuando se ejecuta el programa. Esta es la principal ventaja del polimorfismo: la capacidad de elegir una implementación en tiempo de ejecución. En los ejemplos de clases telefónicas anteriores, usamos la anulación de métodos, un truco en el que cambiamos la implementación de un método definido en la clase base sin cambiar la firma del método. Básicamente, esto reemplaza el método: el nuevo método definido en la subclase se llama cuando se ejecuta el programa. Por lo general, cuando anulamos un método, @Overridese utiliza la anotación. Le dice al compilador que verifique las firmas de los métodos anulados y anulados. Finalmente, para asegurarse de que sus programas Java sean consistentes con los principios de OOP, siga estos consejos:
  • identificar las características principales de un objeto;
  • identificar propiedades y comportamientos comunes y utilizar la herencia al crear clases;
  • usar tipos abstractos para describir objetos;
  • trate de ocultar siempre los métodos y campos relacionados con la implementación interna de una clase.
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION