← Назад к вопросам

Что такое инкапсуляция?

1.0 Junior🔥 151 комментариев
#Архитектура и паттерны

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Инкапсуляция в программировании

Инкапсуляция — это один из основных принципов объектно-ориентированного программирования, который заключается в объединении данных и методов, работающих с этими данными, внутри одного объекта, и в скрытии внутренней реализации от внешнего кода. Это создаёт чёткие границы между внутренним состоянием объекта и его публичным интерфейсом.

Основная идея

Инкапсуляция работает по принципу "чёрного ящика":

  • Внешний код не должен знать, как работает объект изнутри
  • Объект предоставляет контролируемый интерфейс для взаимодействия
  • Внутреннее состояние защищено от неправильного использования
// ❌ Без инкапсуляции
const user = {
  name: 'Alice',
  age: 25,
  email: 'alice@example.com'
};

// Кто угодно может изменить данные неправильно
user.age = -50; // Отрицательный возраст?
user.email = 'not-an-email'; // Некорректный email?

// ✅ С инкапсуляцией
class User {
  #age; // приватное свойство
  #email; // приватное свойство
  
  constructor(name, age, email) {
    this.name = name;
    this.setAge(age); // валидация через метод
    this.setEmail(email); // валидация через метод
  }
  
  setAge(age) {
    if (age < 0 || age > 150) {
      throw new Error('Некорректный возраст');
    }
    this.#age = age;
  }
  
  getAge() {
    return this.#age;
  }
  
  setEmail(email) {
    if (!email.includes('@')) {
      throw new Error('Некорректный email');
    }
    this.#email = email;
  }
  
  getEmail() {
    return this.#email;
  }
}

const user = new User('Alice', 25, 'alice@example.com');
user.getAge(); // 25
user.setAge(-50); // Ошибка: Некорректный возраст

Три уровня доступа

1. Public (публичные свойства и методы)

Доступны всем, могут быть использованы и изменены извне.

class Person {
  // Public свойство
  name = 'John';
  
  // Public метод
  introduce() {
    return `Меня зовут ${this.name}`;
  }
}

const person = new Person();
person.name = 'Alice'; // Можно менять
person.introduce(); // Можно вызывать

2. Private (приватные свойства и методы)

Доступны только внутри самого класса. Обозначаются с помощью # (ES2022).

class BankAccount {
  #balance = 0; // Приватное свойство
  
  #validateAmount(amount) { // Приватный метод
    if (amount <= 0) {
      throw new Error('Сумма должна быть положительной');
    }
  }
  
  deposit(amount) {
    this.#validateAmount(amount); // Используется внутри класса
    this.#balance += amount;
  }
  
  getBalance() {
    return this.#balance; // Публичный доступ к приватным данным
  }
}

