Обектно-ориентираното програмиране предлага устойчив начин за писане на код за спагети. Позволява ви да натрупвате програми като поредица от кръпки.
- Пол Греъм

програмиране

Основи на обектно-ориентираното програмиране

Обектно-ориентираното програмиране е програмна парадигма, при която всичко се представя като обект.

Обектите си предават съобщения един на друг. Всеки обект решава какво да прави с получено съобщение. OOP се фокусира върху състоянията и поведенията на всеки обект.

Какво представляват обектите?

Обектът е обект, който има състояния и поведения.

Например куче, котка и превозно средство. За илюстрация кучето има състояния като възраст, цвят, име и поведение като хранене, сън и бягане.

State ни казва как изглежда обектът или какви свойства има.

Поведението ни казва какво прави обектът.

Всъщност можем да представим куче от реалния свят в програма като софтуерен обект, като дефинираме неговите състояния и поведения.

Софтуерните обекти са действителното представяне на обекти от реалния свят. Паметта се разпределя в RAM при създаване на логически обект.

Обектът също се препраща към екземпляр на клас. Инстанцирането на клас означава същото като създаването на обект.

Важното нещо, което трябва да запомните, когато създавате обект, е: референтният тип трябва да бъде същия тип или а супер тип от типа обект. Ще видим какъв е референтният тип по-късно в тази статия.

Какво представляват класовете?

Класът е шаблон или план, от който се създават обекти.

Представете си клас като бисквитка и обекти като бисквитки.

Класовете определят състоянията като променливи на екземпляра, а поведението като методи на екземпляра.

Променливите на инстанцията са известни също като променливи-членове.

Класовете не заемат място.

За да ви дадем представа за класове и обекти, нека създадем клас Cat, който представлява състояния и поведения на Cat от реалния свят.

Сега успешно дефинирахме шаблон за Cat. Да приемем, че имаме две котки на име Тор и Рамбо.

Как можем да ги определим в нашата програма?

Първо, трябва да създадем два обекта от класа Cat.

След това ще определим техните състояния и поведения.

Подобно на горните примери за код, ние можем да дефинираме нашия клас, да го създадем (създаваме обекти) и да посочваме състоянията и поведението на тези обекти.

Сега разгледахме основите на обектно-ориентираното програмиране. Нека да преминем към принципите на обектно-ориентираното програмиране.

Принципи на обектно-ориентираното програмиране

Това са четирите основни принципа на обектно-ориентираната програмна парадигма. Разбирането им е от съществено значение, за да станете успешен програмист.

  1. Капсулиране
  2. Наследяване
  3. Абстракция
  4. Полиморфизъм

Сега нека разгледаме всеки по-подробно.

Капсулиране

Капсулирането е процес на опаковане на код и данни заедно в една единица.

Това е точно като капсула, която съдържа комбинация от няколко лекарства и е техника, която помага да се запазят променливите на екземпляра.

Това може да се постигне чрез използване на модификатори на частен достъп, които не могат да бъдат достъпни от нищо извън класа. За да имаме безопасен достъп до частни държави, трябва да предоставим публични методи за получаване и задаване. (В Java тези методи трябва да следват стандартите за именуване на JavaBeans.)

Да приемем, че има магазин за звукозаписи, който продава музикални албуми на различни изпълнители и пазач, който ги управлява.

Ако погледнете фигура 4, класът StockKeeper може да осъществи директен достъп до състоянията на класа на албума, тъй като състоянията на класа на албума са зададени като обществени .

Ами ако собственикът на акции създаде албум и зададе състояния на отрицателни стойности? Това може да стане умишлено или неволно от държателя на запаси.

За илюстрация нека видим примерна програма на Java, която обяснява горната схема и изявление.

Цената и броят копия на албума не могат да бъдат отрицателни. Как можем да избегнем тази ситуация? Тук използваме капсулиране.

В този сценарий можем да блокираме собственика на запаси да присвоява отрицателни стойности. Ако се опитат да присвоят отрицателни стойности за цената на албума и броя копия, ние ще ги присвоим като 0,0 и 0.

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

