Наверно уже все наслышаны про технологию 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 отличная штука для документирования или как минимум поможет вам найти ответы, в которых вы застряли в процессе изучении чего-либо.
ссылка на оригинал статьи https://habr.com/ru/post/712630/
Добавить комментарий