Skip to content

iAbbos/clean-code-php

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PHP Toza Kod

Mundarija

  1. Kirish
  2. O'zgaruvchilar
  3. Taqqsolash
  4. Funksiyalar
  5. Obyektlar va ma'lumotlar tuzilmasi
  6. Sinflar
  7. SOLID
  8. O'zingizni takrorlamang (Don’t repeat yourself, DRY)
  9. Tarjimalar

Kirish

Software engineering principles, from Robert C. Martin's book Clean Code, adapted for PHP. This is not a style guide. It's a guide to producing readable, reusable, and refactorable software in PHP.

Not every principle herein has to be strictly followed, and even fewer will be universally agreed upon. These are guidelines and nothing more, but they are ones codified over many years of collective experience by the authors of Clean Code.

Inspired from clean-code-javascript.

Although many developers still use PHP 5, most of the examples in this article only work with PHP 7.1+.

O'zgaruvchilar

Ma'noga ega va tushunarli nomlardan foydalaning

Yomon:

declare(strict_types=1);

$ymdstr = $moment->format('y-m-d');

Yaxshi:

declare(strict_types=1);

$currentDate = $moment->format('y-m-d');

⬆ boshiga qaytish

Bir xil turdagi o'zgaruvchilar uchun xuddi shunday nomdan foydalaning

Yomon:

declare(strict_types=1);

getUserInfo();
getUserData();
getUserRecord();
getUserProfile();

Yaxshi:

declare(strict_types=1);

getUser();

⬆ boshiga qaytish

Izlashga qulay bo'lgan nomlardan foydalaning (1-qism)

Biz kod yozishdan ko'ra ko'proq ularni o'qiymiz. Shuning uchun biz yozadigan kod izlashga va o'qishga qulay bo'lishi muhimdir. Dasturimizni tushunishda ahamaiyatga ega bo'lgan o'zgaruvchilarni ma'noli qilib nomlamaslik, kodni o'qiyotgan dasturchiga qiyinchilik tug'diradi. O'zgaruvchilarni izlashga qulay qilib nomlang.

Yomon:

declare(strict_types=1);

// 448 nimani anglatadi?
$result = $serializer->serialize($data, 448);

Yaxshi:

declare(strict_types=1);

$json = $serializer->serialize($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);

Izlashga qulay bo'lgan nomlardan foydalaning (2-qism)

Yomon:

declare(strict_types=1);

class User
{
    // 7 nimani anglatadi ?
    public $access = 7;
}

// 4 nimani anglatadi?
if ($user->access & 4) {
    // ...
}

// By yerda qanday mantiq ketayorgani vaqti kelib o'zingizni ham yodingizdan ko'tariladi
$user->access ^= 2;

Yaxshi:

declare(strict_types=1);

class User
{
    public const ACCESS_READ = 1;

    public const ACCESS_CREATE = 2;

    public const ACCESS_UPDATE = 4;

    public const ACCESS_DELETE = 8;

    // Bu yerda esa hammasi tushunarli. Boshlang'ich holatda `$access`ga o'qish, tahrirlash va yaratish huquqlari o'zlashtirilmoqda.
    public $access = self::ACCESS_READ | self::ACCESS_CREATE | self::ACCESS_UPDATE;
}

if ($user->access & User::ACCESS_UPDATE) {
    // tahrirlash ...
}

// Nimadir yaratishga bo'lgan huquqni cheklash
$user->access ^= User::ACCESS_CREATE;

⬆ boshiga qaytish

Mantiqni izohlaydigan o'zgaruvchilardan foydalaning

Yomon:

declare(strict_types=1);

$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,]+,\s*(.+?)\s*(\d{5})$/';
preg_match($cityZipCodeRegex, $address, $matches);

saveCityZipCode($matches[1], $matches[2]);

Yomon emas:

Bu yaxshiroq, lekin biz haliham to'liq mantiqni tushunish uchun regex'ga bog'liqmiz.

declare(strict_types=1);

$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,]+,\s*(.+?)\s*(\d{5})$/';
preg_match($cityZipCodeRegex, $address, $matches);

[, $city, $zipCode] = $matches;
saveCityZipCode($city, $zipCode);

Yaxshi:

Subpattern'larni nomlash orqali regex'ga bog'liqligimizni kamaytirdik.

declare(strict_types=1);

$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,]+,\s*(?<city>.+?)\s*(?<zipCode>\d{5})$/';
preg_match($cityZipCodeRegex, $address, $matches);

saveCityZipCode($matches['city'], $matches['zipCode']);

⬆ boshiga qaytish

Shart operatorlarini bir birini ichiga chuqur joylashtirish va qiymatlarni erta qaytarishni oldini oling (1-qism)

Juda ham ko'p if-else operatorlari kodingizni tushunishni qiyinlashtiradi. Aniqlik mujmallikdan ko'ra yaxshiroq.

Yomon:

declare(strict_types=1);

function isShopOpen($day): bool
{
    if ($day) {
        if (is_string($day)) {
            $day = strtolower($day);
            if ($day === 'friday') {
                return true;
            } elseif ($day === 'saturday') {
                return true;
            } elseif ($day === 'sunday') {
                return true;
            }
            return false;
        }
        return false;
    }
    return false;
}

Yaxshi:

declare(strict_types=1);

function isShopOpen(string $day): bool
{
    if (empty($day)) {
        return false;
    }

    $openingDays = ['friday', 'saturday', 'sunday'];

    return in_array(strtolower($day), $openingDays, true);
}

⬆ boshiga qaytish

Shart operatorlarini bir birini ichiga chuqur joylashtirish va qiymatlarni erta qaytarishni oldini oling (2-qism)

Yomon:

declare(strict_types=1);

function fibonacci(int $n)
{
    if ($n < 50) {
        if ($n !== 0) {
            if ($n !== 1) {
                return fibonacci($n - 1) + fibonacci($n - 2);
            }
            return 1;
        }
        return 0;
    }
    return 'Not supported';
}

Yaxshi:

declare(strict_types=1);

function fibonacci(int $n): int
{
    if ($n === 0 || $n === 1) {
        return $n;
    }

    if ($n >= 50) {
        throw new Exception('Not supported');
    }

    return fibonacci($n - 1) + fibonacci($n - 2);
}

⬆ boshiga qaytish

O'zgaruvchilarni nomlashda Aqliy Xaritalamang

Kodingiz o'quvchisini o'zgaruvchi nimani anglatishini tarjima qilishiga majburlamang. Aniqlik mujmallikdan ko'ra yaxshiroq.

Yomon:

$l = ['Austin', 'New York', 'San Francisco'];

for ($i = 0; $i < count($l); $i++) {
    $li = $l[$i];
    doStuff();
    doSomeOtherStuff();
    // ...
    // ...
    // ...
    // Shoshmang, `$li` nima bo'ldi bu yana?
    dispatch($li);
}

