Как стать автором
Обновить

SOLID на примере JavaScript с chatGPT

Время на прочтение4 мин
Количество просмотров9.8K

Наверно уже все наслышаны про технологию ChatGPT. Вкратце недавно решил сам поюзать и вынести отнюдь пользу для программиста, на примере расскажу как я понял очередной пример SOLID и поделюсь примером с теми кто только вникает в шаблоны проектирования.

Запрос:

write me javascript code with solid principles and comment code

Ответ:

// Single Responsibility Principle: Каждая функция должна иметь одну и только одну ответственность
function getUserById(userId) {
  // Эта функция отвечает за получение пользователя по его ID
  return fetch(`https://example.com/users/${userId}`)
    .then(response => response.json())
    .then(user => {
      if (!user) {
        throw new Error(`User with ID ${userId} not found`);
      }
      return user;
    });
}

// Open/Closed Principle: Модуль должен быть открыт для расширения, но закрыт для модификации
class UserService {
  constructor(userRepository) {
    // Класс UserService закрыт для модификации, но его можно расширить, передав другую реализацию userRepository.
    this.userRepository = userRepository;
  }

  getUserById(userId) {
    return this.userRepository.getUserById(userId);
  }
}

// Тут по подробней описал снизу, чтобы окончательно понимать суть принципа...
// Liskov Substitution Principle: Подтипы должны иметь возможность заменять свои родительские типы без изменения корректности программы.
class CachedUserRepository {
  constructor(userRepository) {
    this.userRepository = userRepository;
    this.cache = new Map();
  }

  getUserById(userId) {
    // CachedUserRepository является подтипом UserRepository и может использоваться в качестве замены, не влияя на корректность программы.
    if (this.cache.has(userId)) {
      return Promise.resolve(this.cache.get(userId));
    }
    return this.userRepository.getUserById(userId).then(user => {
      this.cache.set(userId, user);
      return user;
    });
  }
}

// Interface Segregation Principle: Не следует заставлять модуль реализовывать интерфейсы, которые он не использует.
class UserController {
  constructor(userService) {
    this.userService = userService;
  }

  getUserById(req, res) {
    const userId = req.params.id;
    this.userService.getUserById(userId)
      .then(user => res.json(user))
      .catch(err => res.status(404).json({ error: err.message }));
  }
}

// Dependency Inversion Principle: Модули высокого уровня не должны зависеть от модулей низкого уровня.
const userRepository = new UserRepository();
const cachedUserRepository = new CachedUserRepository(userRepository);
const userService = new UserService(cachedUserRepository);
const userController = new UserController(userService);

Если вкратце у нас есть userRepository будь то файл или модуль, в котором есть функция getUserByID, что уже является принципом "S".

Далее мы создаем класс UserService, где в качестве конструктора будет этот userRepository, в нем мы инкапсулируем работу выполнение функции getUserByID, в будущем мы можем расширять данный класс для других методов, здесь заложен у нас принцип под буковкой "О".

"L" - заключается в том что мы не меняем родительский класс, а лишь создаем под класс, который не меняет основную логику родительского класса.

Запрос:

tell me about Liskov Substitution Principle more in this example

Ответ:

Принцип замены Лискова (LSP) гласит, что подтипы должны иметь возможность заменять свои родительские типы без изменения правильности программы. В этом примере класс CachedUserRepository является подтипом класса UserRepository, и его можно использовать в качестве замены для UserRepository, не влияя на корректность программы.

Класс CachedUserRepository предназначен для кэширования результатов вызовов метода getUserById, поэтому, если один и тот же идентификатор пользователя запрашивается несколько раз, можно вернуть кэшированное значение вместо выполнения нового вызова API.

У класса CachedUserRepository есть конструктор, который принимает экземпляр UserRepository в качестве аргумента и использует этот экземпляр для фактического вызова API, когда в кеше нет пользователя.

В этом примере класс CachedUserRepository соблюдает принцип подстановки Лискова, поскольку он имеет тот же метод, что и класс UserRepository, он просто добавляет еще одну функцию кэширования и не меняет поведение или возвращаемое значение метода getUserById.

Следовательно, везде, где в приложении используется UserRepository, CachedUserRepository также можно использовать в качестве замены без нарушения существующего кода.

"I" - принцип разделение интефейсов гласит, что модуль не следует заставлять реализовывать интерфейсы, которые он не использует.

Класс UserController имеет конструктор, который принимает экземпляр UserService в качестве аргумента. Класс UserController использует только метод getUserById из класса UserService и не использует никаких других методов.

Класс UserService может иметь другие методы, такие как getAllUsers, createUser, updateUser и т. д. Но класс UserController использует только getUserById, ему не нужно реализовывать все методы и ему не нужно знать о существовании других методов.

Следуя ISP, класс UserController не вынужден реализовывать какие-либо ненужные методы, что делает код более гибким и простым для понимания.

Такой подход делает код более удобным для сопровождения и более простым для изменения, поскольку изменения в классе UserService повлияют только на классы, которые фактически используют измененные методы, а не на классы, которые их не используют.

Кроме того, ISP позволяет создавать небольшие специализированные интерфейсы, делая их более многоразовыми и гибкими, что делает код более несвязанным и менее подверженным ошибкам.

"D" - принцип инверсии зависимости, гласит что классы не могут быть зависемы от класс низкого уровня, из примера в жизни, что мы не можем быть отцами наших отцов.

В заключении... ChatGPT отличная штука для документирования или как минимум поможет вам найти ответы, в которых вы застряли в процессе изучении чего-либо.

Теги:
Хабы:
Всего голосов 7: ↑0 и ↓7-7
Комментарии9

Публикации

Истории

Работа

Ближайшие события

19 августа – 20 октября
RuCode.Финал. Чемпионат по алгоритмическому программированию и ИИ
МоскваНижний НовгородЕкатеринбургСтавропольНовосибрискКалининградПермьВладивостокЧитаКраснорскТомскИжевскПетрозаводскКазаньКурскТюменьВолгоградУфаМурманскБишкекСочиУльяновскСаратовИркутскДолгопрудныйОнлайн
3 – 18 октября
Kokoc Hackathon 2024
Онлайн
24 – 25 октября
One Day Offer для AQA Engineer и Developers
Онлайн
25 октября
Конференция по росту продуктов EGC’24
МоскваОнлайн
26 октября
ProIT Network Fest
Санкт-Петербург
7 – 8 ноября
Конференция byteoilgas_conf 2024
МоскваОнлайн
7 – 8 ноября
Конференция «Матемаркетинг»
МоскваОнлайн
15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань