Руководство для начинающих по абстракции в объектно-ориентированном программировании

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

Возможно, вы читали ингредиенты из меню, но не знаете, как именно готовится сальса. Приправы, которые они используют для своих овощей, могут быть секретной смесью. Однако вам не нужно знать каждый точный ингредиент. Достаточно знать, что это вкусно.

Вы знаете, что ингредиенты, такие как простой белый рис, бетонные. Остальные элементы абстрактны. Вы знаете, что это сальса, но какая? Или, если кто-то просто вручит вам буррито из ниоткуда, тогда весь буррито будет абстрактным.

Абстракция в абстрактном

Наряду с наследованием важной концепцией объектно-ориентированного программирования является абстракция. Теория заключается в том, что каждый объект должен давать простые и предсказуемые результаты. Объекты также должны разделять только то, что необходимо.

Абстракция сохраняет код и данные скрытыми, когда это необходимо

Вы можете думать о буррито как о предмете. Внутри буррито есть несколько других предметов, таких как бобы, рис, сыр и острый соус. Бобы могли быть приправлены. Сыр может быть смесью. А острый соус может быть комбинацией перцев, выдержанных в уксусе.

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

Абстракция тесно связана с инкапсуляцией

То же самое и с объектами программирования. Когда вы создаете экземпляр объекта (создаете его из класса), это похоже на заказ буррито из прилавка фургона. У вас есть доступ к некоторым данным, но не ко всем. Вам не нужно знать, как работает объект, если функции возвращают правильные данные. Вот буррито на JavaScript / Typescript:

 class CheeseBlend {
private _ingredients = ["Colby Jack", "Cheddar", "Manchego"];
get ingredients() {
return "melted cheese";
}
}
class SecretSalsa {
private _ingredients = ["onions", "tomatoes", "cilantro", "Guatemalan Insanity Peppers"];
get ingredients() {
return "it's a secret";
}
}
class Burrito {
private _beans = "beans";
private _rice = "rice";
private _cheese: CheeseBlend = new CheeseBlend();
private _salsa: SecretSalsa = new SecretSalsa();
get cheese() {
return this._cheese.ingredients;
}
get salsa() {
return this._salsa.ingredients;
}
}
let burro = new Burrito();
console.log(burro.cheese);
console.log(burro.salsa);

Вы можете поиграть с этим кодом в песочнице TypeScript .

В приведенном выше примере ингредиенты сальсы и сыра удалены. Во-первых, они инкапсулированы, чтобы скрыть особые ингредиенты. Затем добавляются геттеры для доступа к ингредиентам. Но ингредиенты дают лишь абстрактное представление о том, что они есть на самом деле.

Абстракция в бетоне

Однако абстракция – это больше, чем концепция. Классы также могут быть абстрактными. Это означает, что они могут определять другие классы. Но сами они не могут быть созданы.

Почему классы иногда должны быть абстрактными

Представьте, что вы идете в ресторан и садитесь. Официант вручает вам меню. Вы открываете его и обнаруживаете, что там только один предмет: еда.

Это довольно абстрактно. Вы бы заказали это? Вы бы съели это? Вы, наверное, не стали бы. Недостаточно сказать, что это еда. Его нужно разбить на что-то более конкретное.

А макароны? Ну, это более конкретный вид еды. И мы знаем, что в нем, вероятно, есть лапша и соус. Но есть много разных видов пасты, так что это все равно абстрактно.

Что такое абстрактное, а что конкретное?

Fettuccine Alfredo – конкретный пример как еды, так и пасты. То же самое и с классами. Не каждый класс должен быть создан. Некоторые классы должны определять только структуру других классов.

Вот пример с абстрактным классом Food и дочерним классом MacadamiaNuts :

 abstract class Food {
constructor(public name: String) {}
abstract totalCalories(): number;
abstract description(): string;
abstract flavor(): string;
}
class MacadamiaNuts extends Food {
constructor() {
super("One Cup of Macadamia Nuts");
}
totalCalories() {
return 962;
}
description() {
return "A nut from Hawaii.";
}
flavor() {
return "rich, buttery, and nutty";
}
}
let nuts = new MacadamiaNuts();
console.log(nuts.name)
console.log(nuts.description())

Вот код.

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

Обратите внимание, что класс MacadamiaNuts расширяет Food . Это означает, что MacadamiaNuts соглашается соблюдать правила питания . Также обратите внимание, что конструктор вызывает super . Эта команда создает экземпляр родительского класса до того, как конструктор создает экземпляр MacadamiaNuts .

Если вы учитесь программировать, вы получите больше удовольствия от этих программных игр .

Практика абстракции

  • Перейдите по ссылке выше и используйте песочницу для создания абстрактного класса Soup.
  • Создайте конкретный дочерний элемент класса Soup с именем Cereal .
  • Используйте console.log для тестирования вашего кода. Что произойдет, если в вашем классе Cereal отсутствует одна из функций, определенных в Soup ?

Но в чем смысл класса, который вы не можете создать?

Поначалу абстрактные классы могут показаться ненужными. В конце концов, вы не можете использовать их для создания объекта. И они не передают свои функции по наследству. Функции должны быть переписаны в каждом дочернем классе.

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