const account = new BankAccount();
account.deposit(100);
console.log(account.getBalance()); // 100
console.log(account.#balance); // Ошибка: приватное свойство

3. Protected (защищённые свойства и методы)

В JavaScript нет встроенного ключевого слова protected, но часто используют соглашение с _ префиксом.

class Animal {
  _name = ''; // "защищённое" свойство (соглашение)
  
  _makeSound() { // "защищённый" метод
    return 'Какой-то звук';
  }
  
  speak() {
    return this._makeSound();
  }
}

class Dog extends Animal {
  speak() {
    return 'Гав! ' + this._makeSound();
  }
}

Примеры инкапсуляции

Пример 1: Шкаф с замком

class Safe {
  #contents = [];
  #isLocked = true;
  #password = 'secret123';
  
  unlock(password) {
    if (password === this.#password) {
      this.#isLocked = false;
      return 'Сейф открыт';
    }
    return 'Неверный пароль';
  }
  
  lock() {
    this.#isLocked = true;
    return 'Сейф закрыт';
  }
  
  addItem(item) {
    if (this.#isLocked) {
      throw new Error('Сейф заперт');
    }
    this.#contents.push(item);
  }
  
  getContents() {
    if (this.#isLocked) {
      throw new Error('Сейф заперт');
    }
    return [...this.#contents]; // возвращаем копию, не оригинал
  }
}

const safe = new Safe();
safe.unlock('secret123');
safe.addItem('документы');
const items = safe.getContents();
safe.lock();

Пример 2: Класс для работы с датами

class Date {
  #day;
  #month;
  #year;
  
  constructor(day, month, year) {
    this.setDate(day, month, year);
  }
  
  setDate(day, month, year) {
    if (month < 1 || month > 12) {
      throw new Error('Месяц должен быть от 1 до 12');
    }
    if (day < 1 || day > 31) {
      throw new Error('День должен быть от 1 до 31');
    }
    this.#day = day;
    this.#month = month;
    this.#year = year;
  }
  
  getDay() { return this.#day; }
  getMonth() { return this.#month; }
  getYear() { return this.#year; }
  
  toString() {
    return `${this.#day}.${this.#month}.${this.#year}`;
  }
}

const date = new Date(22, 3, 2025);
console.log(date.toString()); // 22.3.2025

Пример 3: React компонент с приватным состоянием

class Counter extends React.Component {
  #internalValue = 0; // приватное поле класса
  
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
      history: [] // приватное состояние (не выставляется напрямую)
    };
  }
  
  #recordHistory() { // приватный метод
    this.setState(prev => ({
      history: [...prev.history, prev.count]
    }));
  }
  
  increment = () => {
    this.setState(prev => ({
      count: prev.count + 1
    }));
    this.#recordHistory(); // используется только внутри
  }
  
  getHistory() { // публичный метод для доступа к приватным данным
    return this.state.history;
  }
  
  render() {
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={this.increment}>Increment</button>
      </div>
    );
  }
}

Инкапсуляция в функциональных компонентах

// Используем замыкание для инкапсуляции
function createCounter() {
  let count = 0; // приватная переменная
  
  return {
    increment() {
      count++;
      return count;
    },
    decrement() {
      count--;
      return count;
    },
    getCount() {
      return count;
    }
  };
}

const counter = createCounter();
counter.increment(); // 1
counter.increment(); // 2
counter.getCount(); // 2
counter.count = 100; // это не повлияет на приватную переменную
counter.getCount(); // всё ещё 2

Getters и Setters

class Temperature {
  #celsius = 0;
  
  // Setter
  set celsius(value) {
    if (value < -273) {
      throw new Error('Температура не может быть ниже абсолютного нуля');
    }
    this.#celsius = value;
  }
  
  // Getter
  get celsius() {
    return this.#celsius;
  }
  
  get fahrenheit() {
    return (this.#celsius * 9/5) + 32;
  }
  
  set fahrenheit(value) {
    this.celsius = (value - 32) * 5/9;
  }
}

const temp = new Temperature();
temp.celsius = 25;
console.log(temp.fahrenheit); // 77
temp.fahrenheit = 32;
console.log(temp.celsius); // 0

Преимущества инкапсуляции

  1. Защита данных: внутреннее состояние защищено от неправильного использования
  2. Валидация: все изменения проходят через контролируемые методы
  3. Гибкость: можно изменить внутреннюю реализацию без влияния на внешний код
  4. Безопасность: уменьшается риск случайного повреждения данных
  5. Понятнее: ясный публичный интерфейс, скрытые сложности

Плохие практики (антипаттерны)

// ❌ Плохо: прямой доступ к данным
class BadUser {
  age = 0; // публичное, может быть установлено неправильно
}

const user = new BadUser();
user.age = -50; // никто не проверяет!

// ✅ Хорошо: использование методов
class GoodUser {
  #age = 0;
  
  setAge(age) {
    if (age >= 0 && age <= 150) {
      this.#age = age;
    }
  }
  
  getAge() {
    return this.#age;
  }
}

Сравнение: до и после инкапсуляции

Без инкапсуляции:

const product = { price: 100, quantity: 5 };
product.price = -50; // упс, отрицательная цена!
product.quantity = 'много'; // упс, не число!

С инкапсуляцией:

class Product {
  #price;
  #quantity;
  
  constructor(price, quantity) {
    this.setPrice(price);
    this.setQuantity(quantity);
  }
  
  setPrice(price) {
    if (price > 0) this.#price = price;
  }
  
  setQuantity(quantity) {
    if (quantity >= 0 && Number.isInteger(quantity)) this.#quantity = quantity;
  }
  
  getTotal() {
    return this.#price * this.#quantity;
  }
}

const product = new Product(100, 5);
product.setPrice(-50); // ничего не произойдёт
product.setQuantity('много'); // ничего не произойдёт

Инкапсуляция — это не просто техника, это философия программирования, которая создаёт надёжный, предсказуемый и поддерживаемый код. Она является фундаментом качественной архитектуры приложения.