Botón para abrir el Menú Botón para cerrar el Menú
Logo da empresa Alura
Iniciar Sesión Nuestros Planes
Formaciones Conoce a Luri
  • Programación _
  • Front End _
  • Data Science _
  • DevOps _
  • Innovación y Gestión _
Artículos de Tecnología

Clean Code: que es, casos de Uso, ejemplo de código limpio

Clean Code: que es, casos de Uso, ejemplo de código limpio
Fernando Furtado
Fernando Furtado
08/09/2023

Compartir

Muchos desarrolladores ya han oído hablar sobre Clean Code, o Código Limpio, y cuando hablamos sobre eso, es común asociarlo con un código de fácil mantenimiento.

Pero, ¿será que Clean Code se refiere solo a un código fácil de mantener?

¿Desing de Codigo y el Clean Code?

Si alguna vez has tenido la experiencia de tener que agregar algo relativamente simple en un código existente, y percibiste que esta "agregación simple" impactaría en varios puntos del proyecto, sabes lo que es un código difícil de mantener. Los sistemas heredados no son precisamente el "sueño de un desarrollador" y hay toda una habilidad para lidiar con códigos de software como este, que puede requerir mucho trabajo. Pero no te preocupes, aquí hay una guía para lidiar con softwares heredados. Todo lo que necesitas saber está aquí :)

Sin embargo, si nunca has vivenciado esto, imagina tener que alterar un fragmento de código y darte cuenta de que este cambio simplemente romperá todo el sistema. Definitivamente no sería genial.

Y pasar por esto, hace que pensemos que escribir un código totalmente nuevo es una tarea mucho más simple que hacer mantenimiento en código existente.

Pero, desafortunadamente, en nuestra carrera dedicamos mucho tiempo a mantener el código existente y, si no pensamos adecuadamente en el código que escribimos, vamos a pasar varias veces por situaciones similares a esta.

Recuerda siempre que todo código que escribimos a lo largo del tiempo se convierte en un Pasivo para la empresa. Y cuanto menos nos preocupemos por el mantenimiento del código, mayor es el valor de ese Pasivo.

¿Qué es un código con fácil mantenimiento?

