• Czas czytania ~14 min
  • 16.06.2023

SOLID, ten akronim został ukuty przez Michaela Feathersa, reprezentuje pięć podstawowych zasad programowania obiek"wego opracowanego przez Wuja Boba.

Większość programistów prawdopodobnie zna ten akronim. Ale wydaje mi się, że mniejszość może " rozszyfrować.

Czy " źle? Nie do końca, myślę, że pisanie czystego i prostego kodu jest ważniejsze niż znajomość teorii. Ale nie ignoruj całkowicie teorii. Jak inaczej przekażesz komuś wiedzę? Jak uzasadniasz swój kod w dyskusji podczas przeglądu kodu? Twój kod musi być oparty na teorii i ogólnie przyjętych standardach.

Ważne jest, aby znać podstawy tego, jak wygląda czysty i prosty kod. Zasady SOLID mogą być używane w dowolnym obiek"wym języku programowania. Pracuję w Symfony na co dzień, więc pokażę kilka zasad w PHP.

Przejdźmy więc razem

przez pięć zasad SOLID.Zasada pojedynczej odpowiedzialności (SRP)

.com/blog/static/f0712fd8b3040fc50e86f004f744cf85/fd28b/single-responsibility-principle.png" sizes="(max-width: 811px) 100vw, 811px" srcset="https://acces".com/blog/static/f0712fd8b3040fc50e86f004f744cf85/ff46a/single-responsibility-principle.png 325w, https://acces".com/blog/static/f0712fd8b3040fc50e86f004f744cf85/a6d36/single-responsibility-principle.png 650w, https://acces".com/blog/static/f0712fd8b3040fc50e86f004f744cf85/fd28b/single-responsibility-principle.png 811w" alt=" przez pięć zasad SOLID.Zasada pojedynczej odpowiedzialności (SRP)" loading="lazy">

Myślę, że jest " najbardziej znana zasada (prawdopodobnie dlatego, że jest pierwsza, a niektórzy ludzie nie czytali dalej). Ale poważnie, myślę, że jest " bardzo ważne.

Wujek Bob opisuje " jako "Klasa powinna mieć jeden i tylko jeden powód do zmiany". Co " oznacza? Dla mnie " zdanie nie jest :) pomocne.

Inne wyjaśnienia mówią, że klasa lub funkcja powinna robić jedną rzecz.

Ale czym jest ta "jedna rzecz"? Czy rejestracja użytkownika może być uważana za "jedną rzecz"? A może " bardziej "rzeczy", bo rejestracje obejmują jakieś inne mniejsze rzeczy, szyfrowanie haseł, zapisywanie do bazy danych i wysyłanie e-maila.

Być może wysłanie wiadomości e-mail można uznać za "jedną rzecz"? W końcu składa się z wielu kroków, takich jak przygo"wanie treści i tematu wiadomości e-mail, wyodrębnienie adresu e-mail użytkownika i obsługa odpowiedzi. Czy powinniśmy utworzyć oddzielną klasę dla każdego z tych działań? Jak daleko możemy się posunąć z tą "pojedynczą odpowiedzialnością"?!

Myślę, że musimy użyć innej zasady programowania obiek"wego, aby odpowiedzieć na " pytanie. W 1974 roku zasada "wysokiej spójności i niskiego sprzężenia" została opisana po raz pierwszy w artykule Structured Design w czasopiśmie IBM.

Postaram się przedstawić " na prostych do zrozumienia przykładach.

Spójność określa, za ile funkcja lub klasa jest odpowiedzialna. Prostym przykładem mogą być Bob i Alice, pomocnicy kucharza. Alicja robi desery. Musi zrobić biszkopt, krem, glazurę, pokroić owoce i złożyć wszystko razem. Każdy z tych kroków składa się z kilku innych. To przykład niskiej spójności. Praca Boba polega na obieraniu ziemniaków, nic więcej, " przykład wysokiej spójności. Twoja me"da/klasa powinna być jak Bob, zrób jedną rzecz.

