![translation](https://cdn.durumis.com/common/trans.png)
Esta es una publicación traducida por IA.
[Objetos] Capítulo 2. Programación orientada a objetos
- Idioma de escritura: Coreano
- •
-
País de referencia: Todos los países
- •
- Tecnología de la información
Seleccionar idioma
Texto resumido por la IA durumis
- Explica el enfoque de la programación orientada a objetos para implementar un sistema de reserva de entradas para películas, presentando los conceptos de colaboración, objetos y clases, y enfatizando la estructura del programa que sigue la estructura del dominio.
- En particular, presenta métodos para reducir la interferencia externa y garantizar la libertad del programador mediante la autonomía de los objetos y el encapsulamiento, separando la interfaz de la implementación.
- También explica cómo escribir código flexible y extensible utilizando la colaboración, la herencia y el polimorfismo para calcular tarifas de descuento, y enfatiza la importancia de la abstracción y las ventajas de la composición para la reutilización del código.
Sistema de reserva de películas
Revisar los requisitos
- Se pretende implementar un sistema de reserva de películas online.
- Una película representa la información básica sobre una película.
- Título, hora de proyección, información de precios, etc.
- Una proyección representa el evento real en el que la audiencia ve la película.
- Fecha, hora y número de proyección
- Las personas reservan películas, pero en realidad deberían reservar una proyecciónde una película.
- Condiciones de descuento
- Si el precio está sujeto a descuento
- Condiciones de orden: utilizar el número de proyección para determinar si hay un descuento
- Condiciones de tiempo: utilizar la hora de inicio de la proyección para determinar si hay un descuento
- Políticas de descuento
- Determinar el precio con descuento
- Política de descuento de cantidad: descuento de una cantidad fija del precio de reserva
- Política de descuento de porcentaje: descuento de un porcentaje fijo del precio original
- Cada película puede tener una política de descuento asignada o ninguna, y las condiciones de descuento pueden combinarse.
- Si se aplica una política de descuento pero no se cumplen las condiciones de descuento, o si no se aplica una política de descuento, el precio no se reduce.
Hacia la programación orientada a objetos
Colaboración, objetos y clases
- La orientación a objetos se centra en los objetos.
- No se debe pensar en qué atributos y métodos se necesitan para una clase después de que se haya determinado la clase.
- Se debe determinar qué objetos tienen qué estado y comportamiento.
- Los objetos no deben considerarse entidades independientes, sino como miembros de una comunidad colaborativa.
Estructura del programa que sigue la estructura del dominio
- El dominio es el área en la que el usuario utiliza el programa para resolver un problema.
- Por lo general, los nombres de las clases deben ser los mismos o al menos similares a los nombres de los conceptos de dominio correspondientes.
- La relación entre las clases también debe ser lo más similar posible a la relación establecida entre los conceptos de dominio para que la estructura del programa sea fácil de entender y predecir.
Implementar clases
- Una clase se divide en interno y externo, y para diseñar una buena clase, se debe decidir qué parte se expondrá al exterior y qué parte se ocultará.
- Los atributos de los objetos se mantienen privados y los métodos necesarios para cambiar el estado interno se abren públicamente.
- La separación interna y externa de una clase garantiza la autonomía de los objetos, proporcionando a los programadores la libertad de implementación.
Objetos autónomos
- Los objetos deben ser objetos autónomos que tengan estado y comportamiento.
- La encapsulación es la unión de datos y funciones dentro de un objeto.
- El control de acceso debe reducir la interferencia externa para que los objetos puedan determinar sus propias acciones.
- El principio de separación entre interfaz e implementación es un principio fundamental que se debe seguir en la programación orientada a objetos.
- Interfaz pública: la parte accesible desde el exterior
- Implementación: la parte accesible solo internamente
Libertad del programador
- El rol del programador se divide en creador de clases y programador de clientes.
- El creador de clases agrega un nuevo tipo de datos.
- El programador de clientes usa el tipo de datos agregado por el creador de clases.
- Ocultar la implementación
- El creador de clases puede ocultar la implementación interna al proporcionar solo las partes necesarias al programador de clientes.
- El programador de clientes solo necesita conocer la interfaz, por lo que puede reducir la cantidad de conocimiento.
Una comunidad de objetos que colaboran
- Al expresar dinero, es mejor empaquetarlo como un objeto como Money en lugar de simplemente declarar una variable de tipo Long. Usar objetos mejora la comunicación de significado y permite que los cálculos repetidos se realicen en un solo lugar.
- La interacción que ocurre entre los objetos para implementar una función del sistema se llama colaboración.
Una breve historia sobre la colaboración
- La única forma en que un objeto puede interactuar con otro objeto es enviando o recibiendo mensajes.
- El método se llama el método para manejar el mensaje recibido.
- Es importante diferenciar entre los mensajes y los métodos, y de aquí nace el concepto de polimorfismo.
Obtener el precio con descuento
Empezar a colaborar para calcular el precio con descuento
- La clase Movie no contiene la lógica detallada sobre la política de descuento, sino que se delega a la interfaz DiscountPolicy. Es muy importante utilizar la herencia, el polimorfismo y la abstracción.
Política de descuento y condiciones de descuento
Herencia y polimorfismo
Dependencia en tiempo de compilación y dependencia en tiempo de ejecución
- Las dependencias del código y las dependencias en tiempo de ejecución pueden diferir.
- Cuanto mayor sea la diferencia entre las dos, más difícil será comprender el código, pero más flexible y extensible será el código.
Programación por diferencia
- La herencia permite agregar nuevas clases de forma fácil y rápida basándose en clases existentes, y se puede reutilizar la implementación de la clase padre.
- El método de crear una nueva clase agregando solo las partes que son diferentes de la clase padre se llama programación por diferencia.
Herencia e interfaz
- La herencia hace que la clase hija herede todas las interfaces proporcionadas por la clase padre.
- La interfaz define la lista de mensajes que un objeto puede entender.
public class Movie {
public Money calculateMovieFee(Screening screening) {
return fee.minus(discountPolicy.calculateDiscountAmount(screening));
}
- Movie está enviando un mensaje a DiscountPolicy para calcular el monto del descuento. Desde el punto de vista de Movie, no importa de qué clase sea la instancia que responde, siempre y cuando la respuesta sea exitosa.
- Por lo tanto, tanto AmountDiscountPolicy como PercentDiscountPolicy pueden colaborar con Movie en lugar de DiscountPolicy.
- Este tipo de sustitución de clases hijas por clases padre se llama conversión ascendente. Esto se debe a que parece que la clase hija se convierte automáticamente al tipo de la clase padre.
Polimorfismo
- El polimorfismo es la capacidad de responder de manera diferente según el tipo de objeto cuando se recibe el mismo mensaje.
- Movie envía el mismo mensaje, pero qué método se ejecutará realmente depende de qué clase sea el objeto que recibe el mensaje.
- El polimorfismo se basa en el hecho de que las dependencias del código y las dependencias en tiempo de ejecución pueden diferir.
- El polimorfismo determina el método que se ejecutará en tiempo de ejecución, por lo que se llama enlace diferido o enlace dinámico.
Interfaz y polimorfismo
- Si no necesitas compartir la implementación y solo quieres compartir la interfaz, puedes usar una interfaz en lugar de una clase abstracta.
Abstracción y flexibilidad
El poder de la abstracción
- Ventajas de la abstracción
- Al observar solo el nivel de abstracción, la política de requisitos se puede describir a un alto nivel.
- El diseño se vuelve más flexible.
Diseño flexible
- La abstracción evita que el diseño se combine con situaciones específicas, por lo que se puede crear un diseño flexible.
Compromiso entre clases abstractas e interfaces
public abstract class DiscountPolicy {
private List conditions;
public DiscountPolicy(DiscountCondition... conditions) {
this.conditions = Arrays.asList(conditions);
}
public Money calculateDiscountAmount(Screening screening) {
for (DiscountCondition condition : conditions) {
if (condition.isSatisfiedBy(screening)) {
return getDiscountAmount(screening);
}
}
return Money.ZERO;
}
abstract protected Money getDiscountAmount(Screening screening);
}
public class NoneDiscountPolicy extends DiscountPolicy {
@Override
protected Money getDiscountAmount(Screening screening) {
return Money.ZERO;
}
- En este momento, NoneDiscountPolicy en realidad no tiene ningún problema porque el método calculateDiscountAmount() de DiscountPolicy devuelve 0 si no hay condiciones de descuento. Sin embargo, desde el punto de vista del usuario, se debe agregar una política None que no tenga política de descuento como argumento de Movie, por lo que se agregó.
- Por lo tanto, la clase NoneDiscountPolicy es confusa conceptualmente, por lo que es mejor modificarla de la siguiente manera para que sea más fácil de entender.
public interface DiscountPolicy {
Money calculdateDiscountAmount(Screening screening);
}
public abstract class DefaultDiscountPolicy implements DiscountPolicy {
private List conditions;
public DefaultDiscountPolicy(DiscountCondition... conditions) {
this.conditions = Arrays.asList(conditions);
}
public Money calculateDiscountAmount(Screening screening) {
for (DiscountCondition condition : conditions) {
if (condition.isSatisfiedBy(screening)) {
return getDiscountAmount(screening);
}
}
return Money.ZERO;
}
abstract protected Money getDiscountAmount(Screening screening);
}
public class NoneDiscountPolicy implements DiscountPolicy {
@Override
public Money calculateDiscountAmount(Screening screening) {
return Money.ZERO;
}
}
public class PercentDiscountPolicy extends DefaultDiscountPolicy {
private double percent;
public PercentDiscountPolicy(double percent, DiscountCondition... conditions) {
super(conditions);
this.percent = percent;
}
@Override
protected Money getDiscountAmount(Screening screening) {
return screening.getMovieFee().times(percent);
}
}
public class AmountDiscountPolicy extends DefaultDiscountPolicy {
private Money discountAmount;
public AmountDiscountPolicy(Money discountAmount, DiscountCondition... conditions) {
super(conditions);
this.discountAmount = discountAmount;
}
@Override
protected Money getDiscountAmount(Screening screening) {
return discountAmount;
}
- Como se muestra arriba, DiscountPolicy se abstrae como una interfaz y se divide en su implementación, NoneDiscountPolicy, y DefaultDiscountPolicy, que significa una política de descuento general.
- También se puede pensar que agregar una interfaz solo para NoneDiscountPolicy es ineficiente en comparación con el aumento de la complejidad.
- Debes comprender que todo lo relacionado con la implementación está sujeto a compensaciones de diseño. En otras palabras, todo el código que escribas debe tener una buena razón.
Reutilización de código
- La herencia se puede usar para reutilizar el código.
- Sin embargo, es mejor usar la composición que la herencia para la reutilización del código.
- La forma en que Movie reutiliza el código de DiscountPolicy en el código actual es la composición.
- Si Movie es una clase superior y se divide en AmountDiscountMovie y PercentDiscountMovie, esto se consideraría el uso de herencia.
Herencia
- Desventajas de la herencia
- Viola la encapsulación.
- Debes conocer bien la estructura interna de la clase padre.
- Es más probable que la clase hija deba cambiarse cuando se cambia la clase padre.
- Hace que el diseño sea menos flexible.
- La relación entre la clase padre y la clase hija se determina en tiempo de compilación.
- Es imposible cambiar el tipo de objeto en tiempo de ejecución.
- Sin embargo, la composición permite cambiar las instancias en tiempo de ejecución a través de métodos como changeDiscountPolicy() en el lado del objeto que utiliza.
- Viola la encapsulación.
Composición
- La composición es una forma de reutilizar el código solo a través de los mensajes definidos en la interfaz.
- Se puede implementar la encapsulación y se puede mantener un acoplamiento flexible.
- Si se utiliza una interfaz para el polimorfismo, es necesario utilizar una combinación de herencia y composición.
- Por ejemplo, la interfaz DiscountPolicy debe usar la herencia para implementar las implementaciones secundarias.
Fuente
- Objetos