← Назад к вопросам
Приведи пример использования Liskov Substitution Principle
2.2 Middle🔥 71 комментариев
#Архитектура и паттерны#ООП
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI28 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Принцип подстановки Лисков (Liskov Substitution Principle)
Это один из пяти принципов SOLID. Суть: объекты подклассов должны корректно заменять объекты суперклассов без нарушения работы программы.
Неправильный пример
class Bird {
fly(): void {
console.log("Птица летит");
}
}
class Penguin extends Bird {
fly(): void {
throw new Error("Пингвин не может летать!");
}
}
// Нарушение LSP
function makeBirdFly(bird: Bird) {
bird.fly(); // Может выбросить ошибку для Penguin
}
makeBirdFly(new Penguin()); // Неожиданное исключение
Здесь Penguin нарушает контракт Bird, т.к. не может летать.
Правильное решение 1: Правильная иерархия
abstract class Bird {
abstract move(): void;
}
class FlyingBird extends Bird {
move(): void {
this.fly();
}
private fly(): void {
console.log("Летит в небе");
}
}
class Penguin extends Bird {
move(): void {
this.swim();
}
private swim(): void {
console.log("Плывёт в воде");
}
}
function makeMove(bird: Bird) {
bird.move(); // Работает для всех птиц
}
makeMove(new FlyingBird()); // Летит в небе
makeMove(new Penguin()); // Плывёт в воде
Правильное решение 2: Interface Segregation
interface Flyer {
fly(): void;
}
interface Swimmer {
swim(): void;
}
class Eagle implements Flyer {
fly(): void {
console.log("Орёл летит высоко");
}
}
class Penguin implements Swimmer {
swim(): void {
console.log("Пингвин плывёт");
}
}
class Duck implements Flyer, Swimmer {
fly(): void {
console.log("Утка летит");
}
swim(): void {
console.log("Утка плывёт");
}
}
function makeFly(flyer: Flyer) {
flyer.fly();
}
function makeSwim(swimmer: Swimmer) {
swimmer.swim();
}
makeFly(new Eagle()); // Работает
makeFly(new Duck()); // Работает
makeSwim(new Penguin()); // Работает
makeSwim(new Duck()); // Работает
Пример с хранилищем данных
interface DataStore {
get(id: string): Promise<any>;
set(id: string, data: any): Promise<void>;
}
class PostgresStore implements DataStore {
async get(id: string): Promise<any> {
return { id, data: "из Postgres" };
}
async set(id: string, data: any): Promise<void> {
// Сохранение в БД
}
}
class RedisCache implements DataStore {
async get(id: string): Promise<any> {
return { id, data: "из Redis" };
}
async set(id: string, data: any): Promise<void> {
// Кэширование
}
}
class InMemoryStore implements DataStore {
private data = new Map();
async get(id: string): Promise<any> {
return this.data.get(id);
}
async set(id: string, data: any): Promise<void> {
this.data.set(id, data);
}
}
async function cacheUserData(store: DataStore, userId: string) {
const user = await store.get(userId);
await store.set(userId, { ...user, lastSeen: new Date() });
}
cacheUserData(new PostgresStore(), "123");
cacheUserData(new RedisCache(), "123");
cacheUserData(new InMemoryStore(), "123");
Ключевые признаки нарушения LSP
- Type checking перед использованием — специальная обработка для подтипов
- Empty implementations — методы ничего не делают
- Выброс исключений — метод выбрасывает исключение вместо корректной работы
Практическое применение
- Правильная иерархия классов — наследуй только если есть отношение является
- Interface Segregation — разделяй большие интерфейсы на маленькие
- Dependency Injection — зависит от абстракций
- Unit тестирование — легко подменять реальные объекты на mock'и
Соблюдение LSP делает код более гибким, тестируемым и устойчивым к изменениям.