Sprzęganie dotyczy tego, jak łatwo jest ponownie wykorzystać dany moduł lub klasę. Puzzle i klocki Lego są tego dobrymi przykładami. Puzzle charakteryzują się wysokim sprzężeniem. Jedna łamigłówka mieści się tylko w jednym miejscu, nie można jej łączyć z innymi łamigłówkami. Przeciwieństwo klocków Lego, mają niskie złącze, można je dowolnie łączyć, a każdy z nich może być używany w dowolnym miejscu. Twój kod powinien być jak klocki Lego, łatwy w użyciu w różnych miejscach.

Zasada pojedynczej odpowiedzialności powinna być s"sowana łącznie z zasadą "wysokiej spójności i niskiego powiązania". Obie te zasady, moim zdaniem, starają się powiedzieć " samo.

Teraz przykład w kodzie PHP. Wyobraź sobie klasę BlogPost:

class BlogPost
{
    private Author $author;
    private string $title;
    private string $content;
    private \DateTime $date;
 
    // ..
 
    public function getData(): array
    {
        return [
            'author' => $this->author->fullName(),
            'title' => $this->title,
            'content' => $this->content,
            'timestamp' => $this->date->getTimestamp(),
        ];
    }
 
    public function printJson(): string
    {
        return json_encode($this->getData());
    }
 
    public function printHtml(): string
    {
        return `<article>
                    <h1>{$this->title}</h1>
                    <article>
                        <p>{$this->date->format('Y-m-d H:i:s')}</p>
                        <p>{$this->author->fullName()}</p>
                        <p>{$this->content}</p>
                    </article>
                </article>`;
    }
}

Co tu jest nie tak? Klasa BlogPost robi zbyt wiele rzeczy, a jak wiemy, powinna robić tylko jedną rzecz. Głównym problemem jest ", że jest odpowiedzialny za drukowanie do różnych formatów, JSON, HTML i innych, jeśli zajdzie taka potrzeba. Zobaczmy, jak można " poprawić.

Usuwamy me"dy drukowania z klasy BlogPost, reszta pozostaje niezmieniona. Dodajemy nowy interfejs PrintableBlogPost. Za pomocą me"dy, która może wydrukować wpis na blogu.

interface PrintableBlogPost
{
    public function print(BlogPost $blogPost);
}

Teraz możemy zaimplemen"wać ten interfejs na tyle sposobów, ile potrzebujemy:

class JsonBlogPostPrinter implements PrintableBlogPost
{
    public function print(BlogPost $blogPost) {
        return json_encode($blogPost->getData());
    }
}
 
class HtmlBlogPostPrinter implements PrintableBlogPost
{
    public function print(BlogPost $blogPost) {
        return `<article>
                    <h1>{$blogPost->getTitle()}</h1>
                    <article>
                        <p>{$blogPost->getDate()->format('Y-m-d H:i:s')}</p>
                        <p>{$blogPost->getAuthor()->fullName()}</p>
                        <p>{$blogPost->getContent()}</p>
                    </article>
                </article>`;
    }
}

Możesz zobaczyć cały przykład złej i dobrej implementacji /solid-php" target="_blank" rel="noopener">tutaj

, widziałem projekty, w których klasy mają tylko jedną publiczną me"dę z kilkoma liniami kodu (zwykle wywołaj inną me"dę z innej klasy). Całkowicie nieczytelne i straszne w utrzymaniu. Moim zdaniem jest " przykład pójścia za daleko.

Podsumowując. Twoje zajęcia i me"dy nie powinny być odpowiedzialne za kilka rzeczy. Ale nie chodzi o ", aby popadać w skrajności i emanować absolutnie wszystkim. Aby były łatwe do zrozumienia, ale muszą być również spójne. Abyś nie musiał czytać ich od deski do deski, aby zrozumieć, co robią.

Zasada otwarta/zamknięta (OCP)