Yaxshi:

declare(strict_types=1);

$locations = ['Austin', 'New York', 'San Francisco'];

foreach ($locations as $location) {
    doStuff();
    doSomeOtherStuff();
    // ...
    // ...
    // ...
    dispatch($location);
}

⬆ boshiga qaytish

Keraksiz kontekst qo'shmang

Agar class/obyekt'ingizni nomi nimanidir anglatib turgan bo'lsa, o'zgaruvchingizni nomida uni takrorlamang

Yomon:

declare(strict_types=1);

class Car
{
    public $carMake;

    public $carModel;

    public $carColor;

    //...
}

Yaxshi:

declare(strict_types=1);

class Car
{
    public $make;

    public $model;

    public $color;

    //...
}

⬆ boshiga qaytish

Qisqa formalar yoki shartlar o'rniga standart argumentlardan foydalaning

Yaxshi emas:

Bu yaxshi emas chunki $breweryName ning qiymati NULL bo'lishi mumkin.

function createMicrobrewery($breweryName = 'Hipster Brew Co.'): void
{
    // ...
}

Yomon emas:

Bu variant oldingisiga qaraganda ancha tushunarli, ammo u o'zgaruvchining qiymatini yaxshiroq boshqaradi.

function createMicrobrewery($name = null): void
{
    $breweryName = $name ?: 'Hipster Brew Co.';
    // ...
}

Yaxshi:

Toifalarni boshqarish dan foydalanishingiz mumkin va $breweryName ning qiymati NULL ga teng bo'lmasligiga ishonchingiz komil bo'ladi.

function createMicrobrewery(string $breweryName = 'Hipster Brew Co.'): void
{
    // ...
}

⬆ boshiga qaytish

Taqqoslash

Toifa bilan taqqoslashdan foydalaning

Yaxshi emas:

Oddiy taqqoslash satrni butun songa aylantiradi.

declare(strict_types=1);

$a = '42';
$b = 42;

if ($a != $b) {
    // Ifoda bajarilmaydi
}