Предимства на капсулирането в Java

  1. Можем да направим клас Само за четене или само за писане: за клас само за четене трябва да предоставим само метод за получаване. За клас само за запис трябва да предоставим само метод за задаване.
  2. Контрол върху данните: можем да контролираме данните, като предоставяме логика на методите за настройка, точно както ограничихме собственика на запаси да присвоява отрицателни стойности в горния пример.
  3. Скриване на данни: други класове не могат да имат директен достъп до частни членове на клас.

Наследяване

Да кажем, че звукозаписният магазин, който обсъдихме по-горе, също продава Blu-ray филми.

Както можете да видите в горната диаграма, има много общи състояния и поведения (общ код) между Албум и Филм .

Когато внедрявате тази диаграма на класа в код, ще пишете ли (или копирате и поставяте) целия код за Movie? Ако го направите, вие се повтаряте. Как можете да избегнете дублирането на код?

Тук използваме наследяване.

Наследяването е механизъм, при който един обект придобива всички състояния и поведения на родителски обект.

Наследяването използва взаимоотношения родител-дете (връзка IS-A).

И така, какво точно се наследява?

Модификатори на видимост/достъп въздейства върху това, което се наследява от един клас в друг.

В Java като правило на палеца правим променливите на екземпляра частни, а методите на екземпляра публични .

В този случай можем спокойно да кажем, че следното се наследява:

  1. методи на публична инстанция.
  2. променливи на частния екземпляр (променливите на частния екземпляр могат да бъдат достъпни само чрез публични методи за получаване и задаване).

Видове наследяване в Java

В Java има пет вида наследяване. Те са единични, многостепенни, йерархични, множествени и хибридни.

Класът позволява единично, многостепенно и йерархично наследяване. Интерфейсът позволява множество и хибридни наследства.

Класът може да разшири само един клас, но може да реализира произволен брой интерфейси. Интерфейсът може да разширява повече от един интерфейс.

Връзки

I. Връзка ИС-А

Отношението IS-A се отнася до наследяване или изпълнение.

а. Обобщение

Генерализацията използва връзка IS-A от клас на специализация към клас на генерализация.

II. ИМА-А връзка

Екземпляр на един клас HAS-A препратка към екземпляр на друг клас.

а. Агрегиране

В тази връзка съществуването на клас А и В не са зависими един от друг.

За тази част за агрегиране ще видим пример за класа Student и класа ContactInfo.

Студент има HAS-A ContactInfo. ContactInfo може да се използва и на други места - например клас на служител на компанията също може да използва този клас ContactInfo. Така че Student може да съществува без ContactInfo, а ContactInfo може да съществува без Student. Този тип взаимоотношения са известни като агрегиране.

б. Състав

В тази връзка клас B не може да съществува без клас A - но клас A мога съществуват без клас Б.

За да ви дадем представа за композицията, нека видим пример за класа Student и класа StudentId.

Студент ИМА-A StudentId. Студентът може да съществува без StudentId, но StudentId не може да съществува без Student. Този тип отношения са известни като композиция.

Сега да се върнем към предишния ни пример за магазин за звукозаписи, който обсъдихме по-горе.

Можем да приложим тази диаграма в Java, за да избегнем дублиране на код.

Предимства на наследяването

  1. Повторно използване на кода: дъщерният клас наследява всички членове на инстанцията на родителския клас.
  2. Имате повече гъвкавост за промяна на кода: промяната на кода на място е достатъчна.
  3. Можете да използвате полиморфизъм: заменянето на метода изисква връзка IS-A.

Абстракция

Абстракцията е процес на скриване на подробностите за изпълнението и показване само на функционалност на потребителя.

Чест пример за абстракция е, че натискането на газта ще увеличи скоростта на автомобила. Но водачът не знае как натискането на газта увеличава скоростта - те не трябва да знаят това.

Технически абстрактно означава нещо непълно или да бъде завършено по-късно.

В Java можем да постигнем абстракция по два начина: абстрактен клас (0 до 100%) и интерфейс (100%).

Ключовата дума абстракт може да се приложи към класове и методи. абстрактно и окончателно или статично никога не могат да бъдат заедно.

I. Абстрактен клас

Абстрактен клас е този, който съдържа ключовата дума abstract .

Абстрактните класове не могат да бъдат инстанцирани (не могат да се създават обекти на абстрактни класове). Те могат да имат конструктори, статични методи и крайни методи.

II. Абстрактни методи

Абстрактният метод е този, който съдържа ключовата дума abstract .