.com/blog/static/b076db16e0e2b447626eebecf7b3702c/fd28b/open-closed-principle.png" sizes="(max-width: 811px) 100vw, 811px" srcset="https://acces".com/blog/static/b076db16e0e2b447626eebecf7b3702c/ff46a/open-closed-principle.png 325w, https://acces".com/blog/static/b076db16e0e2b447626eebecf7b3702c/a6d36/open-closed-principle.png 650w, https://acces".com/blog/static/b076db16e0e2b447626eebecf7b3702c/fd28b/open-closed-principle.png 811w" alt="Zasada otwarta/zamknięta (OCP)" loading="lazy">

Po drugie, z zasad SOLID. Ogólne wyjaśnienie jest takie, że "kod powinien być otwarty do rozszerzenia, ale zamknięty do modyfikacji". Nie jest dla mnie oczywiste, co " oznacza w praktyce. Być może lepiej tłumaczy " konsekwencją nieprzestrzegania tej zasady. Zmiana deklaracji me"dy może spowodować jej nieprawidłowe działanie w miejscu, w którym jest używana. Najważniejsze jest ", że zmiany muszą być wstecznie kompatybilne. Oczywiście najlepiej pisać kod, który działa idealnie od początku i nie trzeba go zmieniać, ale nie żyjemy w idealnym świecie.

Postaram się przedstawić " na przykładach:

a) otwarte/zamknięte API Będzie " przykład zasady otwarte/zamknięte nie na pojedynczej klasie, ale na całym API

. To duży SaaS, " system księgowy napisany w PHP, Symfony Framework. Twój API jest używany przez kilkuset klientów, którzy używają go do wystawiania faktur. Interfejs API ma me"dę pobierania faktur w formacie PDF. Załóżmy, że jest " punkt końcowy, taki jak "GET /invoice/{id}/print". Wszystko jest w porządku, ale pewnego dnia klienci żądają opcji pobrania CSV (wszyscy z biznesu uwielbiają tabele).

Dzięki temu można szybko zaimplemen"wać tę funkcję i zmienić punkt końcowy z:

"GET /invoice/{id}/print""

"

GET /invoice/{id}/{format}",

gdzie formatem może być PDF lub CSV.

Now only hundreds of programmers using your API have " change how they download the report in PDF. Well, no, it shouldn't be done that way. How " do it correctly? Unfortunately, it is sometimes necessary " see potential problems and anticipate possible future changes. From the beginning, your endpoint did not follow the open/closed principle because it was not closed for modification. Your endpoint should assume that the need for other formats may arise someday.

b) zwierzęta

otwarte/zamknięte Inny przykład jest bardziej klasyczny. Powiedzmy, że mamy kilka różnych klas zwierząt:I klasę, która pozwala zwierzętom komunikować się:

class Dog
{
    public function bark(): string
    {
        return 'woof woof';
    }
}
class Duck
{
    public function quack(): string
    {
        return 'quack quack';
    }
}
class Fox
{
    public function whatDoesTheFoxSay(): string
    {
        return 'ring-ding-ding-ding-dingeringeding!, wa-pa-pa-pa-pa-pa-pow!';
    }
}

And a class that allows animals " communicate:

class Communication
{
    public function communicate($animal): string
    {
        switch (true) {
            case $animal instanceof Dog:
                return $animal->bark();
            case $animal instanceof Duck:
                return $animal->quack();
            case $animal instanceof Fox:
                return $animal->whatDoesTheFoxSay();
            default:
                throw new \InvalidArgumentException('Unknown animal');
        }
    }
}

Is the Communication class open for extension and closed for modification? To answer this question, we can ask it differently. Are we able " add a new animal class without changing the existing code? No. Adding a new animal class would necessitate the modification of the switch in the communicate() function. So what should our code look like " comply with our principle? Let's try " improve our classes a bit.

Możemy zacząć od dodania interfejsu komunikacyjnego i wykorzystania go w naszych klasach.