$a != $b ifodasi FALSE (yolg'on) qiymat qaytaradi, aslide esa TRUE (rost) qaytarishi kerak. Chunki, satr toifasidagi 42 butun toifadagi 42 dan farq qiladi.

Yaxshi:

Toifa bilan taqqoslaganda o'zgaruvchilarning qiymati bilan birgalikda toifasi ham solishtiriladi.

declare(strict_types=1);

$a = '42';
$b = 42;

if ($a !== $b) {
    // Ifoda bajariladi
}

$a !== $b taqqosi TRUE (rost) qiymat qaytaradi.

⬆ boshiga qaytish

Null birlashma operatori

Null birlashma operatori PHP 7 versiyadan boshlab joriy qilingan yangi operator hisoblanadi. Bu operator ternar isset () bilan birgalikda ishlatish kerak bo'lgan oddiy holat uchun sintaktik qisqartma sifatida qo'shilgan. Bu operator dastlabki operanda mavjud bo'lsa va u null ga teng bo'lmasa shu operandani qaytaradi, aks holda keyingi operandani qaytaradi;

Yomon:

declare(strict_types=1);

if (isset($_GET['name'])) {
    $name = $_GET['name'];
} elseif (isset($_POST['name'])) {
    $name = $_POST['name'];
} else {
    $name = 'nobody';
}

Yaxshi:

declare(strict_types=1);

$name = $_GET['name'] ?? $_POST['name'] ?? 'nobody';

⬆ boshiga qaytish

Funksiyalar

Funksiya argumentlari (2 yoki kamrog'i ideal)

Funksiya qabul qiluvchi parametrlarini cheklash juda muhimdir, chunki bu testlash jarayonini osonlashtiradi. Uchtadan ortiq parametrlarni qabul qilish har bir holatni testlayotgan paytingizda ko'plab qiyinchiliklarni yuzaga keltiradi.

Argumentsiz funksiyalar bu ideal holatdir. Bir yoki ikkita argument qabul qilish yaxshi, uchtadan esa qochgan ma'qul. Bundan boshqa holatlarda esa imkon qadar birlashtirish kerak. Odatda, agar sizda 2 tadan ortiq argument bo'lsa u holda sizning funksiyangiz juda ko'p amalni bajarishga harakat qiladi. Bunday bo'lmagan hollarda esa argumentlarni obyektlarga joylashtiring.

Yomon:

declare(strict_types=1);

class Questionnaire
{
    public function __construct(
        string $firstname,
        string $lastname,
        string $patronymic,
        string $region,
        string $district,
        string $city,
        string $phone,
        string $email
    ) {
        // ...
    }
}

Yaxshi:

declare(strict_types=1);

class Name
{
    private $firstname;

    private $lastname;

    private $patronymic;

    public function __construct(string $firstname, string $lastname, string $patronymic)
    {
        $this->firstname = $firstname;
        $this->lastname = $lastname;
        $this->patronymic = $patronymic;
    }

    // getters ...
}

class City
{
    private $region;

    private $district;

    private $city;

    public function __construct(string $region, string $district, string $city)
    {
        $this->region = $region;
        $this->district = $district;
        $this->city = $city;
    }

    // getters ...
}

class Contact
{
    private $phone;

    private $email;

    public function __construct(string $phone, string $email)
    {
        $this->phone = $phone;
        $this->email = $email;
    }

    // getters ...
}

class Questionnaire
{
    public function __construct(Name $name, City $city, Contact $contact)
    {
        // ...
    }
}

⬆ boshiga qaytish

Funksiya nomi qanday amal bajarilayotganini aytishi kerak

Yomon:

class Email
{
    //...

    public function handle(): void
    {
        mail($this->to, $this->subject, $this->body);
    }
}

$message = new Email(...);
// Nima bu ? Hozir biz faylga yozayapmizmi ?
$message->handle();

Yaxshi:

class Email
{
    //...

    public function send(): void
    {
        mail($this->to, $this->subject, $this->body);
    }
}

$message = new Email(...);
// Tushunarli va aniq
$message->send();

⬆ boshiga qaytish

Funksiyalar bir darajada abstrakt bo'lishi kerak

Sizda bir darajadan ortiq abstraktsiya bo'lganida funksiyangiz odatada ko'p ishlaydi. Funksiyalarni ajratish qayta foydalanuvchanlikni yaxshilaydi va testlashni osonlashtiradi.

Yomon:

declare(strict_types=1);

function parseBetterPHPAlternative(string $code): void
{
    $regexes = [
        // ...
    ];

    $statements = explode(' ', $code);
    $tokens = [];
    foreach ($regexes as $regex) {
        foreach ($statements as $statement) {
            // ...
        }
    }

    $ast = [];
    foreach ($tokens as $token) {
        // lex...
    }

    foreach ($ast as $node) {
        // parse...
    }
}

Yaxshi emas:

Biz biroz funksionnalikni amalga oshirdik, lekin parseBetterPHPAlternative() funksiyasi haliham testlab bo'lmaydigan darajada juda murakkab.

function tokenize(string $code): array
{
    $regexes = [
        // ...
    ];

    $statements = explode(' ', $code);
    $tokens = [];
    foreach ($regexes as $regex) {
        foreach ($statements as $statement) {
            $tokens[] = /* ... */;
        }
    }

    return $tokens;
}

function lexer(array $tokens): array
{
    $ast = [];
    foreach ($tokens as $token) {
        $ast[] = /* ... */;
    }

    return $ast;
}

function parseBetterPHPAlternative(string $code): void
{
    $tokens = tokenize($code);
    $ast = lexer($tokens);
    foreach ($ast as $node) {
        // parse...
    }
}

Yaxshi:

Eng yaxshi yechim bu parseBetterPHPAlternative() funksiyasini bog'liqligini ko'chirish.

class Tokenizer
{
    public function tokenize(string $code): array
    {
        $regexes = [
            // ...
        ];

        $statements = explode(' ', $code);
        $tokens = [];
        foreach ($regexes as $regex) {
            foreach ($statements as $statement) {
                $tokens[] = /* ... */;
            }
        }

        return $tokens;
    }
}

class Lexer
{
    public function lexify(array $tokens): array
    {
        $ast = [];
        foreach ($tokens as $token) {
            $ast[] = /* ... */;
        }

        return $ast;
    }
}

class BetterPHPAlternative
{
    private $tokenizer;
    private $lexer;

    public function __construct(Tokenizer $tokenizer, Lexer $lexer)
    {
        $this->tokenizer = $tokenizer;
        $this->lexer = $lexer;
    }

    public function parse(string $code): void
    {
        $tokens = $this->tokenizer->tokenize($code);
        $ast = $this->lexer->lexify($tokens);
        foreach ($ast as $node) {
            // parse...
        }
    }
}

⬆ boshiga qaytish

Funksiyada mantiqiy toifalardan parametr sifatida foydalanmang

Mantiqiy operatorlar foydalanuvchiga funksiya bir nechta amal bajarishini bildiradi. Funksiya bitta amalni bajraishi kerak. Agar funksiya mantiqiy asosga ko'ra turli xil amllarni bajarishi kerak bo'lsa, uni ajrating.

Yomon:

declare(strict_types=1);

function createFile(string $name, bool $temp = false): void
{
    if ($temp) {
        touch('./temp/' . $name);
    } else {
        touch($name);
    }
}

Yaxshi:

declare(strict_types=1);

function createFile(string $name): void
{
    touch($name);
}

function createTempFile(string $name): void
{
    touch('./temp/' . $name);
}

⬆ boshiga qaytish

Yondosh ta'sirlarni oldini oling

Agar funksiya qabul qilgan qiymatidan tashqari amal bajarsa va boshqa bir qiymat yoki qiymatlarni qaytarsa, yon ta'sirni yuzaga keltiradi. Yon ta'sirlar faylga yozish, ba'zi global o'zgaruvchilarni o'zgartirish yoki tasodifan barcha pullaringizni begonaga o'takib yuborish kabilar bo'lishi mumkin.

Masalan, ba'zida dasturda yon ta'sirlarga ega bo'lishingizga to'g'ri kelib qoladi. Yuqorida aytilganidek, faylga yozish kerak deylik. Siz qilmoqchi bo'lgan narsa, buni qilayotgan joyingizni markazlashtirishdir. Muayyan faylga yozadigan bir nechta funksiya va sinflar yaratmang. Buni amalga oshiradigan bitta servis yarating. Bitta va yagona.

Asosiysi, hech qanday strukturaga ega bo'lmagan ob'ektlar o'rtasida ma'lumot almashinish, har qanday yozilishi mumkin bo'lgan dinamik ma'lumot turlaridan foydalanish va yon ta'sirlar paydo bo'ladigan joyni markazlashtirmaslik kabi umumiy tuzoqlardan qochishdir. Agar siz buni amalga oshira olsangiz, boshqa dasturchilarning katta qismidan ko'ra baxtliroq bo' lasiz.

Yomon:

declare(strict_types=1);

// Global oʻzgaruvchiga quyidagi funksiya orqali havola qilingan.
// Agar bizda aynan shu nomdan foydalanadigan boshqa funksiya bo'lganida, endi u massiv bo'lardi va uni buzishi mumkin edi.

$name = 'Ryan McDermott';

function splitIntoFirstAndLastName(): void
{
    global $name;

    $name = explode(' ', $name);
}

splitIntoFirstAndLastName();

var_dump($name);
// ['Ryan', 'McDermott'];

Yaxshi:

declare(strict_types=1);

function splitIntoFirstAndLastName(string $name): array
{
    return explode(' ', $name);
}

$name = 'Ryan McDermott';
$newName = splitIntoFirstAndLastName($name);

var_dump($name);
// 'Ryan McDermott';

var_dump($newName);
// ['Ryan', 'McDermott'];

⬆ boshiga qaytish

Global funksiyalarga yozmang

Global funksiyalarga o'zgartirish kiritish ko'p tillarda yomon amaliyotdir, chunki siz boshqa kutubxona bilan to'qnash kelishingiz mumkin va sizning API foydalanuvchingiz amaliyotga tadbiq etilganda istisnoga duch kelmaguncha muammoli bo' lib qolaveradi. Aytaylik, quyidagi misolni ko'rib chiqamiz: agar siz konfiguratsiyani massiv ko'rinishida qabul qilishingizga to'g'ri kelib qolsa nima bo'ladi? Siz config() ga o'xshash global funktsiya yozishingiz mumkin, lekin u xuddi shu vazifani bajaruvchi boshqa kutubxona bilan muammo yuzaga kelishi mumkin.

Yomon:

declare(strict_types=1);

function config(): array
{
    return [
        'foo' => 'bar',
    ];
}

Yaxshi:

declare(strict_types=1);

class Configuration
{
    private $configuration = [];

    public function __construct(array $configuration)
    {
        $this->configuration = $configuration;
    }

    public function get(string $key): ?string
    {
        // null birlashma operatori
        return $this->configuration[$key] ?? null;
    }
}

Konfiguratsiyalarni chaqirib oling va Configuration sinfidan vorislik oling.

declare(strict_types=1);

$configuration = new Configuration([
    'foo' => 'bar',
]);

Endi esa dasturingizda Configuration vorisidan foydalanishingiz kerak.

⬆ boshiga qaytish

Singleton pattern'dan foydalanmang

Singleton anti-pattern hisoblanadi. Brian Button shunday deydi:

  1. Ular odatda global instance (global namuna) sifatida ishlatiladi, nega bu yomon? Chunki siz dasturingiz kodidagi bog'liqliklarni interfeyslar orqali aniq ko'rsatish o'rniga bog'liqliklarni yashirasiz. Biror narsani tarqatmaslik uchun uni gloabl qilish code smell (koddagi muammoni ko'rsatishi mumkin bo'lgan umumiy dasturlash xususiyati).
  2. Ular o'zlarining yaratilishlari va hayot siklini nazorat qilishlari tufayli yagona javobgarlik prinspini buzadi.
  3. Ular tabiatan kodni mahkam bog'lanishiga olib keladi. Bu esa ko'p hollarda testlash jarayonini qiyinlashtiradi.
  4. Ular dastur ishlashining boshidan oxirigacha ma'lumotlarni olib yuradilar. Bu esa testlashni qiyinlashtiruvchi yana bir faktor, unit testlashni bajarishda muammoga sabab bo'lishi mumkin. Nega? Chunki har bir unit testlash bir biridan mustaqil bo'lgani yaxshi.

Bu yerda muammoning ildizi haqida Misko Heveryning fikrlarini keltirib o'tilgan.

Yomon:

declare(strict_types=1);

class DBConnection
{
    private static $instance;

    private function __construct(string $dsn)
    {
        // ...
    }

    public static function getInstance(): self
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    // ...
}

$singleton = DBConnection::getInstance();

Yaxshi:

declare(strict_types=1);

class DBConnection
{
    public function __construct(string $dsn)
    {
        // ...
    }

    // ...
}

Create instance of DBConnection class and configure it with DSN.

declare(strict_types=1);

$connection = new DBConnection($dsn);

Va endi siz dasturingizda DBConnection obyektidan foydalanishingiz kerak.

⬆ boshiga qaytish

Shartlarni inkapsulatsiyalash

Yomon:

declare(strict_types=1);

if ($article->state === 'published') {
    // ...
}

Yaxshi:

declare(strict_types=1);

if ($article->isPublished()) {
    // ...
}

⬆ boshiga qaytish

Inkor qiluvchi shartlardan foydalanmang

Yomon:

declare(strict_types=1);

function isDOMNodeNotPresent(DOMNode $node): bool
{
    // ...
}

if (! isDOMNodeNotPresent($node)) {
    // ...
}

Yaxshi:

declare(strict_types=1);

function isDOMNodePresent(DOMNode $node): bool
{
    // ...
}

if (isDOMNodePresent($node)) {
    // ...
}

⬆ boshiga qaytish

Shartlarni oldini oling

Bu uddalab bo'lmas vazifadek ko'rinadi. Buni birinchi marta eshitganda, ko'pchilik aytadiki "if siz men buni qanday qila olishim mumkin?" Buning javobi shundaki, siz ko'p hollarda polimarfizmdan foydalangan holda bunga erishishingiz mumkin. Ikkinchi savol shundaki, "Yaxshi, bu zo'r lekin lekin nega men buni qilishim kerak?". Javob shuki, biz o'rganayotgan toza kod konseptsiyasiga ko'ra funksiya faqat birgina amalni bajarishi lozim. Qachonki dasturingizdagi funksiya yoki sinfingizda shart operatori qatnashsa, siz foydalanuvchiga funksiyangiz bir dan ortiq amalni bajarayotganini aytasiz. Eslab qoling, faqatgina bir narsani qiling.

Yomon:

declare(strict_types=1);

class Airplane
{
    // ...

    public function getCruisingAltitude(): int
    {
        switch ($this->type) {
            case '777':
                return $this->getMaxAltitude() - $this->getPassengerCount();
            case 'Air Force One':
                return $this->getMaxAltitude();
            case 'Cessna':
                return $this->getMaxAltitude() - $this->getFuelExpenditure();
        }
    }
}

Yaxshi:

declare(strict_types=1);

interface Airplane
{
    // ...

    public function getCruisingAltitude(): int;
}

class Boeing777 implements Airplane
{
    // ...

    public function getCruisingAltitude(): int
    {
        return $this->getMaxAltitude() - $this->getPassengerCount();
    }
}

class AirForceOne implements Airplane
{
    // ...

    public function getCruisingAltitude(): int
    {
        return $this->getMaxAltitude();
    }
}

class Cessna implements Airplane
{
    // ...

    public function getCruisingAltitude(): int
    {
        return $this->getMaxAltitude() - $this->getFuelExpenditure();
    }
}

⬆ boshiga qaytish

Toifani tekshirishni oldini oling (1-qism)

PHP dinamik tipli dasturlash tili hisoblanadi, ya'ni funksiya har qanday turdagi argumentlarni qabul qilishi mumkin. Ba'zida bunday erkinlik noqulaylik yaratadi va funksiyada argumant turini tekshirgingiz kelib qoladi. Buni qilishdan qochishning ko'plab usullari mavjud. Ko'rib chiqilishi kerak bo'lgan birinchi holat - bu ishonchli API hisoblanadi.

Yomon:

declare(strict_types=1);

function travelToTexas($vehicle): void
{
    if ($vehicle instanceof Bicycle) {
        $vehicle->pedalTo(new Location('texas'));
    } elseif ($vehicle instanceof Car) {
        $vehicle->driveTo(new Location('texas'));
    }
}

Yaxshi:

declare(strict_types=1);

function travelToTexas(Vehicle $vehicle): void
{
    $vehicle->travelTo(new Location('texas'));
}

⬆ boshiga qaytish

Toifani tekshirishni oldini oling (2-qism)

Shuni inobatga olingki agar siz satrlar, butun sonlar va massivlar kabi asosiy primitiv qiymatlar bilan ishlayotgan bo'lsangiz va PHP 7+ versiyadan foydalansangiz shunigdek polimorfizmdan foydalana olmasangiz, lekin shundaham o'zgaruvchi tipini tekshirish kerak bo'lsa, tiplarni e'lon qilish yoki strict_types rejimidan foydalanishingiz mumkin. Bu kalit so'z orqali kodda standart PHP sintaksisi ustiga statik tipizatsiya qilish ta'minlanadi. Tipni tekshirishga majburlash bilan bog'liq muammo shundaki, uni amalga oshirish shunchalik qo'shimcha so'zlarni talab qiladiki, siz olgan soxta "turli xavfsizlik" yo'qolgan o'qish qobiliyatini qoplamaydi. Kodni toza yozing, yaxshi testlar yozing va yaxshi kod sharhlariga ega bo'ling. Aks holda, bularning barchasini statik turdagi e'lon qilish yoki strict_types rejimi bilan bajaring.

Yomon:

declare(strict_types=1);

function combine($val1, $val2): int
{
    if (! is_numeric($val1) || ! is_numeric($val2)) {
        throw new Exception('Must be of type Number');
    }

    return $val1 + $val2;
}

Yaxshi:

declare(strict_types=1);

function combine(int $val1, int $val2): int
{
    return $val1 + $val2;
}

⬆ boshiga qaytish

O'lik kodni olib tashlang

O‘lik kod dublikat kod kabi yomondir. Bunday turdagi kodni dasturingizda saqlab turishingizga sabab yo‘q. Agar u dasturda ishlatilmasa undan qutuling! Agar keyinchalik kerak bo‘lib qolsa, u git tarixida saqlanib qoladi.

Yomon:

declare(strict_types=1);

function oldRequestModule(string $url): void
{
    // ...
}

function newRequestModule(string $url): void
{
    // ...
}

$request = newRequestModule($requestUrl);
inventoryTracker('apples', $request, 'www.inventory-awesome.io');

Yaxshi:

declare(strict_types=1);

function requestModule(string $url): void
{
    // ...
}

$request = requestModule($requestUrl);
inventoryTracker('apples', $request, 'www.inventory-awesome.io');

⬆ boshiga qaytish

Obyektlar va ma'lumotlar tuzilmasi

Obyektni inkapsulatsiyalashdan foydalaning

PHPda metodlar uchun public, protected va private kabi kalit so'zlardan foydalaniladi. Bulardan foydalanish orqali obyektning xususiyatlarini o'zgartirishni boshqarish mumkin.

  • Obyekt xususiyatini olishdan tashqari ko'proq narsa qilishni xohlasangiz, dasturingizdagi har bir qismni qidirish yoki o'zgartirish shart emas.
  • set qilayotganda validatsiya qo'shishni osonlashtiradi.
  • Ichkaridagi amalga oshiriladigan jarayonni inkapsulatsiyalaydi.
  • Qiymat olishda va o'rnatishda, loglashni qo'shish va xatoliklarni tutib olish oson.
  • Sinfdan meros olish orqali boshlang'ich funksiyalarni qayta yoza olasiz.
  • Obyekt xususiyatlarini lazy load qila olasiz, masalan serverdan olganda.

Shuningdek, bu Open/Closed tamoyilining bir qismi hisoblanadi.

Yomon:

declare(strict_types=1);

class BankAccount
{
    public $balance = 1000;
}

$bankAccount = new BankAccount();

// Buy shoes...
$bankAccount->balance -= 100;

Yaxshi:

class BankAccount
{
    private $balance;

    public function __construct(int $balance = 1000)
    {
      $this->balance = $balance;
    }

    public function withdraw(int $amount): void
    {
        if ($amount > $this->balance) {
            throw new \Exception('Amount greater than available balance.');
        }

        $this->balance -= $amount;
    }

    public function deposit(int $amount): void
    {
        $this->balance += $amount;
    }

    public function getBalance(): int
    {
        return $this->balance;
    }
}

$bankAccount = new BankAccount();

// Buy shoes...
$bankAccount->withdraw($shoesPrice);

// Get balance
$balance = $bankAccount->getBalance();

⬆ boshiga qaytish

Obyektlarni private, protected xususiyatli qiling

  • public metodlar va xususiyatlar o'zgarishlar uchun juda xavfli hisoblanadi, chunki ba'zi tashqi kodlar ularga tayanishi mumkin va siz aynan qaysi kod ularga tayanayotganini nazorat qila olmaysiz. Sinfdagi o'zgarishlar sinfning barcha foydalanuvchilari uchun xavfli hisoblanadi.

  • protected modifikatoriham public kabi xavfli hisoblanadi, chunki ular har qanday voris sinf uchun ochiq. Buni shuni anglatadiki, public va protected modifikatorlar orasidagi farq faqatgina kirish mexanizmida, lekin inkapsulatsiya bu bir xilda xavflidir. Sinfdagi o'zgarishlar sinfning barcha vorislari uchun xavfli hisoblanadi.

  • private modifikatori esa faqat yagona sinf chegarasida o'zgartirish xavfliligini kafolatlaydi (kodingiz o'zgartirishlar uchun xavfsiz va siz Jenga effectiga duch kelmaysiz).

Shuning uchun, private modifikatordan foydalanishdan boshlang, qo'shimcha sinflarda foydalanish kerak bo'lib qolsa public/protected modifikatorlari orqali bunga erish olasiz.

Mavzu doirasida to'liqroq ma'lumot uchun Fabien Potencier tomonidan yozilgan ushbu postni o'qishingiz mumkin.

Yomon:

declare(strict_types=1);

class Employee
{
    public $name;

    public function __construct(string $name)
    {
        $this->name = $name;
    }
}

$employee = new Employee('John Doe');
// Employee name: John Doe
echo 'Employee name: ' . $employee->name;

Yaxshi:

declare(strict_types=1);

class Employee
{
    private $name;

    public function __construct(string $name)
    {
        $this->name = $name;
    }

    public function getName(): string
    {
        return $this->name;
    }
}

$employee = new Employee('John Doe');
// Employee name: John Doe
echo 'Employee name: ' . $employee->getName();

⬆ boshiga qaytish

Sinflar

Meros olishdan ko'ra kompozitsiyani tanlang

Gang of Four tomonidan yozilgan Design Patterns kitobiga ko'ra, imkoni bo'lsa meros olishdan ko'ra kompozitsiyani afzal ko'rishingiz lozim. Kompozitsiya yoki meros olishga oid juda ko'p sabablar mavjud. Asosiy farq shundaki, agar aqlingiz instinktiv ravishda meros olishni tanlasa, kompozitsiyalash muammoingizni yaxshiroq modellashtirishi mumkinligi haqida o'ylang. Ba'zi hollarda bu to'g'ri yechim bo'ladi.

Ba'zan esa siz "qachon meros olishdan foydalanishim kerak?" deb o'ylarsiz. Bu sizning muammoingizga bog'liq. Quyida esa meros olish kompozitsiyalashdan ko'ra ko'proq tavsiya etiladigan hollar keltirilgan:

  1. Meros olish "has-a" emas balki, "is-a" munosabatni ifodalasa (Human->Animal vs. User->UserDetails).
  2. Asosiy sinfdagi kodni qayat ishlatish imkoniyati mavjud bo'lsa (insonlar barcha hayvonlar kabi harakatlana olishi mumkin).
  3. Asosiy sinfni o'zgartirish orqali meros olingan sinflarga global o'zgartirish kiritish kerak bo'lsa (barcha hayvonlar harakatlanayotganda ularning kaloriya sarfini o'zgartirish kerak bo'lsa).

Yomon:

declare(strict_types=1);

class Employee
{
    private $name;

    private $email;

    public function __construct(string $name, string $email)
    {
        $this->name = $name;
        $this->email = $email;
    }

    // ...
}

// Bu yomon chunki, Employees (xodimlar) soliq ma'lumotlariga ega
// EmployeeTaxData (soliq ma'lumotlari) Employee turiga tegishli

class EmployeeTaxData extends Employee
{
    private $ssn;

    private $salary;

    public function __construct(string $name, string $email, string $ssn, string $salary)
    {
        parent::__construct($name, $email);

        $this->ssn = $ssn;
        $this->salary = $salary;
    }

    // ...
}

Yaxshi:

declare(strict_types=1);

class EmployeeTaxData
{
    private $ssn;

    private $salary;

    public function __construct(string $ssn, string $salary)
    {
        $this->ssn = $ssn;
        $this->salary = $salary;
    }

    // ...
}

class Employee
{
    private $name;

    private $email;

    private $taxData;

    public function __construct(string $name, string $email)
    {
        $this->name = $name;
        $this->email = $email;
    }

    public function setTaxData(EmployeeTaxData $taxData): void
    {
        $this->taxData = $taxData;
    }

    // ...
}

⬆ boshiga qaytish

Fluent interfeyslardan saqlaning

Fluent interfeys bu obyektga yo'naltirilgan API bo'lib metod zanjiri yordamida dastur kodining o'qilishini yaxshilashga hizmat qiladi.

Shunday vaziyatlar bo'lishi mumkinki, quruvchi obyektlarda bu shablon dastur kodining aniqligini tushiradi (masalan PHPUnit Mock Builder yoki Doctrine Query Builder), ko'pincha bunday holat ba'zi kamchiliklarga olib keladi:

  1. Inkapsulatsiyani buzish.
  2. Dekorator shablonini buzish.
  3. mock ma'lumotlarni testlashni qiyinlashtiradi.
  4. Kommitlardagi farqlarni o'qishni qiyinlashtiradi.

To'liq ma'lumot uchun mavzuga oid Marco Pivetta tomonidan yozilgan ushbu blogni o'qishingiz mumkin.

Yomon:

declare(strict_types=1);

class Car
{
    private $make = 'Honda';

    private $model = 'Accord';

    private $color = 'white';

    public function setMake(string $make): self
    {
        $this->make = $make;

        // ESLATMA: Zanjir uchun qaytarilgan
        return $this;
    }

    public function setModel(string $model): self
    {
        $this->model = $model;

        // ESLATMA: Zanjir uchun qaytarilgan
        return $this;
    }

    public function setColor(string $color): self
    {
        $this->color = $color;

        // ESLATMA: Zanjir uchun qaytarilgan
        return $this;
    }

    public function dump(): void
    {
        var_dump($this->make, $this->model, $this->color);
    }
}

$car = (new Car())
    ->setColor('pink')
    ->setMake('Ford')
    ->setModel('F-150')
    ->dump();

Yaxshi:

declare(strict_types=1);

class Car
{
    private $make = 'Honda';

    private $model = 'Accord';

    private $color = 'white';

    public function setMake(string $make): void
    {
        $this->make = $make;
    }

    public function setModel(string $model): void
    {
        $this->model = $model;
    }

    public function setColor(string $color): void
    {
        $this->color = $color;
    }

    public function dump(): void
    {
        var_dump($this->make, $this->model, $this->color);
    }
}

$car = new Car();
$car->setColor('pink');
$car->setMake('Ford');
$car->setModel('F-150');
$car->dump();

⬆ boshiga qaytish

Final sinflardan foydalaning

Imkon qadar final sinflardan foydalaning:

  1. Bu orqali nazorat qilib bo'lmaydigan meros zanjirini oldi olinadi.
  2. Bu kompozitsiyalashni rag'batlantiradi.
  3. Bu yagona javobgarlik prinspiga mos keladi.
  4. Bu orqali dasturchilarni himoyalangan metodlarga kirish uchun sinfni kengaytirish o'rniga umumiy metodlardan foydalanishga undash mumkin.
  5. Bu sizga sinfingizdan foydalanadigan ilovalarning hech qanday uzilishisiz kodingizni o'zgartirish o'zgartirish imkonini beradi.

Yagona shart shundaki sinfingiz interfeysdan foydalanishi va boshqa umumiy metodlar bo'lmasligi lozim.

To'liqroq ma'lumot uchun Marco Pivetta (Ocramius) tomonidan yozilgan ushbu postni o'qishingiz mumkin.

Yomon:

declare(strict_types=1);

final class Car
{
    private $color;

    public function __construct($color)
    {
        $this->color = $color;
    }

    /**
     * @return string The color of the vehicle
     */
    public function getColor()
    {
        return $this->color;
    }
}

Yaxshi:

declare(strict_types=1);

interface Vehicle
{
    /**
     * @return string The color of the vehicle
     */
    public function getColor();
}

final class Car implements Vehicle
{
    private $color;

    public function __construct($color)
    {
        $this->color = $color;
    }

    public function getColor()
    {
        return $this->color;
    }
}

⬆ boshiga qaytish

SOLID

SOLID - bu Robert Martin tomonidan nomlangan birinchi besh tamoyil uchun Michael Feathers kiritgan mnemonik qisqartma bo'lib, u obyektga yo'naltirilgan dasturlash va dizaynning beshta asosiy tamoyilini anglatadi.

Yagona javobgarlik printspi (Single Responsibility Principle, SRP)

"Clean code"da aytilganidek, "Sinfni o'zgartirish uchun hech qachon bir nechta sabab bo'lmasligi kerak". Muammo shundaki, sizning sinfingiz kontseptual jihatdan birlashtirilgan bo'lmaydi va bu uni o'zgartirish uchun ko'p sabablarni yuzaga keltiradi. Sinfni o'zgartirish uchun ketadigan vaqtni kamaytirish muhimdir. Bu juda muhim, chunki bitta sinfda juda ko'p funksiya mavjud bo'lsa va siz uning bir qismini o'zgartirsangiz, bu o'zgartirish dasturdagi boshqa bog'liq modullarga qanday ta'sir qilishini tushunish qiyin bo'lishi mumkin.

Yomon:

declare(strict_types=1);

class UserSettings
{
    private $user;

    public function __construct(User $user)
    {
        $this->user = $user;
    }

    public function changeSettings(array $settings): void
    {
        if ($this->verifyCredentials()) {
            // ...
        }
    }

    private function verifyCredentials(): bool
    {
        // ...
    }
}

Yaxshi:

declare(strict_types=1);

class UserAuth
{
    private $user;

    public function __construct(User $user)
    {
        $this->user = $user;
    }

    public function verifyCredentials(): bool
    {
        // ...
    }
}

class UserSettings
{
    private $user;

    private $auth;

    public function __construct(User $user)
    {
        $this->user = $user;
        $this->auth = new UserAuth($user);
    }

    public function changeSettings(array $settings): void
    {
        if ($this->auth->verifyCredentials()) {
            // ...
        }
    }
}

⬆ boshiga qaytish

Ochiq yopiqlik printspi (Open closed Principle, OCP)

Bertrrand Meyerga ko'ra, "dasturiy qismlar (sinflar, modullar, funktsiyalar va boshqalar) kengaytirish uchun ochiq ammo o'zgartirish uchun yopiq bo'lishi kerak." Bu nimani anglatadi? Ushbu tamoyilga ko'ra siz foydalanuvchilarga mavjud kodni o'zgartirmasdan yangi funktsiyalarni qo'shishga imkon berishingiz kerak.

Yomon:

declare(strict_types=1);

abstract class Adapter
{
    protected $name;

    public function getName(): string
    {
        return $this->name;
    }
}

class AjaxAdapter extends Adapter
{
    public function __construct()
    {
        parent::__construct();

        $this->name = 'ajaxAdapter';
    }
}

class NodeAdapter extends Adapter
{
    public function __construct()
    {
        parent::__construct();

        $this->name = 'nodeAdapter';
    }
}

class HttpRequester
{
    private $adapter;

    public function __construct(Adapter $adapter)
    {
        $this->adapter = $adapter;
    }

    public function fetch(string $url): Promise
    {
        $adapterName = $this->adapter->getName();

        if ($adapterName === 'ajaxAdapter') {
            return $this->makeAjaxCall($url);
        } elseif ($adapterName === 'httpNodeAdapter') {
            return $this->makeHttpCall($url);
        }
    }

    private function makeAjaxCall(string $url): Promise
    {
        // request and return promise
    }

    private function makeHttpCall(string $url): Promise
    {
        // request and return promise
    }
}

Yaxshi:

declare(strict_types=1);

interface Adapter
{
    public function request(string $url): Promise;
}

class AjaxAdapter implements Adapter
{
    public function request(string $url): Promise
    {
        // request and return promise
    }
}

class NodeAdapter implements Adapter
{
    public function request(string $url): Promise
    {
        // request and return promise
    }
}

class HttpRequester
{
    private $adapter;

    public function __construct(Adapter $adapter)
    {
        $this->adapter = $adapter;
    }

    public function fetch(string $url): Promise
    {
        return $this->adapter->request($url);
    }
}

⬆ boshiga qaytish

Liskov almashtirish printsipi (Liskov Substitution Principle, LSP)

Bu juda oddiy tushuncha uchun murakkab atama. Rasmiy ravishda "Agar S ning turi T bo'lsa, u holda T tipidagi obyektlar S tipidagi obyektlar bilan dasturning istalgan xususiyatlarini o'zgartirmasdan almashtirilishi mumkin (ya'ni, S tipidagi obyektlar T tipidagi obyektlarni almashtirishi mumkin)." Bu yanada murakkabroq ta'rif.

Soddaroq qilib aytganda, agar sizda ota sinfi va bola sinfi bo'lsa, unda asosiy sinf va bola sinfi noto'g'ri natijalarga erishmasdan bir-birining o'rnida ishlatilishi mumkin. Bu hali ham chalkash bo'lishi mumkin, shuning uchun klassik kvadrat-to'rtburchak misolini ko'rib chiqaylik. Matematik jihatdan kvadrat to'rtburchakdir, lekin agar siz uni meros orqali "is-a" munosabatidan foydalanib modellashtirsangiz, tezda muammoga duch kelasiz.

Yomon:

declare(strict_types=1);

class Rectangle
{
    protected $width = 0;

    protected $height = 0;

    public function setWidth(int $width): void
    {
        $this->width = $width;
    }

    public function setHeight(int $height): void
    {
        $this->height = $height;
    }

    public function getArea(): int
    {
        return $this->width * $this->height;
    }
}

class Square extends Rectangle
{
    public function setWidth(int $width): void
    {
        $this->width = $this->height = $width;
    }

    public function setHeight(int $height): void
    {
        $this->width = $this->height = $height;
    }
}

function printArea(Rectangle $rectangle): void
{
    $rectangle->setWidth(4);
    $rectangle->setHeight(5);

    // YOMON: Kvadrat uchun 25 ni qaytaradi. Aslida esa 20 bo'lishi kerak.
    echo sprintf('%s has area %d.', get_class($rectangle), $rectangle->getArea()) . PHP_EOL;
}

$rectangles = [new Rectangle(), new Square()];

foreach ($rectangles as $rectangle) {
    printArea($rectangle);
}

Yaxshi:

Eng yaxshi usul - to'rtburchaklarni ajratish va ikkala shakl uchun ham umumiy kichik turni ajratib olishdir.

Kvadrat va to'rtburchakning aniq bo'lgan o'xshashligiga qaramay, ular bir-biridan farq qiladi. Kvadrat romb va parallelogramm bilan to'rtburchaklar bilan juda ko'p umumiyliklarga ega, ammo ular kichik tur emas. Kvadrat, to'rtburchak, romb va parallelogramma o'xshash bo'lsa-da, o'ziga xos xususiyatlarga ega bo'lgan alohida shakllardir.

interface Shape
{
    public function getArea(): int;
}

class Rectangle implements Shape
{
    private $width = 0;
    private $height = 0;

    public function __construct(int $width, int $height)
    {
        $this->width = $width;
        $this->height = $height;
    }

    public function getArea(): int
    {
        return $this->width * $this->height;
    }
}

class Square implements Shape
{
    private $length = 0;

    public function __construct(int $length)
    {
        $this->length = $length;
    }

    public function getArea(): int
    {
        return $this->length ** 2;
    }
}

function printArea(Shape $shape): void
{
    echo sprintf('%s has area %d.', get_class($shape), $shape->getArea()).PHP_EOL;
}

$shapes = [new Rectangle(4, 5), new Square(5)];

foreach ($shapes as $shape) {
    printArea($shape);
}

⬆ boshiga qaytish

Interfeysni ajratish printsipi (Interface Segregation Principle, ISP)

"ISP" ga ko'ra, "Mijozlarni o'zlari foydalanmayotgan interfeyslarga bog'lanishga majburlamaslik kerak".

Ko'plab sozlamali obyektlarni talab qiladigan sinflar uchun ushbu printsip yaxshi misol bo'la oladi. Mijozlardan katta hajmdagi variantlarni o'rnatishni talab qilmaslik foydalidir, chunki ko'pincha ularga barcha sozlamalar kerak bo'lmaydi. Ularni ixtiyoriy qilish ortiqcha interfeysga ega bo'lishni oldini oladi.

Yomon:

declare(strict_types=1);

interface Employee
{
    public function work(): void;

    public function eat(): void;
}

class HumanEmployee implements Employee
{
    public function work(): void
    {
        // ....working
    }

    public function eat(): void
    {
        // ...... eating in lunch break
    }
}

class RobotEmployee implements Employee
{
    public function work(): void
    {
        //.... working much more
    }

    public function eat(): void
    {
        //.... robot can't eat, but it must implement this method
    }
}

Yaxshi:

Har bir ishchi xodim emas, lekin har bir xodim ishchi hisoblanadi.

declare(strict_types=1);

interface Workable
{
    public function work(): void;
}

interface Feedable
{
    public function eat(): void;
}

interface Employee extends Feedable, Workable
{
}

class HumanEmployee implements Employee
{
    public function work(): void
    {
        // ....working
    }

    public function eat(): void
    {
        //.... eating in lunch break
    }
}

// robot can only work
class RobotEmployee implements Workable
{
    public function work(): void
    {
        // ....working
    }
}

⬆ boshiga qaytish

Bog'liqlik inversiyasi printsipi (Dependency Inversion Principle, DIP)

Ushbu tamoyil ikkita muhim narsani belgilaydi:

  1. Yuqori darajadagi modullar past darajadagi modullarga bog'liq bo'lmasligi kerak. Ikkalasi ham abstraktsiyalarga bog'liq bo'lishi kerak.
  2. Abstraktsiyalar tafsilotlarga bog'liq bo'lmasligi kerak. Tafsilotlar abstraktsiyalarga bog'liq bo'lishi kerak.

Avvaliga buni tushunish qiyin bo'lishi mumkin, lekin agar siz PHP freymvorklari (masalan, Symfony) bilan ishlagan bo'lsangiz, bu tamoyilning Dependency Injection (DI) ko'rinishida amalga oshirilishini ko'rgansiz. Ular bir xil tushunchalar bo'lmasada, DIP yuqori darajadagi modullarni o'zining past darajadagi modullari tafsilotlarini bilishni va ularni sozlashni oldini oladi. Buni DI orqali amalga oshirishi mumkin. Buning asosiy afzalligi shundaki, u modullar orasidagi bog'lanishni kamaytiradi. Bog'lanishlar orqali kod yozish yaxshi emas, chunki u kodingizni o'zgartirishni qiyinlashtiradi.

Yomon:

declare(strict_types=1);

class Employee
{
    public function work(): void
    {
        // ....working
    }
}

class Robot extends Employee
{
    public function work(): void
    {
        //.... working much more
    }
}

class Manager
{
    private $employee;

    public function __construct(Employee $employee)
    {
        $this->employee = $employee;
    }

    public function manage(): void
    {
        $this->employee->work();
    }
}

Yaxshi:

declare(strict_types=1);

interface Employee
{
    public function work(): void;
}

class Human implements Employee
{
    public function work(): void
    {
        // ....working
    }
}

class Robot implements Employee
{
    public function work(): void
    {
        //.... working much more
    }
}

class Manager
{
    private $employee;

    public function __construct(Employee $employee)
    {
        $this->employee = $employee;
    }

    public function manage(): void
    {
        $this->employee->work();
    }
}

⬆ boshiga qaytish

O'zingizni takrorlamang (Don’t repeat yourself, DRY)

DRY tamoyiliga amal qilishga harakat qiling.

Yozgan kodingizni takrorlamaslikka imkon qadar harakat qiling. Takrorlangan kod bu yomon, chunki siz biror qismda mantiqni o'zgartirsangiz, boshqa bir nechta joyda ham o'zgartirishlar qilishingizga to'g'ri kelib qoladi.

Tasavvur qiling, siz restoran ishlayapsiz va siz o'zingizning anjomlaringizni kuzatib borasiz: barcha pomidorlaringiz, piyozlaringiz, sarimsoqlaringiz, ziravorlaringiz va boshqalar. Agar sizda buni saqlaydigan bir nechta ro'yxatlar bo'lsa, pomidor bilan taom berishda hammasi yangilanishi kerak bo'ladi. Agar sizda faqat bitta ro'yxat bo'lsa, yangilash uchun faqat bitta qismni o'zgartirasiz!

Ko'pincha takrorlangan kod mavjud bo'ladi, chunki sizda ikkita yoki undan ko'p bir oz farq qiladigan amallar mavjud, ular juda ko'p umumiylikga ega, ammo ularning farqlari sizni bir xil amallarni bajaradigan ikki yoki undan ortiq alohida funktsiyalarga ega bo'lishga majbur qiladi. Ikki nusxadagi kodni olib tashlash bu turli xil amallar to'plamini faqat bitta funktsiya/modul/sinf bilan boshqara oladigan abstraksiya yaratishni anglatadi.

Abstraktsiyani to'g'ri amalga oshirish juda muhim, buning uchun siz sinflar bo'limida keltirilgan SOLID tamoyillariga amal qilishingiz kerak. Noto'g'ri abstraktsiyalar takrorlangan koddan ham yomonroq bo'lishi mumkin, shuning uchun ehtiyot bo'ling! Agar to'g'ri abstraksiya qila olsangiz, buni amalga oshiring! O'zingizni takrorlamang, aks holda siz bir amalni o'zgartirmoqchi bo'lganingizda bir nechta joylarni yangilashingizga to'g'ri keladi.

Yomon:

declare(strict_types=1);

function showDeveloperList(array $developers): void
{
    foreach ($developers as $developer) {
        $expectedSalary = $developer->calculateExpectedSalary();
        $experience = $developer->getExperience();
        $githubLink = $developer->getGithubLink();
        $data = [$expectedSalary, $experience, $githubLink];

        render($data);
    }
}

function showManagerList(array $managers): void
{
    foreach ($managers as $manager) {
        $expectedSalary = $manager->calculateExpectedSalary();
        $experience = $manager->getExperience();
        $githubLink = $manager->getGithubLink();
        $data = [$expectedSalary, $experience, $githubLink];

        render($data);
    }
}

Yaxshi:

declare(strict_types=1);

function showList(array $employees): void
{
    foreach ($employees as $employee) {
        $expectedSalary = $employee->calculateExpectedSalary();
        $experience = $employee->getExperience();
        $githubLink = $employee->getGithubLink();
        $data = [$expectedSalary, $experience, $githubLink];

        render($data);
    }
}

Very good:

Kodning ixcham versiyasidan foydalanish yanayam yaxshiroq.

declare(strict_types=1);

function showList(array $employees): void
{
    foreach ($employees as $employee) {
        render([$employee->calculateExpectedSalary(), $employee->getExperience(), $employee->getGithubLink()]);
    }
}

⬆ boshiga qaytish

Tarjimalar

Boshqa tillarda:

⬆ boshiga qaytish

About

🛁 Clean Code concepts adapted for PHP

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • PHP 100.0%