Абстрактният метод няма изпълнение (няма тяло на метода и завършва с точка и запетая). Не трябва да се маркира като личен .

III. Абстрактен клас и абстрактни методи

  • Ако в клас съществува поне един абстрактен метод, тогава целият клас трябва да бъде абстрактен.
  • Можем да имаме абстрактен клас без абстрактни методи.
  • Можем да имаме произволен брой абстрактни, както и абстрактни методи в абстрактния клас едновременно.
  • Първият конкретен подклас на абстрактния клас трябва да осигури изпълнение на всички абстрактни методи.
  • Ако това не се случи, тогава подкласът също трябва да бъде маркиран като абстрактен.

В реален сценарий изпълнението ще бъде осигурено от някой, който е непознат за крайните потребители. Потребителите не знаят класа на изпълнение и действителното изпълнение.

Нека разгледаме пример за използване на абстрактни понятия.

Кога искаме да маркираме клас като абстрактен?

  1. Да принуди подкласовете да прилагат абстрактни методи.
  2. За да спрете да имате действителни обекти от този клас.
  3. За да продължите да имате справка за клас.
  4. За да запазите общ код на класа.

Интерфейс

Интерфейсът е план на клас.

Интерфейсът е 100% абстрактен. Тук не се допускат конструктори. Той представлява връзка IS-A.

ЗАБЕЛЕЖКА: Интерфейсите дефинират само необходимите методи. Не можем да запазим общ код.

Интерфейсът може да има само абстрактни методи, но не и конкретни методи. По подразбиране методите на интерфейса са публични и абстрактни. Така че вътре в интерфейса не е необходимо да посочваме публично и абстрактно .

Така че, когато клас реализира метод на интерфейс, без да посочва нивото на достъп на този метод, компилаторът ще изведе грешка, заявявайки „Не може да намали видимостта на наследения метод от интерфейса“. Така че нивото на достъп на внедрения метод трябва да бъде зададено като обществено .

По подразбиране променливите на интерфейса са публични, статични и окончателни .

Нека да видим пример, който обяснява концепцията за интерфейса:

По подразбиране и статични методи в интерфейси

Обикновено ние прилагаме интерфейсни методи в отделен клас. Да приемем, че от нас се изисква да добавим нов метод в интерфейс. Тогава трябва да приложим този метод и в този отделен клас.

За преодоляване на този проблем Java 8 въведе стандартни и статични методи, които прилагат методи в интерфейса, за разлика от абстрактните методи.

  • Метод по подразбиране
  • Статичен метод

Подобно на статичните методи на класове, можем да ги наричаме по името на интерфейса им.

  • Интерфейс на маркер

Това е празен интерфейс. Например сериализуеми, клонируеми и отдалечени интерфейси.

Предимства на интерфейсите

  • Те ни помагат да използваме множествено наследяване в Java.
  • Те осигуряват абстракция.
  • Те осигуряват свободно свързване: обектите са независими един от друг.

Кога искаме да сменим клас на интерфейс?

  1. Да принуди подкласовете да прилагат абстрактни методи.
  2. За да спрете да имате действителни обекти от този клас.
  3. За да продължите да имате справка за клас.

ЗАБЕЛЕЖКА: Не забравяйте, че не можем да задържим общ код вътре в интерфейса.

Ако искате да дефинирате потенциално необходимите методи и общ код, използвайте абстрактен клас.

Ако просто искате да дефинирате необходимия метод, използвайте интерфейс.

Полиморфизъм

Полиморфизмът е способността на обекта да приема много форми.

Полиморфизмът в ООП възниква, когато супер клас препраща към обект от подклас.

Всички Java обекти се считат за полиморфни, тъй като те споделят повече от една връзка IS-A (поне всички обекти ще преминат тест IS-A за техния собствен тип и за обекта на класа).

Можем да получим достъп до обект чрез референтна променлива. Референтната променлива може да бъде само от един тип. След като бъде деклариран, типът на референтната променлива не може да бъде променен.

Референтната променлива може да бъде декларирана като клас или тип интерфейс.

Един обект може да бъде посочен чрез референтни променливи от много различни типове, стига да са същия тип или а супер тип на обекта.

Претоварване на метода

Ако клас има множество методи с едно и също име, но различни параметри, това е известно като претоварване на метода.