interface Communicative
{
    public function speak(): string;
}
class Dog implements Communicative
{
    public function speak(): string
    {
        return 'woof woof';
    }
}
class Duck implements Communicative
{
    public function speak(): string
    {
        return 'quack quack';
    }
}
class Fox implements Communicative
{
    public function speak(): string
    {
        return 'ring-ding-ding-ding-dingeringeding!, Wa-pa-pa-pa-pa-pa-pow!';
    }
}

Następnie możemy zmienić klasę Communication, aby była zgodna z zasadą open/close.

class Communication
{
    public function communicate(Communicative $animal): string
    {
        return $animal->speak();
    }
}

How " code according " the opened/closed principle?

In code, it is worth using interfaces and sticking " them. However, if you need " change something, consider the decora"r pattern.

A class or method should be small enough and have one specific task so that no future event can necessitate modification (single responsibility principle). But you also need " consider whether there may be a need for changes in the future, such as a new response format or an additional parameter, your code should be closed for modification.

Zasada substytucji Liskova (LSP)

.com/blog/static/c3195c3a6034610fa91210f7b21dff14/681f1/liskov-substitution-principle.png" sizes="(max-width: 899px) 100vw, 899px" srcset="https://acces".com/blog/static/c3195c3a6034610fa91210f7b21dff14/ff46a/liskov-substitution-principle.png 325w, https://acces".com/blog/static/c3195c3a6034610fa91210f7b21dff14/a6d36/liskov-substitution-principle.png 650w, https://acces".com/blog/static/c3195c3a6034610fa91210f7b21dff14/681f1/liskov-substitution-principle.png 899w" alt="Zasada substytucji Liskova (LSP)" loading="lazy">

The substitution principle applies " well-designed class inheritance. The author of this principle is Barbara Liskov. The principle says that we can use any inheriting class in place of the base class. If we implement a subclass, we must also be able " use it instead of the main class. Otherwise, it means that inheritance has been implemented incorrectly.

Istnieje kilka popularnych przykładów zasady substytucji Liskova w PHP:

a) prostokąt-kwadrat

Pierwszy przykład. Mamy już klasę PHP Rectangle. Teraz dodajemy klasę PHP Square która dziedziczy klasę Rectangle. Ponieważ każdy kwadrat jest również prostokątem :). Mają te same właściwości, wysokość i szerokość.

Wysokość kwadratu jest taka sama jak szerokość. Tak więc setHeight() i setWidth() ustawią obie (a co z pojedynczą odpowiedzialnością?) z tych wartości:

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

Czy to dobre rozwiązanie? Niestety, nie jest zgodny z zasadą substytucji Liskov. Załóżmy, że istnieje test, który oblicza obszar prostokąta i wygląda on następująco:

public function testCalculateArea()
{
    $shape = new Rectangle();
    $shape->setWidth(10);
    $shape->setHeight(2);
 
    $this->assertEquals($shape->calculateArea(), 20);
 
    $shape->setWidth(5);
    $this->assertEquals($shape->calculateArea(), 10);
}

According " the Liskov substitution principle, we should be able " replace the Rectangle class with the Square class. But if we replace it, it turns out that the test does not pass (100 != 20). Overriding the setWidth() and setHight() methods broke the Liskov substitution rule. We should not change how the parent class's methods work.

Jakie jest więc właściwe rozwiązanie? Nie każdy pomysł z "rzeczywistości" powinien być zaimplementowany 1:1 w kodzie. Klasa Square nie powinna dziedziczyć po klasie Rectangle. Jeśli obie te klasy mogą mieć obszar obliczeniowy, pozwól im zaimplementować wspólny interfejs, a nie dziedziczyć jeden od drugiego, ponieważ są zupełnie różne.

You can see an example solution /solid-php/blob/master/liskov-substitution-principle/liskov-substitution-principle-good.php" target="_blank" rel="noopener">here

