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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
return (
<GridLayout>
<Header>Hello, {user.name}</Header>
{vipAccount ? (
<VipUser
user={user}
someProp={someProp}
someProp2={someProp2}
/>
) : (
<RegularUser
user={user}
someProp={someProp}
someProp2={someProp2}
/>
)}
</GridLayout>
);
return ( <GridLayout> <Header>Hello, {user.name}</Header> {vipAccount ? ( <VipUser user={user} someProp={someProp} someProp2={someProp2} /> ) : ( <RegularUser user={user} someProp={someProp} someProp2={someProp2} /> )} </GridLayout> );
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ć:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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>
);
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> );
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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>
);
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> );
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function getDiscount(price, isPremium) {
let discount = 0;
if (isPremium) {
discount = price * 0.2;
} else {
discount = price * 0.1;
}
return discount;
}
function getDiscount(price, isPremium) { let discount = 0; if (isPremium) { discount = price * 0.2; } else { discount = price * 0.1; } return discount; }
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.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function getDiscount(price, isPremium) {
if (isPremium) {
return price * 0.2;
}
return price * 0.1;
}
function getDiscount(price, isPremium) { if (isPremium) { return price * 0.2; } return price * 0.1; }
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):

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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
}
}
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 } }
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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
}
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 }
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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
}
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 }
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.