Рубрики
ООП

ООП Наследование «понятие»

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

крайне простая комплекция

ПРИМЕР

У нас есть класс Человек, у которого есть Фамилия, Имя и Возраст. На данном этапе какие-то данные нам не нужны.

Так же у нас появляется класс Работник. У него так же есть Имя, Фамилия и Возраст. Но помимо этого всего есть данные, которые нужны для Работодателя (ИНН, СНИЛС, Серия Паспорта, Номер Паспорта

Таким образом получается, что Класс Работник наследует свойства и методы от Класса Человек. Иным образом можно сказать, что Класс Работник расширяет Класс Человек.

Дальше, двигаясь по цепочке, у нас появляется Класс Разработчик. Помимо Имени, Фамилии, СНИЛС и т.д. у Разработчика есть свои свойства, которые нужны конкретно для него. Например Язык Программирования, с которым работает этот разработчик, Номер Команды и другие свойства.

Хорошо спроектированная ООП система позволяет очень эффективно переиспользовать код, масштабировать и поддерживать. И очень важным винтиком в этой системе является «Наследование»

Создаём класс Person. Добавляем три свойства (Имя, Фамилия и Возраст) и создаём конструктор. После чего мы для каждого свойства создаём по get и set. Но для возраста _age мы немного меняем set, поскольку возраст не может быть меньше нуля.

class Person{
 private _firstName;
 private _lastName;
 private _age;

 constructor(firstName, lastName, age){
  this._firstName=firstName;
  this._lastName=lastName;
  this._age=age;
 }

 set firstName(value){
  this._firstName=value;
 }
 get firstName(){
  return this._firstName;
 }

 set lastName(value){
  this._lastName=value;
 }
 get lastName(){
  return this._lastName;
 }

 set age(value){
  this._age=value;
 }
 get age(){
  return this._age;
 }
}

Затем мы создаём следующий класс работник Eployee. При этом у работника так, же как и у обычного Человека, есть Имя, Фамилия и Возраст. И эти данные так же необходимо хранить, но для того чтобы, не дублировать код, мы не будем писать эти свойства в данном классе. Мы возьмём и расширим уже существующий класс. Для этого предназначено ключевое слово extends. После чего мы указываем тот класс, от которого мы наследуемся. Обратите на то, что в подавляем большинстве языков программирования наследование единственное. Мы не можем унаследоваться от нескольких классов, но при этом например в C++ такая возможность есть. И если класс наследует сразу от нескольких других классов, то он примет сразу все свойства и методы тех классов, от которых он наследуется.

Создадим объект eployee1 класса работник Eployee и передадим туда необходимые параметры. Обратите внимание, что у класса Eployee нет конструктора, но при этом создания объекта мы должны укать Имя, Фамилия и Возраст. Всё дело в том, что конструктор по умолчанию наследуется. Выведем получившиеся объект в логи. И видим, что у получившегося работника есть имя _FirstName, фамилия _LastName, возраст _age. Это говорит о том, что класс Eployee унаследовал все свойства и методы от класса Person.

Следующим этапом добавим ещё пару новых свойств классу Employee. Это соответственно ИНН, Паспорт, СНИЛС. Поэтому создадим конструктор. Как видите он принимает все те же поля, что принимал родительский конструктор, но помимо этого он принимает так же ИНН, Паспорт и СНИЛС. Запись функции super говорит нам о том что, в первую очередь будет вызван родительский конструктор т.е. конструктор класса Person и только потом мы будем уже выполнять какие-то действия в классе Employee.

class Employee extends Person{
 private inn;
 private number;
 private snils;

 constructor(firstName,lastName,age,inn,number,snils){
  super(firstName,lastName,age);

  this.inn=inn;
  this.number=number;
  this.snils=snils;
 }
}

Передадим соответствующие значения в конструктор при создании объекта employee1 класса Employee. Выведем объект в логи. И видим что, объект обладает ещё новыми свойствами, которые мы добавили в класс.

const employee1=new Employee("firstName","lastName","age","inn","number","snils");
console.log(employee1);

Создадим ещё один класс разработчик Developer, который наследуется от класса Employee. Добавляем ещё одно свойство level (джун, миддле, синьор). Создаём конструктор. Обратите внимание, что здесь также вызывается родительский конструктор и в него передаются все необходимые аргументы. После чего создадим экземпляр этого класса объект и в конструктор передаём все необходимые аргументы. Таким образом с помощью наследования мы можем создавать целую иерархию классов, которые обладает своими уникальными свойствами и методами, при этом каждый наследуемый класс перенимает свойства и методы от родительского класса.

class Developer extends Employee{
 private level;
 private language;
 
 constructor(firstName, lastName, age, inn, number, snils, level, language){
 super(firstName, lastName, age, inn, number, snils);
 this.level=level;
 this.language=language;
}
}

Теперь попробуем создать ещё один get в классе Person, которое будет возвращать полное имя того или иного человека. Этот get доступен и для классов, которые наследуются от родительского. Обратимся к этому get у объекта и выведем в консоль

class Person{
...
public get fullName(){
 return 'Фамилия' - ${this._lastName} Имя - ${this._firstName}'
}
}
...
console.log(obj1.fullName);
Рубрики
ООП

S.O.L.I.D

Понятие о SOLID

Рубрики
ООП принцип "Полиморфизм"

ООП принцип Полиморфизм «Понятие»

Полиморфизм

определение
это что-то многообразное т.е. имеет поли много форм

в контексте ООП полиморфизм можно воспринимать как некоторый принцип, который позволяет одному и тому же фрагменту кода работать с разными типами данных. Это очень важный принцип и на нём основано подавляющее количество паттернов в принципе проектирования. Но все принципы ООП связаны и без того же наследования полиморфизм не был бы возможен.

выделяют два основных типа полиморфизма

  • параметрический является истинным

Рассмотрим иерархию (Человека, Работник, Программист). Все они встретились в одной комнате и задача их познакомится т.е. каждый из этих объектов должен сказать какие-то приветственные слова и объяснить кто он.

  • Объект, созданный из класса Person
    говорит «Привет я человек Вася».
  • Объект, созданный из класса Employee
    говорит «Привет я работник Петя»
  • Объект, созданный из класса Devoloper
    говорит «Привет я прогер Евпатий»

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

class Person{
...
public greeting(){
 console.log('Привет я человек и меня зовут ${this._firstName}');
}
...
}
class Employee extends Person{
...
public greeting(){
 console.log('Привет я работник и меня зовут ${this._firstName}');
}
...
}
class Developer extends Employee {
...
public greeting(){
 console.log('Привет я программист и меня зовут ${this._firstName}');
}
...
}

Первое, что приходит в голову это реализовать функцию, которая принимает три аргумента

первый массив объектов, созданных из Person
второй массив объектов, созданных из Работников
третий массив объектов, созданных из Разработчиков

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

Первоначальная задача реализовать такую функцию, которая позволит всем находящимся в комнате друг друга поприветствовать.

Создадим массив, который содержит все как разработчика, так и обычного работника, так просто человека. Сразу укажем, что это у нас массив типа данных т.е. явным образом мы не указываем, что у нас там помимо Person, находятся там и разработчик Developer, работник Employee. Мы просто указываем, что это массив типа Person.

const personList:Person[] = [developer,employee,person]

После чего мы создаём функцию и называем её «массовое приветствие» и аргументом она принимает массив типа Person и сразу же вызываем эту функцию с нашим массивом

Внутри самой функции мы итерируемся по массиву и для каждого объекта из этого массива вызываем функцию приветствия.

function massGreeting(persons:Person[]){
for (let i =0; i < persons.length; i++){
 const person = persons[i];
 person.greeting();
}
}
massGreeting(personList);

Смотри в логи и видем следующий результат: каждый из объектов поприветствовал нужным образом т.е.

  • разработчик сказал, что он разработчик
  • работник сказал, что он работник
  • человек сказал, что он человек

Не смотря, что класс Developer или класс Employee внутри этой функции не задействованы вообще ни как, но отработали соответствующие методы т.е. данное поведение называется полиморфизмом. Мы работаем с объектами, у которых функция greeting работает по разному. При этом в нашем примере там идёт всего лишь вывод в логи, но там может быть какая-то другая логика (более сложная). При этом у нас в массиве находится три разных типа, но функция massGreeting работает с ними всеми одинаково, но при это когда вызывается соответствующий метод, то логика в этом методе разная. В этом и заключается смысл полиморфизма т.е. в данном случае у нас форм, но работаем по сути мы с ними одинаково.

  • ad-hoc является мнимым

В примере класс калькулятор Calculator. В нём есть два метода, которые называются одинаково add и принимают одинаковые параметры, но разных типов. Первый метод работает с числовым типом number, а второй со строковым string. Если мы вызовем метод с двумя 5 то в результате получим 10 т.е. результат суммы этих чисел. Если вызовем со строками, то по сути получим контикинацию «55» т.е. получается, что один метод работает как бы с двумя типами данных. Это происходит за счёт перегрузки методов и такой полиморфизм считается мнимым.

Так мнимым полиморфизмом (так называемым add-hoc полиморфизмом) считается приведение типов. Это когда мы явно какой-то дочерний класс преобразовываем к родительскому.

class Calculator {
 add(a: number, b: number): number {
  return a + b;
 }

 add(a: string, b:string): string {
  return a + b;
 }
}
add(5,5)  -> reult = 10;
add("5","5") -> result = "55";
Рубрики
ООП

Парадигма ООП «понятие»

Был придуман объектно-ориентированный подход, который воспринимался сообществом программистов, как нечто грандиозное, способное решить все проблемы

КЛАСС — ОБЪЕКТ

Рассмотрим два основных понятия
объектно-ориентированных парадигмы

Есть человек
Человека можно охарактеризовать следующими свойствами

  • Имя
  • Фамилия
  • Возраст
  • Вес
  • Рост

Это некоторый список характеристик, с помощью которого можно охарактеризовать любого человека. При этом в контексте ООП подобная характеристика называется КЛАССОМ. (Класс Человек, Класс Human, Класс Person)

ОБЪЕКТ

Конкретный представитель КЛАССАЭКЗЕМПЛЯР называется ОБЪЕКТОМ. В данном примере — это Вася Пупкин, которому 27 лет

КЛАСС

Класс — это некоторое описание характеристик, а Объект — это конкретный экземпляр, у которого каждая характеристика имеет какое-то значение (Например Имя=Вася)

СВОЙСТВА

В контексте ООП эти характеристики (Имя, Фамилия и т.д.) называются СВОЙСТВАМИ.

МЕТОДЫ

А действия, которые может совершать тот или иной объект называются МЕТОДАМИ (Например Ходить, Рисовать, Говорить и т.д.)

КОНСТРУКТОР

У любого класса есть конструктор — это специальный метод, некоторый блок инструкций, который вызывается при создании объекта. Он также может принимать в себя некоторые аргументы. Обычно в конструкторе свойством объекта присваиваются какие-то значения. С помощью оператора new мы можем создать объект (отдельный экземпляр какого-либо класса).

Особенности Класса

У созданного объекта мы можем вызвать соответствующий метод, который нам вернёт значение (результат). При этом из любого класса мы можем создать столько объектов, сколько нам потребуется.

Каждый класс может включать в себя столько свойств и методов — сколько потребуется, но хорошей практикой является делать классы под конкретные ЗАДАЧИ.