b) live duck vs "y duck

Imagine a living duck and a "y duck and their representations in the code (PHP classes). Both of these classes implement the TheDuck interface.

interface TheDuck
{
    public function swim(): void;
}

Mamy też kontroler z akcją swim().

class SomeController
{
    public function swim(): void
    {
        $this->releaseDucks([
            new LiveDuck(),
            new ToyDuck()
        ]);
    }
 
    private function releaseDucks(array $ducks): void
    {
        /** @var TheDuck $duck */
        foreach ($ducks as $duck) {
            $duck->swim();
        }
    }
}

But after calling this action ToyDuck doesn't swim. Why? Because " make it swim, you must first call the "turnOn()" method.

class ToyDuck implements TheDuck
{
    private bool $isTurnedOn = false;
 
    public function swim(): void 
    {
        if (!$this->isTurnedOn) {
            return;
        }
 
        // ...
    }
}

Możemy zmodyfikować akcję kontrolera i dodać warunek, który wywołujemy turnOn() w instancji ToyDuck przed swim().

private function releaseDucks(array $ducks): void
{
    /** @var TheDuck $duck */
    foreach ($ducks as $duck) {
        if ($duck instanceof ToyDuck) {
            $duck->turnOn();
        }
            
        $duck->swim();
    }
}

It violates the Liskov substitution principle because we should be able " use a subclass without knowing the object, so we cannot condition by subclasses (it also violates the open/close principle - because we need " change the implementation).

Obsługa zbioru obiektów danej klasy bazowej może nie wymagać sprawdzania, czy dany obiekt jest instancją podklasy X i powinien być traktowany odmiennie.

Jak to powinno wyglądać poprawnie? Wspólny interfejs dla obu tych kaczek nie jest dobrym pomysłem, ich działanie jest zupełnie inne, mimo że uważamy, że oba działają podobnie, ponieważ pływają, tak nie jest.

c) ReadOnlyFile

I ostatni przykład. Mamy klasę File z metodami read() i write().

class File
{
    public function read()
    {
       // ...
    }
 
    public function write()
    {
       // ...
    }
}

Dodajemy nową klasę - ReadOnlyFile.

class ReadOnlyFile extends File
{
    public function write()
    {
        throw new ItsReadOnlyFileException();
    }
}

The ReadOnlyFile class inherits from the File class. In the ReadOnlyFile class, the write() method will throw an Exception, because you cannot write " a read-only file.

This is a poorly designed abstraction, the Liskov rule has been broken because we are unable " use the ReadOnlyFile class instead of File.

Zasada segregacji interfejsów (ISP)

.com/blog/static/8402cfe5dfc356a6db99e6593e4b1e13/fd28b/interface-segregation-principle.png" sizes="(max-width: 811px) 100vw, 811px" srcset="https://acces".com/blog/static/8402cfe5dfc356a6db99e6593e4b1e13/ff46a/interface-segregation-principle.png 325w, https://acces".com/blog/static/8402cfe5dfc356a6db99e6593e4b1e13/a6d36/interface-segregation-principle.png 650w, https://acces".com/blog/static/8402cfe5dfc356a6db99e6593e4b1e13/fd28b/interface-segregation-principle.png 811w" alt="Zasada segregacji interfejsów (ISP)" loading="lazy">

Uncle Bob introduced this principle when he collaborated with Xerox. They couldn't cope with the ever-long process of implementing changes " their code. The rule is: “No client should be forced " depend on methods it does not use”. The user of the interface should not be forced " rely on methods he does not use. We should not use “fat interfaces” that declare multiple methods if any of them could be left unused. Better " have a few dedicated small interfaces than one that is "o general. It is also in line with the single responsibility principle.

So let's see a badly written code, not following the interface segregation principle. I present " you the Exportable, PHP Interface. An interface that allows you " export something " PDF and export something " CSV. We also have an Invoice and a CreditNote class.

