Продолжаем разбирать SOLID. Сегодня буква I — Interface Segregation Principle (принцип разделения интерфейсов).
О чём этот принцип?
Клиенты не должны зависеть от методов, которые они не используют. Интерфейсы должны быть узкими и заточенными под конкретную задачу.
Если проще — интерфейсы и контракты классов должны быть узкими и содержать только те методы, которые действительно нужны конкретному клиенту.
Иначе классам придётся реализовывать ненужные методы или выбрасывать исключения.
Пример плохого подхода
Как улучшить?
Разделим интерфейсы на специфичные:
Теперь каждый класс реализует только то, что нужно.
Что это даёт?
— Код чище и проще для поддержки
— Меньше шансов вызвать неподдерживаемый метод
— Легче расширять систему
Дробите интерфейсы на небольшие части, чтобы классы не наследовали ненужные методы.
#BestPractices #JavaScript #typescript
О чём этот принцип?
Клиенты не должны зависеть от методов, которые они не используют. Интерфейсы должны быть узкими и заточенными под конкретную задачу.
Если проще — интерфейсы и контракты классов должны быть узкими и содержать только те методы, которые действительно нужны конкретному клиенту.
Иначе классам придётся реализовывать ненужные методы или выбрасывать исключения.
Пример плохого подхода
interface Device {
print(doc: string): void;
scan(doc: string): void;
fax(doc: string): void;
}
class SimplePrinter implements Device {
print(doc: string): void {
console.log(`Печатаю: ${doc}`);
}
scan(doc: string): void {
throw new Error('Не поддерживает сканирование');
}
fax(doc: string): void {
throw new Error('Не поддерживает факс');
}
}
SimplePrinter вынужден наследовать методы scan и fax, которые ему не нужны, что нарушает принцип.Как улучшить?
Разделим интерфейсы на специфичные:
interface Printable {
print(doc: string): void;
}
interface Scannable {
scan(doc: string): void;
}
interface Faxable {
fax(doc: string): void;
}
class SimplePrinter implements Printable {
print(doc: string): void {
console.log(`Печатаю: ${doc}`);
}
}
Теперь каждый класс реализует только то, что нужно.
Что это даёт?
— Код чище и проще для поддержки
— Меньше шансов вызвать неподдерживаемый метод
— Легче расширять систему
Дробите интерфейсы на небольшие части, чтобы классы не наследовали ненужные методы.
#BestPractices #JavaScript #typescript
❤9👍5
Привет! Обычно для группировки я использую
1.
2.
Пример данных с которыми будем дальше работать:
Object.groupBy:
Map.groupBy:
Разница
1.
2.
Встроенные методы — классная альтернатива без зависимостей, но Lodash остаётся для меня удобным и универсальным инструментом)
#JavaScript
groupBy из Lodash, но в самом JavaScript тоже есть встроенные методы: 1.
Object.groupBy 2.
Map.groupByПример данных с которыми будем дальше работать:
const users = [
{ name: "Аня", age: 25, city: "Москва" },
{ name: "Иван", age: 30, city: "Москва" },
{ name: "Оля", age: 22, city: "Казань" },
{ name: "Петя", age: 30, city: "Казань" },
];
Object.groupBy:
const byCity = Object.groupBy(users, user => user.city);
console.log(byCity);
// {
// "Москва": [ { name: "Аня", age: 25 }, { name: "Иван", age: 30 } ],
// "Казань": [ { name: "Оля", age: 22 }, { name: "Петя", age: 30 } ]
// }
Map.groupBy:
const byAge = Map.groupBy(users, user => user.age);
console.log(byAge);
// Map(3) {
// 25 => [ { name: "Аня", age: 25 } ],
// 30 => [ { name: "Иван", age: 30 }, { name: "Петя", age: 30 } ],
// 22 => [ { name: "Оля", age: 22 } ]
// }
Разница
1.
Object.groupBy - возвращает объект, ключи всегда строки.2.
Map.groupBy - возвращает Map, где ключи могут быть любыми.Встроенные методы — классная альтернатива без зависимостей, но Lodash остаётся для меня удобным и универсальным инструментом)
#JavaScript
👍9🔥7👏1
В проектах часто встречается рендер через логический оператор И:
Если
А что если мы хотим вывести компонент при одном из условий?
Можно написать так:
Но тут есть какой-то подвох... Он не всегда бросается в глаза.
Из-за приоритета операторов И(
Поэтому этот код на самом деле работает так:
В результате компонент появится только тогда, когда
Чтобы логика работала правильно, достаточно добавить группировку:
Теперь компонент отрендерится, если хотя бы одно условие истинно.
Но на самом деле можно упростить ещё больше. Я уже писал про условный рендер в этом посте(тык). Он выглядит понятнее и не заставляет задумываться о приоритетах:
Так код читается сразу и не заставляет думать лишний раз.
#BestPractices #JavaScript
{condition && <div>Component</div>}
Если
condition истинно, то отрендерится компонент. Если же условие ложно, то мы ничего не увидим. Причину этого мы разбирали в этом посте(клац).А что если мы хотим вывести компонент при одном из условий?
Можно написать так:
{condition || secondCondition && <div>Component</div>}
Но тут есть какой-то подвох... Он не всегда бросается в глаза.
Из-за приоритета операторов И(
&&) выполняется раньше, чем ИЛИ(||). Поэтому этот код на самом деле работает так:
{condition || (secondCondition && <div>Component</div>)}
В результате компонент появится только тогда, когда
condition ложно, а secondCondition истинно.Чтобы логика работала правильно, достаточно добавить группировку:
{(condition || secondCondition) && <div>Component</div>}
Теперь компонент отрендерится, если хотя бы одно условие истинно.
Но на самом деле можно упростить ещё больше. Я уже писал про условный рендер в этом посте(тык). Он выглядит понятнее и не заставляет задумываться о приоритетах:
{condition || secondCondition ? <div>Component</div> : null}
Так код читается сразу и не заставляет думать лишний раз.
#BestPractices #JavaScript
⚡7👍5
Привет! Сегодня разберём метод
Что он делает?
Этот метод создаёт промис и возвращает объект, содержащий сам промис и функции для его разрешения или отклонения.
Синтаксис:
-
-
-
Теперь можно вызывать
Пример:
Допустим, вы ждёте событие от внешнего API
Раньше писали так:
Преимущества withResolvers:
1. Упрощает создание управляемых промисов.
2. Убирает антипаттерн ручного присваивания.
3. Делает код более читаемым и безопасным.
Поддержку смотрите через Can I Use.
#JavaScript #BestPractices
Promise.withResolvers. Он упрощает работу с промисами, особенно когда нужно управлять их состоянием извне.Что он делает?
Этот метод создаёт промис и возвращает объект, содержащий сам промис и функции для его разрешения или отклонения.
Синтаксис:
const { promise, resolve, reject } = Promise.withResolvers();
-
promise — сам промис.-
resolve(value) — функция для успешного завершения промиса.-
reject(reason) — функция для отклонения промиса.Теперь можно вызывать
resolve(data) или reject(error) в любом месте, и промис перейдёт в соответствующее состояние.Пример:
Допустим, вы ждёте событие от внешнего API
const { promise, resolve, reject } = Promise.withResolvers();
const ws = new WebSocket('wss://example.com');
ws.onmessage = event => resolve(event.data);
ws.onerror = error => reject(error);
promise
.then(data => console.log('Получено:', data))
.catch(error => console.error('Ошибка:', error));
Раньше писали так:
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
Преимущества withResolvers:
1. Упрощает создание управляемых промисов.
2. Убирает антипаттерн ручного присваивания.
3. Делает код более читаемым и безопасным.
Поддержку смотрите через Can I Use.
#JavaScript #BestPractices
⚡7👍6
Заканчиваем серии постов про SOLID! Сегодня разберём последнюю букву - D, которая расшифровывается как Dependency Inversion Principle (Принцип инверсии зависимостей).
О чём этот принцип?
Высокоуровневые модули не должны зависеть от низкоуровневых. Оба должны зависеть от абстракций. А абстракции не должны зависеть от деталей — детали зависят от абстракций.
Если проще - зависите от интерфейсов, а не от конкретных классов. Это делает код гибким и независимым от деталей реализации.
Пример плохого подхода:
Как улучшить?
Введём интерфейс и передадим зависимость через конструктор:
Теперь
Что это даёт?
- Легко менять реализацию (база, файл, API) без правок в сервисе.
- Подставляйте моки для тестов.
- Зависимости явные, меньше связей.
#BestPractices #JavaScript #typescript
О чём этот принцип?
Высокоуровневые модули не должны зависеть от низкоуровневых. Оба должны зависеть от абстракций. А абстракции не должны зависеть от деталей — детали зависят от абстракций.
Если проще - зависите от интерфейсов, а не от конкретных классов. Это делает код гибким и независимым от деталей реализации.
Пример плохого подхода:
class Database {
save(data: string): void {
console.log(`Сохранено в базе: ${data}`);
}
}
class UserService {
private db = new Database();
saveUser(user: string): void {
this.db.save(user);
}
}
const service = new UserService();
service.saveUser("Alice"); // Сохранено в базе: Alice
UserService жёстко привязан к Database. Хотите сохранить данные в файл или API? Придётся переписывать UserService.Как улучшить?
Введём интерфейс и передадим зависимость через конструктор:
interface Storage {
save(data: string): void;
}
class Database implements Storage {
save(data: string): void {
console.log(`Сохранено в базе: ${data}`);
}
}
class FileStorage implements Storage {
save(data: string): void {
console.log(`Сохранено в файл: ${data}`);
}
}
class UserService {
constructor(private storage: Storage) {}
saveUser(user: string): void {
this.storage.save(user);
}
}
const dbService = new UserService(new Database());
dbService.saveUser("Alice"); // Сохранено в базе: Alice
const fileService = new UserService(new FileStorage());
fileService.saveUser("Bob"); // Сохранено в файл: Bob
Теперь
UserService зависит от абстракции Storage.Что это даёт?
- Легко менять реализацию (база, файл, API) без правок в сервисе.
- Подставляйте моки для тестов.
- Зависимости явные, меньше связей.
#BestPractices #JavaScript #typescript
🔥10👍6
Привет! Недавно мы разбирали нативный метод
Задача
Дан массив объектов:
Нужно сгруппировать по возрасту:
Решение через
Как работает?
1.
2. Для каждого item вычисляем ключ через
3. Если ключа нет в
4. Добавляем
5. Возвращаем обновлённый
#JavaScript #interview
Object.groupBy, а сегодня разберём задачу с реализацией кастомного groupBy. Задача
Дан массив объектов:
const users = [
{ name: 'Алиса', age: 21 },
{ name: 'Макс', age: 25 },
{ name: 'Ваня', age: 21 },
];
Нужно сгруппировать по возрасту:
groupBy(users, user => user.age);
// Результат:
// {
// 21: [{ name: 'Алиса', age: 21 }, { name: 'Ваня', age: 21 }],
// 25: [{ name: 'Макс', age: 25 }]
// }
Решение через
reduce:
function groupBy(array, fn) {
return array.reduce((acc, item) => {
const key = fn(item);
(acc[key] ||= []).push(item);
return acc;
}, {});
}
Как работает?
1.
reduce накапливает объект acc.2. Для каждого item вычисляем ключ через
fn(item).3. Если ключа нет в
acc, создаём массив.4. Добавляем
item в этот массив.5. Возвращаем обновлённый
acc.#JavaScript #interview
🔥9👍4