Блог

Неліктен ерекшеліктерді шығару - ең жақсы таңдау?

Юрий Мельников

Қарапайым және түсінікті мысалдармен қысқаша талдау.

Ерекшеліктерді қолдану объективті түрде орынды болатын бірнеше мысалды қарастырайық.

Tip2Go жобасында орынның суретін жаңарту мысалын келтірейін.

PlaceService класын updatePlaceImage әдісімен жаза бастайық.

class PlaceService {
  public function updatePlaceImage(int $place_id, string $image_filename): string
  {
    $place = $this->getPlaceById($place_id);
    ...
  }
}

Қате пайда болу мүмкіндігіне біз орынның ID бойынша алынуында тап болдық, себебі жоқ ID берілуі мүмкін және орнына null қайтарылады.

Егер null қайтарылса, кодты орындауды жалғастырудың қажеті жоқ және суретті жаңартпай кері қайту керек. Бұл үшін updatePlaceImage әдісінің қайтарылатын типтерін өзгертіп, қате жағдайында null қайтару мүмкіндігін қосу керек.

class PlaceService {
  public function updatePlaceImage(int $place_id, string $image_filename): ?string
  {
    $place = $this->getPlaceById($place_id);

    if (!$place instanceof Place) {
      return null;
    }

    if (!file_exists($image_filename)) {
      return null;
    }

    $newImage = ImageHelper::resize($image_filename);

    if (!file_exists(self::IMAGES_DIR . '/' . $place->getId())) {
      $mkdir = mkdir(self::IMAGES_DIR . '/' . $place->getId());
    }

    if (!$mkdir) {
      return null;
    }
  }
}

Енді біз бастапқы файлдың және суреттерді сақтау директориясының бар-жоғын тексеруді қостық, егер ол жоқ болса, оны орынның ID-ін пайдаланып жасаймыз.

Бұл жағдайда біз бірден екі қате жағдайымен бетпе-бет келуіміз мүмкін: бастапқы файлдың жоқтығы және жаңа директорияны құру мүмкін еместігі. Қателер болған жағдайда, біз де null қайтарып, жаңа суретті орнатуды тоқтатамыз.

Жақсы, біздің әдісіміз қателерге байланысты үш жерде орындалуын тоқтата алады. Әрі қарай, уақытша файлды жаңа директорияға ауыстыру кезінде қателерді тексеруді қосуға болады және әдістің орындалуы тоқтайтын төрт нүкте пайда болады.

Ал біз не болғанын қалай түсінеміз? Бірінші және ең көрнекі нұсқа - updatePlaceImage әдісіне int қайтару мүмкіндігін қосу және онда қате кодын алу.

class PlaceService {
  public function updatePlaceImage(int $place_id, string $image_filename): string|int
  {
    $place = $this->getPlaceById($place_id);

    if (!$place instanceof Place) {
      return -1;
    }

    if (!file_exists($image_filename)) {
      return -2;
    }

    $newImage = ImageHelper::resize($image_filename);

    if (!file_exists(self::IMAGES_DIR . '/' . $place->getId())) {
      $mkdir = mkdir(self::IMAGES_DIR . '/' . $place->getId());
    }

    if (!$mkdir) {
      return -3;
    }
  }
}

Бұл жағдайда, шақырушы әдісте қайтарылған мәндердің барлық нұсқаларын тексеруіміз керек болады.

$placeService = new PlaceService();
$result = $placeService->updatePlaceImage(1, 'filename.jpg');

if (is_string($result)) {
  return 'Картинка обновлена';
} else {
  switch($result) {
    case -1:
      return 'Место с таким ID не найдено';
      break;
    case -2:
      return 'Исходная картинка не найдена';
      break;
    case -3:
      return 'Невозможно создать директорию с ID места';
      break;
  }
}

Енді тексерулер саны артқан кезде switch блогының қалай өсетінін елестетіп көріңіз? Ал егер әдіс int қайтаруы керек болса, қате қайтару типімен тағы бірдеңе ойлап табу керек пе? Жалпы, мұндай код тез оқылмайтын спагеттиге айналады.

Енді сол тексерулерді ерекшеліктерді пайдаланып көрейік.

class PlaceService {
  public function updatePlaceImage(int $place_id, string $image_filename): string
  {
    $place = $this->getPlaceById($place_id);

    if (!$place instanceof Place) {
      throw new Exception('Место с таким ID не найдено');
    }

    if (!file_exists($image_filename)) {
      throw new Exception('Исходная картинка не найдена');
    }

    $newImage = ImageHelper::resize($image_filename);

    if (!file_exists(self::IMAGES_DIR . '/' . $place->getId())) {
      $mkdir = mkdir(self::IMAGES_DIR . '/' . $place->getId());
    }

    if (!$mkdir) {
      throw new Exception('Невозможно создать директорию с ID места');
    }
  }
}

Бұл жағдайда, әдісті шақырғанда, біз тек бір ғана ерекшелікті тексереміз.

$placeService = new PlaceService();

try {
  $placeService->updatePlaceImage(1, 'filename.jpg');
} catch (Exception $exception) {
  return $exception->getMessage();
}

return 'Картинка обновлена';

Ерекшелікті шығарғанда қате мәтінін көрсеткендіктен, оны қайта жазудың қажеті жоқ, ол $exception->getMessage() шақырғанда қайтарылады.

Осылайша, біз кодты оқуға жеңіл әрі қайтарылатын мәнді тексеру кезінде мүмкін болатын қателерден сақтандырдық.

Әрине, ерекшеліктердің де кемшіліктері бар. Мысалы, ерекшелік оқиғасы болғанда, қажетті деректермен толтырылған сәйкес объект жасалады.

Иә, қазіргі заманғы жағдайларда бұл аз уақыт алады және оны елемеуге болады, бірақ сезімтал жүйелерде бұл ескерілуі керек.

Ерекшеліктерді қолдану керек, бірақ ақылмен!