interface Exportable
{
    public function getPDF();
    public function getCSV();
}
class Invoice implements Exportable
{
    public function getPDF() {
        // ...
    }
    public function getCSV() {
        // ...
    }
}
class CreditNote implements Exportable
{
    public function getPDF() {
        throw new \NotUsedFeatureException();
    }
    public function getCSV() {
        // ...
    }
}

Fakturę możemy pobrać w formacie PDF i CSV. Możemy pobrać plik CSV CreditNote. Ale pobieranie pliku PDF z CreditNote było bezużyteczną funkcjonalnością i nie zostało zaimplementowane (teraz rzuca wyjątek).

We shouldn't force our interface implementations " implement methods they don't use. In the above case, we forced the CreditNote class " do so, it implements the getPDF() method even though it does not need it at all.

So how should it look " be good?

According " the interface segregation principle, we have " separate the interfaces. We divide Exportable and create an interface ExportablePdf and create an interface ExportableCSV.

interface ExportablePdf
{
    public function getPDF();
}
interface ExportableCSV
{
    public function getCSV();
}
class Invoice implements ExportablePdf, ExportableCSV
{
    public function getPDF() {
        //
    }
    public function getCSV() {
        //
    }
}
class CreditNote implements ExportableCSV
{
    public function getCSV() {
        //
    }
}

This way, CreditNote no longer has " worry about implementing not used getPDF() public function. If necessary in the future, just need " use a separate interface and implement it. As you can see here, specific interfaces are better.

The example of ReadOnlyFile related " the Liskov principle is also a good example of the Interface segregation principle. There, the File class has been doing "o many things, it's better " have separate interfaces for each action.

To jest segregacja interfejsu, łatwa.

Zasada odwrócenia zależności (DIP)

.com/blog/static/36f1127d8dae2387d8078ee7bffd7477/fd28b/dependency-inversion-principle.png" sizes="(max-width: 811px) 100vw, 811px" srcset="https://acces".com/blog/static/36f1127d8dae2387d8078ee7bffd7477/ff46a/dependency-inversion-principle.png 325w, https://acces".com/blog/static/36f1127d8dae2387d8078ee7bffd7477/a6d36/dependency-inversion-principle.png 650w, https://acces".com/blog/static/36f1127d8dae2387d8078ee7bffd7477/fd28b/dependency-inversion-principle.png 811w" alt="Zasada odwrócenia zależności (DIP)" loading="lazy">

Ostatnio z zasad SOLID reguła ta brzmi:

  • Moduły wysokiego poziomu nie powinny importować niczego z modułów niskiego poziomu. Oba powinny zależeć od abstrakcji (np. interfejsów).
  • Abstrakcje nie powinny zależeć od szczegółów. Szczegóły (konkretne wdrożenia) powinny zależeć od abstrakcji.

What does it mean? We should reduce dependencies " specific implementations but rely on interfaces. If we make any change " the interface (it violates the open/close principle), this change necessitates changes in the implementations of this interface. But if we need " change a specific implementation, we probably don't need " change our interface.

Aby zilustrować problem, przejdźmy do tego przykładu PHP.

class DatabaseLogger
{
    public function logError(string $message)
    {
        // ..
    }
}

Here we have a class that logs some information " the database. Now use this class.

class MailerService
{
    private DatabaseLogger $logger;
 
    public function __construct(DatabaseLogger $logger)
    {
        $this->logger = $logger;
    }
 
    public function sendEmail()
    {
        try {
            // ..
        } catch (SomeException $exception) {
            $this->logger->logError($exception->getMessage());
        }
    }
}

Here is the PHP class that sends e-mails, in case of an error, error details are logged " the database using the logger we have just seen above.

It breaks the principle of dependency inversion. Our e-mail-sending service uses a specific logger implementation. What if we want " log information about errors " a file or Sentry? We will have " change MailerService. This is not a flexible solution, such a replacement becomes problematic.

Jak to powinno wyglądać?

