Ten jeden trik znacząco upraszcza mój kod!

Czy zastanawiało Cię, jak poprawić czytelność swojego kodu i uczynić go prostszym? Właśnie znalazłeś się w odpowiednim miejscu! W dzisiejszym artykule zobaczysz technikę zwaną “early return” i dowiesz się jak może ona sprawić, że kod będzie wyglądał i działał lepiej.

Dlaczego więc warto używać early return?

Kod jest wyraźniejszy

Spójrz na ten kod:

return (
  <GridLayout>
    <Header>Hello, {user.name}</Header>
    {vipAccount ? (
      <VipUser
        user={user}
        someProp={someProp}
        someProp2={someProp2}
      />
    ) : (
      <RegularUser
        user={user}
        someProp={someProp}
        someProp2={someProp2}
      />
    )}
  </GridLayout>
);

Moim zdaniem tworzy się tu lekki natłok 🙂 Twoim zdaniem też?

Early return pozwoli go rozplątać:

if (vipAccount) {
  return (
    <GridLayout>
      <Header>Hello, {user.name}</Header>
      <VipUser
          user={user}
          someProp={someProp}
          someProp2={someProp2}
        />
    </GridLayout>
  );
}

return (
  <GridLayout>
    <Header>Hello, {user.name}</Header>
    <RegularUser
        user={user}
        someProp={someProp}
        someProp2={someProp2}
      />
  </GridLayout>
);

Taki kod lepiej Ci będzie czytać 🙂 A jeśli razi Cię duplikacja komponentów odpowiedzialnych za układ strony, możesz wyekstrahować układ do osobnego komponentu:

const AccountLayout = ({ name, children }) => {
  return (
    <GridLayout>
      <Header>Hello, {name}</Header>
      {children}
    </GridLayout>
  );
};

if (vipAccount) {
  return (
    <AccountLayout name={user.name}>
      <VipUser
          user={user}
          someProp={someProp}
          someProp2={someProp2}
        />
    </AccountLayout>
  );
}

return (
  <AccountLayout name={user.name}>
    <RegularUser
        user={user}
        someProp={someProp}
        someProp2={someProp2}
      />
  </AccountLayout>
);

Różnicę pomiędzy obecnością early return a jej brakiem widać gołym okiem. A gdyby komponenty miały np więcej propsów uderzyłoby Cię to po oczach jeszcze bardziej.

Znikają nadmiarowe zmienne

Wyobraź sobie taki kod:

function getDiscount(price, isPremium) {
  let discount = 0;
  if (isPremium) {
    discount = price * 0.2;
  } else {
    discount = price * 0.1;
  }
  return discount;
}

W powyższym przykładzie funkcja getDiscount zwraca zniżkę w zależności od ceny produktu i czy klient jest premium. Jednakże, kod można uprościć i zastosować technikę early return, aby uniknąć użycia zmiennej discount.

function getDiscount(price, isPremium) {
  if (isPremium) {
    return price * 0.2;
  }
  return price * 0.1;
}

W tym przypadku zastosowaliśmy early return, aby zwrócić wynik bez przypisywania go do zmiennej discount. Dzięki temu kod jest krótszy i łatwiejszy do czytania.

Znikają zagnieżdżenia

Kod który jest bardziej płaski jest też bardziej czytelny.

Często za dodatkowe zagnieżdżenia odpowiada programowanie defensywne, gdy jednocześnie nie stosujemy podejścia fail fast (np nie rzucamy wyjątków bądź nie stosujemy early return):

const processPayment = (user, payment) => {
  if (user) {
    if (payment) {
      if (payment.isValid()) {
        // process payment
      } else {
        // notify user about invalid payment
      }
    } else {
      // notify user about expired payment
    }
  } else {
    // Logout user cause session expired
  }
}

Jeśli wyobrazimy sobie, że każda z opcji ma kilkanaście linii kodu wewnątrz, może pojawić się kilka problemów.

Na przykład ciężko będzie od razu zorientować się, jaki warunek doprowadził nas do danej linii kodu.

Zastosowanie early return zlikwiduje te problemy:

const processPayment = (user, payment) => {
  if (!user) {
    // Logout user cause session expired
    return;
  }

  if (!payment) {
    // Notify user about expired payment
    return;
  }

  if (!payment.isValid()) {
    // notify user about invalid payment
    return;
  }

  // Process payment
}

Co w sytuacji, gdy jednak zagnieżdżone ify musiałyby pozostać w kodzie?
Bo np obsługa braku payment jest inna gdy jest user / nie ma go?

Nadal early return pozwoli oddzielić obsługę problemów od faktycznej obsługi płatności:

const processPayment = (user, payment) => {
  if (user) {
    if (!payment) {
      // Notify user about expired payment
      return;
    }

    if (!payment.isValid()) {
      // notify user about invalid payment
      return;
    }
  }
  else {
    if (payment) {
      // notify security, looks like a hack
      return;
    }
    else {
      // session expired, logout user
    }
  }

  // Process payment
}

Takie podejście pozwoli jasno oddzielić obsługę problemów od faktycznej obsługi płatności.

Podsumowanie

Podsumowując, jeśli szukasz sposobu na uproszczenie kodu early return świetnie wywiąże się z tego zadania.

Wypróbuj early return u siebie a polubisz to podejście 🙂 Znikną niepotrzebne zmienne i zagnieżdżenia a kod stanie się czytelniejszy.