Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Poprawny kod

Speednet
October 17, 2018

Poprawny kod

Author: Łukasz Sosnowski

Czy każdy kod jest poprawny? Co to znaczy "poprawny kod"?
Na to i inne pytania postaram się odpowiedzieć opierając się na własnym doświadczeniu oraz pewnych metodykach.

Speednet

October 17, 2018
Tweet

Other Decks in Technology

Transcript

  1. Dobry edytor • Ostrzega nas przed błędami • Wskazuje źródła

    potencjalnych problemów • Ułatwia pisanie kodu i refaktoring
  2. Trochę Historii • Dekadę temu ekosystem PHP wyglądał inaczej •

    Luty 2008 inicjajatywa goPHP5 – dużo opensourcowych projektów przestaje wspierać wersję 4 • W 2009 zawiązuje PHP Standard Group, który następnie zmienia nazwę na PHP-FIG – Framework Interoperability Group
  3. PSR-2 Coding Style Guide • Określa zasady stosowania klamr •

    Deklarowania widoczności final public static, czy static final public, a może public final static, • Gdzie i kiedy używać spacji w kodzie • Jak powinny wyglądać struktury kontrolne • Ciekawostka – dopuszczony jest case z przelotem
  4. switch ($expr) { case 0: echo 'First case, with a

    break'; break; case 1: echo 'Second case, which falls through'; // no break case 2: case 3: case 4: echo 'Third case, return instead of break'; return; default: echo 'Default case'; break; }
  5. <?php namespace Awesome\Framework; use Awesome\Framework\Request; use Awesome\Framework\Stuff\Counter; class Response extends

    Core { const TYPE_FIRST = 1; const TYPE_LAST = 0; private $_request; private $_counter; function __construct(Request $request) { $this->_counter = $request->getCounter(); $this->_request = $request; } function determineIsOdd(): bool { if ($this->counter->count()==0 return 0; elseif ($this->counter->count()%2!=0) return TRUE; else return FALSE; } function processStuff ( ...$args ) { if (count($args)==3) $this->request->conform( $args[0],$args[1],$args[2] ); if (count($args)==2) { $this->request->conform($args[0],$args[1]); } } } <?php declare(strict_types=1); namespace Awesome\Framework; use Awesome\Framework\Request; use Awesome\Framework\Stuff\Counter; class Response extends Core { protected const TYPE_FIRST = 1; protected const TYPE_LAST = 0; /** @var Request */ private $request; /** @var Counter */ private $counter; function __construct(Request $request) { $this->counter = $request->getCounter(); $this->request = $request; } function determineIsOdd(): bool { if ($this->counter->count() == 0) { return 0; } elseif ($this->counter->count() % 2 != 0) { return true; } else { return false; } } function processStuff(...$args) { if (count($args) == 3) { $this->request->conform( $args[0], $args[1], $args[2]); } elseif (count($args) == 2) { $this->request->conform($args[0], $args[1]); } } }
  6. • Menedżer pakietów z prawdziwego zdarzenia • Automatyczne pobieranie pakietów

    zależnych • Współpracuje z większością frameworków • Łatwa aktualizacja pakietów
  7. /** * @throws InvalidArgumentException */ private static function guardAgainstWrongData(array $data)

    { if (!isset($data['_payload'])) { throw new InvalidArgumentException("Payload not found"); } if (!isset($data['_headers'])) { throw new InvalidArgumentException("Headers not defined"); } if (!isset($data['_headers']['target'])) { throw new InvalidArgumentException("Missing target header"); } if (!is_array($data['_headers']['target']) || count($data['_headers']['target']) !== 2) { throw new InvalidArgumentException("Wrong target"); } }
  8. /** * @throws InvalidArgumentException */ private static function guardAgainstWrongData(array $data)

    { try { Assertion::keyExists($data, '_payload'); Assertion::keyExists($data, '_headers'); Assertion::keyExists($data['_headers'], 'target'); Assertion::isArray($data['_headers']['target']); Assertion::count($data['_headers']['target'], 2); } catch (AssertionFailedException $exception) { throw InvalidArgumentException::fromException($exception); } } Assert
  9. Zadanie 1 Funkcja przyjmuje 3 parametry – ulicę, miasto kod

    pocztowy (wszystkie opcjonalne). Należy je sformatować w następujący sposób: Ulica, Miasto Kod
  10. function address(?string $street, ?string $city, ?string $zip): string { $fullAddress

    = !empty($street) ? $street : ''; if (empty($city) && empty($zip)) { return $fullAddress; } if (!empty($fullAddress)) { $fullAddress .= ', '; } if (!empty($zip)) { if (!empty($city)) { $fullAddress .= $city . ' '; } $fullAddress .= $zip; } else { $fullAddress .= $city; } return $fullAddress; }
  11. function address2(?string $street, ?string $city, ?string $zip): string { $zipPart

    = implode(' ', array_filter([$city, $zip])); return implode(', ', array_filter([$street, $zipPart])); }
  12. function address3(?string $street, ?string $city, ?string $zip): string { if

    (!array_filter([$street, $city, $zip])) { return ''; } if (!array_filter([$city, $zip])) { return $street; } if (!$street) { return trim("$city $zip"); } return $street.', '.trim("$city $zip"); }
  13. function address4(?string $street, ?string $city, ?string $zip): string { return

    trim( preg_replace( '/\s+/', ' ', sprintf('%s, %s %s', $street, $city, $zip) ), ', ' ); }
  14. • Liczy ilość niezależnych ścieżek przebiegu • Obliczanie: liczba punktów

    decyzyjnych + 1 • Maksymalna liczba testów niezbędnych do przetestowania wszystkich wariantów kodu.
  15. function address(?string $street, ?string $city, ?string $zip): string { $fullAddress

    = !empty($street) ? $street : ''; if (empty($city) && empty($zip)) { return $fullAddress; } if (!empty($fullAddress)) { $fullAddress .= ', '; } if (!empty($zip)) { if (!empty($city)) { $fullAddress .= $city . ' '; } $fullAddress .= $zip; } else { $fullAddress .= $city; } return $fullAddress; } 1 1 1 1 1 1 6
  16. Złożoność Halsteada • Objętość • Trudność napisania kodu, a także

    zrozumienia • Wysiłek niezbędny to utrzymania programu • Szacunkową liczbę błędów • Czas niezbędny do napisania
  17. Czy prosty kod jest bardziej poprawny? Zazwyczaj… tak! Czy prostszy

    kod działa szybciej? Zazwyczaj... nie ;( Pytania
  18. function address(?string $street, ?string $city, ?string $zip): string { $fullAddress

    = !empty($street) ? $street : ''; if (empty($city) && empty($zip)) { return $fullAddress; } if (!empty($fullAddress)) { $fullAddress .= ', '; } if (!empty($zip)) { if (!empty($city)) { $fullAddress .= $city . ' '; } $fullAddress .= $zip; } else { $fullAddress .= $city; } return $fullAddress; } Cycl. Obj. Trud. Write Bugs Czas 6 329 24 7 min 0,11 0,35µs
  19. function address2(?string $street, ?string $city, ?string $zip): string { $zipPart

    = implode(' ', array_filter([$city, $zip])); return implode(', ', array_filter([$street, $zipPart])); } Cycl. Obj. Trud. Write Bugs Czas 6 329 24 7 min 0,11 0,35µs 1 119 9 1 min 0,04 0,53µs
  20. function address3(?string $street, ?string $city, ?string $zip): string { if

    (!array_filter([$street, $city, $zip])) { return ''; } if (!array_filter([$city, $zip])) { return $street; } if (!$street) { return trim("$city $zip"); } return $street.', '.trim("$city $zip"); } Cycl. Obj. Trud. Write Bugs Czas 6 329 24 7 min 0,11 0,35µs 1 119 9 1 min 0,04 0,53µs 4 225 16 3 min 0,08 0,60µs
  21. function address4(?string $street, ?string $city, ?string $zip): string { return

    trim( preg_replace( '/\s+/', ' ', sprintf('%s, %s %s', $street, $city, $zip) ), ', ' ); } Cycl. Obj. Trud. Write Bugs Czas 6 329 24 7 min 0,11 0,35µs 1 119 9 1 min 0,04 0,53µs 4 225 16 3 min 0,08 0,60µs 1 96 6 36 s 0,03 0,54µs
  22. function address5(?string $street, ?string $city, ?string $zip): string { return

    trim( $street . ", ". trim($city . " " . $zip), ", " ); } Cycl. Obj. Trud. Write Bugs Czas 6 329 24 7 min 0,11 0,35µs 1 119 9 1 min 0,04 0,53µs 4 225 16 3 min 0,08 0,60µs 1 96 6 36 s 0,03 0,54µs 1 95 8 40 s 0,03 0,28µs
  23. Zadanie 2 Napisać funkcję która konwertuje nazwę zmiennej zapisanej w

    składni snake_case na camelCase Wejście some_random_fancy_string Wyjście someRandomFancyString
  24. //wariant 1 function snakeCaseToCamelCase(string $snakeCaseString): string { $camelCaseString = '';

    $upperThis = false; for($i = 0, $strlen = strlen($snakeCaseString); $i < $strlen; $i++) { $char = $snakeCaseString[$i]; if ($char === '_') { $upperThis = true; continue; } if ($upperThis) { $char = strtoupper($char); $upperThis = false; } $camelCaseString .= $char; } return $camelCaseString; } Cycl. Obj. Trud. Write Bugs Czas 4 304 27 8 min 0,1 0,44µs
  25. //wariant 2 function snakeCaseToCamelCase2(string $snakeCase): string { $isLetterToUpperNow = false;

    $returnString = $snakeCase; $length = strlen($snakeCase); $returnStringIndex = 0; for ($i = 0; $i < $length; $i++) { $returnString[$i] = ' '; if ($isLetterToUpperNow) { $returnString[$returnStringIndex++] = strtoupper($snakeCase[$i]); $isLetterToUpperNow = false; } else { $isLetterToUpperNow = $snakeCase[$i] === '_'; if (!$isLetterToUpperNow) { $returnString[$returnStringIndex++] = $snakeCase[$i]; } } } return trim($returnString); } Cycl. Obj. Trud. Write Bugs Czas 4 304 27 8 min 0,1 0,44µs 4 418 38 14 min 0,14 0,54µs
  26. //wariant 3 function snakeCaseToCamelCase3(string $snakeCase): string { $returnStringIndex = 0;

    $dashes = 0; $i = 0; $length = strlen($snakeCase); while ($i < $length) { if ($snakeCase[$i] === '_') { $dashes++; $snakeCase[$returnStringIndex++] = strtoupper($snakeCase[++$i]); } else { $snakeCase[$returnStringIndex++] = $snakeCase[$i]; } $i++; } return substr($snakeCase, 0 , $length - $dashes); } Cycl. Obj. Trud. Write Bugs Czas 4 304 27 8 min 0,1 0,44µs 4 418 38 14 min 0,14 0,54µs 3 357 38 12 min 0,12 0,40µs
  27. //wariant 4 function snakeCaseToCamelCase4(string $snakeCaseString): string { $expl = explode('_',

    $snakeCaseString); $first = array_shift($expl); $expl = array_map('ucfirst', $expl); array_unshift($expl, $first); return implode($expl); } Cycl. Obj. Trud. Write Bugs Czas 4 304 27 8 min 0,1 0,44µs 4 418 38 14 min 0,14 0,54µs 3 357 38 12 min 0,12 0,40µs 1 139 18 2 min 0,05 0,21µs
  28. Zadanie 3 Wczytać i przekształcić do tablicy plik csv w

    formacie: KOD_PRODUKTU;ROZMIAR1;CENA_BAZOWA;CENA_MM;ROZMIAR2;CENA_BAZOWA2;CENA_MM2… Tablica wynikowa powinna posiadać następujący format: [ 'kod' => [ 'rozmiar1' => ['cena_bazowa', 'cena_mm'], 'rozmiar2' => ['cena_bazowa', 'cena_mm'] ], 'kod2' => [ 'rozmiar1' => ['cena_bazowa', 'cena_mm'], 'rozmiar2' => ['cena_bazowa', 'cena_mm'] ] ]
  29. //wariant 1 function loadPrices1(string $path) { $prices = []; if

    (false !== $handle = fopen($path, 'r')) { while (false !== $data = fgetcsv($handle, 200, ';')) { $type = array_shift($data); $c = count($data); for ($i = 0; $i < $c; $i = $i + 3) { $prices[$type][$data[$i]] = [$data[$i+1], $data[$i+2]]; } } } return $prices; } Cycl. Obj. Trud. Write Bugs Czas 4 389 26 9 min 0,04 1,68µs
  30. //wariant 3 function handleFile (string $name) { $result = [];

    $handle = @fopen($name, "r"); if ($handle) { while (($line = fgets($handle)) !== false) { handleLine($line, $result); } if (!feof($handle)) { echo "Error: unexpected fgets() fail\n"; } fclose($handle); } return $result; } function findPart ($haystag, $haystagLen, $delimiter, &$currentIndex) { $start = $currentIndex; while ($currentIndex < $haystagLen) { if ($haystag[$currentIndex] === $delimiter) { break; } else { $currentIndex += 1; } } return substr($haystag, $start, $currentIndex - $start); } function handleLine (string $line, array &$resultArray) { $lineLength = strlen($line); $delimiter = ';'; $lineIndex = 0; $headerPart = findPart($line, $lineLength, $delimiter, $lineIndex); $lineIndex +=1; $partNumber = 0; $tempParts = [null, null, null]; $aggregatedParts = []; while ($lineIndex < $lineLength) { $part = findPart($line, $lineLength, $delimiter, $lineIndex); $lineIndex +=1; $tempParts[$partNumber % 3] = $part; $partNumber++; if ($partNumber % 3 === 0) { $aggregatedParts[$tempParts[0]] = [$tempParts[1], $tempParts[2]]; $tempParts = [null, null, null]; } } $resultArray[$headerPart] = $aggregatedParts; } Cycl. Obj. Trud. Write Bugs Czas 4 389 26 9 min 0,04 1,68µs 10 1236 63 72 min 0,4 1,56µs
  31. //wariant 2 function loadPrices2(string $path) { $prices = []; foreach

    (file($path) as $line) { $entries = explode(';', trim($line)); $code = array_shift($entries); foreach (array_chunk($entries, 3) as [$size, $base, $mm]) { $prices[$code][$size] = [$base, $mm]; } } return $prices; } Cycl. Obj. Trud. Write Bugs Czas 4 389 26 9 min 0,04 1,68µs 10 1236 63 72 min 0,4 1,56µs 1 236 14 3 min 0,07 0,47µs