According " this principle, MailerService should rely on abstraction rather than detailed implementation. Therefore, we are adding the LoggerInterface interface.

interface LoggerInterface
{
    public function logError(string $message): void;
}

I używamy go w naszym DatabaseLogger:

class DatabaseLogger implements LoggerInterface
{
   public function logError(string $message): void
   {
       // ..
   }
}

Teraz możemy skorzystać z Symfony Dependency Injection.

class MailerService
{
    private LoggerInterface $logger;
 
    public function sendEmail()
    {
        try {
            // ..
        } catch (SomeException $exception) {
            $this->logger->logError($exception->getMessage());
        }
    }
}

W ten sposób możemy dowolnie zastępować logi w bazie logami gdziekolwiek chcemy, o ile szczegółowa implementacja implementuje LoggerInterface. Ta zmiana nie będzie wymagała modyfikacji MailerService, ponieważ nie zależy od niej, zależy tylko od interfejsu.

Newsletter

Otrzymuj powiadomienia o naszych nowych treściach dla programistów i właścicieli

All these principles come "gether as one, they often overlap. It's nice when you know the theory like SOLID principles because it makes it easier " make good code. Then you also have strong arguments behind your code, for example in code review. All the rules are aimed at making the code easy " understand and maintain.

SOLID is one of the many good practices that help us write .com/blog/how-"-improve-your-code-readability-usefull-clean-code-ideas-"-know/">clean code. I've written about the .com/blog/Boy-scout-rule-in-6-examples-the-basic-principle-of-web-development/" target="_blank" rel="noopener">Boy Scout Rule before. But that's not all, there are many other rules and standards " follow. Let me just mention them:

  • PSR (PHP Standards Recommendations) — PHP Framework Interop Group (PHP-FIG) is a group of people associated with the largest PHP projects who jointly develop PSR. I think every PHP programmer should know coding styles standards PSR-1 and PSR-12 (formerly PSR-2). You can find all the current sets of standards here
  • KISS (Keep It Simple Stupid) — Don't complicate the code. The code should be its documentation itself. Any new programmer on the team should be able " get in" the project quickly.
  • DRY (Don’t Repeat Yourself) — Do not code using the Copy-Paste principle (there is no such rule). See that the same code repeats in several places? Extract code for a separate function.
  • YAGNI (You Aren’t Gonna Need It) — 17th-century German philosopher Johannes Clauberg formulated a principle called Occam's Razor (I was also surprised Ockham was not its author ;) ) “entities should not be multiplied beyond necessity". I think this sentence expresses the YAGNI principle well. We should not write code “for the future”. Such code is not needed at the moment.
  • GRASP (General Responsibility Assignment Software Patterns) — is a large set of rules about which I could write a separate article. These are the basic principles that we should follow when creating object design and responsibility assignments. It consists of Information Expert, Controller, Crea"r, High Cohesion, Low Coupling, Pure Fabrication, Polymorphism, Protected Variations and Indirection.

Applying the SOLID principles in our daily work helps us not get in" technical debt. What are the consequences of incurring technical debt, you can find out in the .com/blog/technical-debt-the-silent-villain-of-web-development/" target="_blank" rel="noopener">article written by our CEO Piotr.

If you have problems understanding your project. Write " us, we have experience in dealing with .com/case-study/legacy/" target="_blank" rel="noopener">difficult cases and .com/services/php-refac"ring-services/" target="_blank" rel="noopener">PHP refac"ring.

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

O

Professional Fullstack Developer with extensive experience in website and desktop application development. Proficient in a wide range of tools and technologies, including Bootstrap, Tailwind, HTML5, CSS3, PUG, JavaScript, Alpine.js, jQuery, PHP, MODX, and Node.js. Skilled in website development using Symfony, MODX, and Laravel. Experience: Contributed to the development and translation of MODX3 i...

O autorze CrazyBoy49z
WORK EXPERIENCE
Kontakt
Ukraine, Lutsk
+380979856297