Правила за претоварване на метода:

  1. Трябва да има различен списък с параметри.
  2. Може да има различни типове връщане.
  3. Може да има различни модификатори за достъп.
  4. Може да хвърля различни изключения.

ЗАБЕЛЕЖКА: Статичните методи също могат да бъдат претоварени.

ЗАБЕЛЕЖКА: Можем да претоварим метода main (), но Java Virtual Machine (JVM) извиква метода main (), който получава масиви String като аргументи.

Правила за полиморфизъм

Компилирайте правилата за време

  1. Компилаторът познава само референтен тип.
  2. Може да търси методи само в референтен тип.
  3. Извежда подпис на метод.

Правила за времето за изпълнение

  1. По време на изпълнение JVM следва точно тип на изпълнение (тип обект) за намиране на метод.
  2. Трябва да съвпада подпис на метода на времето за компилация с метод в класа на действителния обект.

Замяна на метода

Ако подкласът има същия метод, както е деклариран в супер класа, това е известно като заместване на метода.

Правила за отмяна на метода:

  1. Трябва да има същия списък с параметри.
  2. Трябва да има същия тип връщане: въпреки че ковариантното връщане ни позволява да променим типа връщане на заменения метод.
  3. Не трябва да има по-ограничителен модификатор за достъп: може да има по-малко ограничителен модификатор за достъп.
  4. Не трябва да изхвърля нови или по-широки отметки с изключения: може да изхвърля по-тесни отметки с изключения и може да изхвърля всякакви отметки с изключение.
  5. Само наследените методи могат да бъдат заменени (трябва да имат връзка IS-A).

Пример за заместване на метод:

ЗАБЕЛЕЖКА: Статичните методи не могат да бъдат заменени, защото методите са заменени по време на изпълнение. Статичните методи са свързани с класове, докато методите на екземпляра са свързани с обекти. Така че в Java методът main () също не може да бъде заменен.

ЗАБЕЛЕЖКА: Конструкторите могат да бъдат претоварени, но не и отменени.

Типове обекти и референтни типове

В Person mary = нов студент ();, създаването на този обект е напълно добре.

mary е референтна променлива тип Person и new Student () ще създаде нов обект Student.

mary не може да получи достъп до study () по време на компилация, защото компилаторът знае само референтния тип. Тъй като в клас на референтен тип няма проучване (), той няма достъп до него. Но по време на изпълнение mary ще бъде тип студент (тип изпълнение/тип обект).

Моля, прегледайте тази публикация за повече информация относно типовете на изпълнение.

В този случай можем да убедим компилатора, като кажем „по време на изпълнение, Мери ще бъде тип студент, така че, моля, позволете ми да го извикам“. Как можем да убедим компилатора по този начин? Тук използваме кастинг.

Можем да направим mary тип Student за време на компилация и можем да извикаме study (), като го хвърлим.

След това ще научим за кастинг.

Преливане на тип обект

Леенето от тип Java се класифицира на два типа:

  1. Разширяване на отливането (имплицитно): автоматично преобразуване на типа.
  2. Стесняване на отливките (изрично): нужда от изрично преобразуване.

В примитивите long е по-голям тип от int. Както при обектите, родителският клас е по-голям тип от дъщерния клас.

Референтната променлива се отнася само за обект. Предаването на референтна променлива не променя обекта в купчината, но обозначава същия обект по друг начин чрез достъпност на членовете на екземпляра.

I. Разширяване на отливките

II. Стесняване на отливките

Трябва да внимаваме при стесняване. Когато стесняваме, убеждаваме компилатора да компилира без никаква грешка. Ако го убедим погрешно, ще получим грешка във времето за изпълнение (обикновено ClassCastException).

За да извършим стесняването правилно, използваме оператора instanceof. Той проверява за връзка IS-A.

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

Заключение

Благодаря на всички за четенето. Надявам се тази статия да ви е помогнала.

Силно ви препоръчвам да прочетете повече свързани статии за ООП.

Поръчайте моята оригинална серия статии за Medium: Обектно-ориентирани принципи на програмиране в Java

Моля, уведомете ме, ако имате въпроси.

Мечтата не е това, което виждате, докато спите, това е нещо, което не ви позволява да заспите.
- A P J Абдул Калам, Крила на огъня: Автобиография

Честито кодиране!