Cuando hablamos de un código con fácil mantenimiento, nos referimos a un código con Bajo Acoplamiento, Alta Cohesión, utilizando [SOLID](https://kata-software.com/es/publicaciones/principios-solid-en-programacion#:~:text=SOLID es un acrónimo acuñado,eficiente y fácil de mantener.), Objetos inmutables (cuando tiene sentido), aplicando Patrones de Diseño, minimizando Efectos Secundarios, maximizando el uso de Funciones Puras y varias otras cosas.

Todo esto puede ser resumido en tener un buen Desing de Codigo, una parte muy importante para tener código limpio

¿Y qué es lo que mi código necesita tener para ser considerado un código limpio?

Más allá del mantenimiento

Deténgase 1 minuto a revisar este código y intente responder : ¿que hace el?

@Service
public class MovieSessionService {

  private MovieSessionRepository sessionRepository;
  private UnavailabilityRepository unavailabilityRepository;
  private Converter<MovieSessionDTO, MovieSession> converter;

  public MovieSessionService(MovieSessionRepository sessionRepository, UnavailabilityRepository unavailabilityRepository, Converter<MovieSessionDTO, MovieSession> converter) {
      this.sessionRepository = sessionRepository;
      this.unavailabilityRepository = unavailabilityRepository;
      this.converter = converter;
  }

  public Result<MovieSession> create(MovieSessionDTO dto) {

      MovieSession session = converter.convert(dto);

      List<MovieSession> sessions = sessionRepository.listAllByTheaterId(dto.getTheaterId());

      if (sessions.stream().anyMatch(s -> s.getStart().equals(session.getStart()) && s.getEnd().equals(session.getEnd()))) {
          return Result.fail(SessionConflictException.class, session);
      }

      if (sessions.stream().anyMatch(s -> session.getStart().isBefore(s.getStart()) || session.getStart().isAfter(s.getEnd()))) {
          return Result.fail(SessionConflictException.class, session);
      }

      List<Unavailability> unavailabilities = unavailabilityRepository.listAllByTheaterId(dto.getTheaterId());

      if (unavailabilities.stream().anyMatch(u -> u.getStart().equals(session.getStart()) && u.getEnd().equals(session.getEnd()))) {
          return Result.fail(UnavailablePeriodException.class, session);
      }

      if (unavailabilities.stream().anyMatch(u -> session.getStart().isBefore(u.getStart()) || session.getStart().isAfter(u.getEnd()))) {
          return Result.fail(UnavailablePeriodException.class, session);
      }

      sessionRepository.save(session);

      return Result.success(session);
  }
}

¿Qué piensas de esta secuencia de if? ¿Y este montón de expresiones que se evalúan dentro de cada if? ¿Cómo podríamos reducir la cantidad de código duplicado?

Te das cuenta que hicimos un esfuerzo muy grande para intentar entender lo que hace este código, y es posible que aún no lo hayamos comprendido.

Este código tiene como función guardar una sesión de cine, siempre y cuando la sesión que estamos intentando guardar no tenga conflictos con otras sesiones existentes ni con una posible indisponibilidad en la sala (por ejemplo, la sala está indisponible por mantenimiento).

Toda esa carga cognitiva que hicimos para tratar de comprender el código resultó en un cansancio físico y mental. Ahora observa que pasamos la mayor parte del tiempo leyendo código.

Así que la legibilidad cuenta mucho cuando estamos escribiendo código.

Por lo tanto, podríamos refactorizar el código a algo como:

@Service
public class MovieSessionService {

  private MovieSessionRepository sessionRepository;
  private UnavailabilityRepository unavailabilityRepository;
  private Converter<MovieSessionRequest, MovieSession> converter;

  public MovieSessionService(MovieSessionRepository sessionRepository, UnavailabilityRepository unavailabilityRepository, Converter<MovieSessionRequest, MovieSession> converter) {
      this.sessionRepository = sessionRepository;
      this.unavailabilityRepository = unavailabilityRepository;
      this.converter = converter;
  }

  public Result<MovieSession> createMovieSessionBy(MovieSessionRequest movieSessionRequest) {

      MovieSession newMovieSession = converter.convert(movieSessionRequest);

      Result<MovieSession> overlapResult = checkOverlapsWith(newMovieSession);

      if (overlapResult.isFail()) {
          return overlapResult;
      }

      sessionRepository.save(newMovieSession);

      return Result.success(newMovieSession);
  }

  private Result<MovieSession> checkOverlapsWith(MovieSession session) {

      if (hasOverlapsWithAnotherMovieSessionsBy(session)) {
          return Result.fail(SessionConflictException.class, session);
      }

      if (hasOverlapsWithUnavailabilitiesBy(session)) {
          return Result.fail(SessionConflictException.class, session);
      }

      return Result.success(session);
  }

  private boolean hasOverlapsWithAnotherMovieSessionsBy(MovieSession session) {
      List<MovieSession> sessions = sessionRepository.listAllByTheater(session.getTheater());

      return hasOverlapsBetween(sessions, session);

  }

  private boolean hasOverlapsWithUnavailabilitiesBy(MovieSession session) {
      List<Unavailability> unavailabilities = unavailabilityRepository.listAllByTheater(session.getTheater());

      return hasOverlapsBetween(unavailabilities, session);
  }

  private boolean hasOverlapsBetween(List<? extends Periodable> periods, MovieSession session) {
      LocalDateTime startTime = session.getStart();
      LocalDateTime endTime = session.getEnd();

      if (periods.stream().anyMatch(period -> period.getStart().equals(startTime) && period.getEnd().equals(endTime))) {
          return true;
      }

      return periods.stream().anyMatch(period -> startTime.isBefore(period.getStart()) || startTime.isAfter(period.getEnd()));
  }
}

El código ahora está más organizado, con algunos nombres mejorados para aumentar la semántica y sin tanta duplicidad. Además, podemos leerlo de arriba a abajo en un flujo continuo.

Podríamos continuar refactorizando el código infinitamente, trasladando las responsabilidades a las clases correctas y así mejorar aún más el Desing del Código y la mantenibilidad.

De esta forma tenemos mucho menos esfuerzo para leer y tratar de comprender el código.

Recuerda que la legibilidad cuenta mucho para un código limpio.

Pero, ¿cómo podemos garantizar que después de estos cambios nuestro código continuará funcionando?

Pruebas, pruebas y más pruebas

Sí, para garantizar que tu código continúe funcionando, necesitamos escribir pruebas.

Las pruebas hacen parte del juego cuando estamos desarrollando, y el hecho de tenerlas no elimina totalmente la posibilidad de tener un bug, pero minimiza mucho.

Con las pruebas conseguimos garantizar que los escenarios previstos están funcionando y extrapolar estos escenarios es lo que hace que nuestros tests sean más eficientes.

Cuantos más niveles de pruebas (unitarias, de integración, de aceptación, de regresión, etc.) tengamos, más seguridad tenemos al aplicar una refactorización.

La tarea más difícil cuando se escribe una prueba es saber qué debemos probar. Y es precisamente en eso en lo que debemos enfocar nuestro esfuerzo.

Las pruebas son una parte importante de todo el ciclo de vida de desarrollo y, sí, un código limpio es un código testable.

Ahora sí ¿que es un código limpio?

Un código limpio es la composición de diversas características, como:

  • Legible

    El código legible permite la identificación de puntos que necesitan mejorarse. Pasamos más tiempo leyendo código que escribiéndolo, por lo tanto, cuanto más fácil sea de leer el código, menos esfuerzo necesitamos para comprenderlo.

  • Testable

    Debemos probar nuestro código, ya que esto nos proporciona seguridad al alterarlo. Y garantiza que los escenarios que hemos previsto se ajusten a lo esperado.

  • Fácil de mantener

    Nuestro código debe permitir alteraciones tanto para agregar nuevas funcionalidades como para mejorar la legibilidad o mantenibilidad.

De manera muy concisa Un código limpio es un codigo testable, fácil de mantener y de leer.

En Alura, hemos creado una formación en Orientación a Objetos pensando justamente en esta buena práctica.

Fernando Furtado

Navegación

  • Planes
  • Instructores
  • Blog
  • Política de privacidad
  • Términos de uso
  • Sobre nosotros
  • Preguntas frecuentes

¡CONTÁCTANOS!

  • ¡Quiero entrar en contacto!

Blog

  • Programación
  • Data Science
  • Front End
  • Innovación y Gestión
  • DevOps

AOVS Sistemas de Informática S.A CNPJ 05.555.382/0001-33

SÍGUENOS EN NUESTRAS REDES SOCIALES

YouTube Facebook Instagram Linkedin Whatsapp Spotify

NOVEDADES Y LANZAMIENTOS

Aliados

  • Programa de aceleração Scale-Up Endeavor
  • En Alura somos unas de las Scale-Ups seleccionadas por Endeavor, programa de aceleración de las empresas que más crecen en el país.
  • Growth Academy 2021 do Google For Startups
  • Fuimos unas de las 7 startups seleccionadas por Google For Startups en participar del programa Growth Academy en 2021
Alura

Powered by

Caelum

AOVS Sistemas de Informática S.A CNPJ 05.555.382/0001-33

SÍGUENOS EN NUESTRAS REDES SOCIALES

YouTube Facebook Instagram Linkedin Whatsapp Spotify

Cursos

Cursos de Programación
Lógica de Programación | Java
Cursos de Front End
HTML y CSS | JavaScript | React
Cursos de Data Science
Data Science | Machine Learning | Excel | Base de Datos | Data Visualization | Estadística
Cursos de DevOps
Docker | Linux
Cursos de Innovación y Gestión
Transformación Ágil | Marketing Analytics

Alura

  • Educação em Tecnologia

    • logo fiap FIAP
    • logo casa do codigo Casa do Código
    • logo pm3 PM3 - Cursos de Produto
  • Mais Alura

    • logo alura start START BY Alura
    • logo alura lingua Alura Língua
    • logo alura para empresas Alura Para Empresas
    • logo alura latam Alura LATAM
  • Comunidade

    • logo tech guide Tech Guide
    • logo 7 days of code 7 days of code
    • logo Hipsters ponto Jobs Hipsters ponto Jobs
  • Podcasts

    • logo Hipster Network Hipster Network
    • logo Hipsters ponto Tech Hipsters ponto Tech
    • logo Dev sem fronteiras Dev sem Fronteiras
    • logo Like a Boss Like a Boss
    • logo IA Sob Controle IA Sob Controle
    • logo Mesa de Produto Mesa de Produto
    • logo Decode Decode
    • logo FIAPCast FIAPCast