Полиморфизмът е една от трите основни OOP парадигми. Накратко, полиморфизмът е способността на даден обект да използва методи на производен клас, който не съществува по време на създаването на основния. За тези, които не са особено запознати с ООП, това вероятно звучи сложно. Затова нека да разгледаме използването на полиморфизъм с помощта на пример.

Формулиране на проблема

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

Най-простите варианти, които ви идват на ум, са да напишете три отделни класа и да работите с тях. Или напишете един клас, който ще съдържа всички свойства, присъщи на трите вида публикации, и ще се използват само необходимите. Но за различните типове методите, които са сходни по логика, трябва да работят по различен начин. Създаването на няколко метода от един и същи тип за различни типове (get_news, get_announcements, get_articles) е напълно неграмотно. Това е мястото, където полиморфизмът идва на помощ.

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

Грубо казано, това е шаблонен клас. Той изпълнява функционалност само на нивото, на което е известно в момента. Производните класове го допълват. Но е време да преминем от теория към практика. Нека направя резервация веднага: разглеждаме примитивен пример с минимална функционалност. Всички обяснения са в коментарите в кода.

абстрактен клас Публикация
{
// таблица, която съхранява данни за елемента
защитена $таблица;

// свойствата на елемента са неизвестни за нас
защитени $properties = array();

// конструктор

{
// имайте предвид, че не знаем от коя таблица трябва да вземем данните
$result = mysql_query ("SELECT * FROM `" . $this -> table . "` WHERE `id`="" . $id . "" LIMIT 1" );
// ние също не знаем какви данни сме получили
$this -> свойства = mysql_fetch_assoc ($result);
}

// метод, еднакъв за всеки тип публикация, връща стойността на свойството
публична функция get_property ($name)
{
if (isset($this -> properties [ $name ]))
върне $this -> свойства [ $name ];

Връща невярно;
}

// метод, еднакъв за всеки тип публикация, задава стойността на свойството
публична функция set_property ($name, $value)
{
if (!isset($this -> properties [ $name ]))
връща невярно;

$this -> свойства [ $name ] = $value;

Върнете $стойност;
}

// и този метод трябва да отпечата публикацията, но ние не знаем как точно да направим това и затова го обявяваме абстрактно
абстрактна публична функция do_print();
}

Производни класове

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

клас Новини разширява Публикация
{
// конструктор на класа новини, извлечен от класа публикации
публична функция __construct ($id)
{
// задайте стойността на таблицата, в която се съхраняват новинарски данни
$this -> table = "news_table" ;
родител :: __construct ($id);
}

Публична функция do_print()
{
echo $this -> свойства ["заглавие"];
ехо "

" ;
echo $this -> свойства ["текст"];
ехо "
Източник: " . $this -> свойства [ "източник" ];
}
}

Съобщението на класа удължава публикацията
{
// конструктор на класа реклама, извлечен от класа публикация
публична функция __construct ($id)
{
// задайте стойността на таблицата, в която се съхраняват рекламните данни
$this -> table = "announcements_table" ;
// извикване на конструктора на родителския клас
родител :: __construct ($id);
}

// заменя абстрактния метод за печат
публична функция do_print()
{
echo $this -> свойства ["заглавие"];
ехо "
внимание! Обявата е валидна до "
. $this -> свойства ["крайна_дата"];
ехо "

" . $this -> свойства [ "текст" ];
}
}

Статията на класа разширява публикацията
{
// конструктор на класа статия, извлечен от класа публикация
публична функция __construct ($id)
{
// задаваме стойността на таблицата, в която се съхраняват данните за статиите
$this -> table = "articles_table" ;
// извикване на конструктора на родителския клас
родител :: __construct ($id);
}

// заменя абстрактния метод за печат
публична функция do_print()
{
echo $this -> свойства ["заглавие"];
ехо "

" ;
echo $this -> свойства ["текст"];
ехо "
" . $this -> свойства [ "автор" ];
}
}

Сега за употребата

Въпросът е, че един и същ код се използва за обекти от различни класове.

// попълване на масива за публикация с обекти, извлечени от публикация
$publications = нови новини ($news_id);
$publications = ново съобщение($announcement_id);
$publications = нова статия($article_id);

Foreach ($publications като $publication) (
// ако работим с наследници на публикации
if ($publication instanceof Publication) (
// след това отпечатайте данните
$publication -> do_print();
) иначе (
// обработка на изключение или грешка
}
}

Това е всичко. С леко движение на ръката панталона се превръща в елегантни шорти :-).

Основното предимство на полиморфизма е лекотата, с която могат да се създават нови класове, които се държат подобно на свързаните, което от своя страна позволява разширяемост и модифицираемост. Статията показва само примитивен пример, но дори той показва как използването на абстракции може да улесни разработката. Можем да работим с новини точно както с реклами или статии и дори не е нужно да знаем с какво работим! В реални, много по-сложни приложения тази полза е още по-голяма.

Малко теория

  • Методите, които изискват отмяна, се наричат ​​абстрактни. Логично е, че ако един клас съдържа поне един абстрактен метод, то той също е абстрактен.
  • Очевидно обект от абстрактен клас не може да бъде създаден, иначе не би бил абстрактен.
  • Производният клас има свойства и методи, които принадлежат на базовия клас, и може също да има свои собствени методи и свойства.
  • Метод, който е заменен в производен клас, се нарича виртуален метод. Няма информация за този метод в базовия абстрактен клас.
  • Смисълът на абстракцията е да се дефинира метод на мястото, където има най-пълна информация за това как трябва да работи.
UPD:Относно нарушенията на sql-inj и MVC - господа, това е само пример и то пример за полиморфизъм, при който не смятам за нужно да обръщам внимание на тези неща. Това е тема за съвсем различни статии.

Обектно-ориентирано програмиране(OOP) е подход за създаване на програми, базиран на използването на класове и обекти, които взаимодействат помежду си.

Клас (java клас) описва структурата и поведението на обектите. Устройството се описва чрез набор от характеристики (свойства), а поведението се описва чрез набор от операции (методи), достъпни за обектите. Класове могат да бъдат създадени въз основа на съществуващи чрез добавяне или замяна на свойства и методи.

Класовете представляват шаблоните, по които се изграждат обектите. Обектите са програмни елементи, които имат подобен набор от характеристики и поведение (т.е. те са елементи, изградени на базата на един и същи клас). Всеки обект има определено състояние, то се определя от стойността на всички негови свойства. Няколко класа могат да съществуват в една програма и обекти от различни класове могат да взаимодействат помежду си (чрез методи).

Наследство, разширява се

Наследяването е неразделна част от Java. При използване на наследяване се взема предвид, че нов клас, който наследява свойствата на базовия (родителския) клас, има всички свойства, които притежава родителят. Кодът използва операнд се простира, последвано от името на базовия клас. Това отваря достъп до всички полета и методи на базовия клас.

С помощта на наследяването е възможно да се създаде общ "java клас", който дефинира характеристики, общи за набор от свързани елементи. След това можете да наследите от него и да създадете допълнителни класове, за които можете да дефинирате допълнителни характеристики, които са уникални за тях.

Основният наследен клас в Java се нарича суперклас супер. Наследяващият клас се извиква подклас. По този начин подкласът е специализирана версия на суперклас, която наследява всички свойства на суперкласа и добавя свои собствени уникални елементи.

Нека разгледаме пример за описание на Java класа "ученик Ученик, който има име, фамилия, възраст и номер на групата. Ще създадем ученически клас въз основа на супер класа на потребителя Потребител, който вече има собствено име, фамилия и определена възраст:

Публичен клас Потребител ( int age; String firstName; String lastName; // Конструктор public User(int age, String firstName, String lastName) ( this.age = възраст; this.firstName = firstName; this.lastName = lastName; ) )

Сега създаваме отделен клас Student, който наследява свойствата на супер класа. Когато наследявате клас, трябва също да замените конструкторите на родителския клас:

Public class Student extends User ( int group; // Constructor public Student(int age, String firstName, String lastName) ( super(age, firstName, lastName); ) boolean isMyGroup(int g) ( return g == group; ) )

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

Ключова дума супер

В конструктора на класа Student извикваме конструктора на родителския клас чрез оператора супер, предавайки му целия необходим набор от параметри. В Java ключовата дума суперобозначава суперклас, т.е. Класът, от който произлиза текущият клас. Ключовата дума super може да се използва за извикване на конструктор на суперклас и за достъп до член на суперклас, скрит от член на подклас.

Да видим как ще стане наследствопо отношение на създаването на обект:

Студент студент = нов Студент(18, "Киса", "Воробянинов", 221);

Първо се отваря конструкторът на класа Student, след това се извиква конструкторът на суперкласа User и след това се изпълняват останалите операции в конструктора Student. Тази последователност от действия е доста логична и ви позволява да създавате по-сложни обекти въз основа на по-прости.

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

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

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

Клас A ( int i; ) // наследява от клас A клас B разширява A ( int i; // името на променлива съвпада и скрива променлива i в клас A B (int a, int b) ( super.i = a; // достъп към променлива i от клас A i = b; // достъп до променлива i от клас B ) void show() ( System.out.println("i от суперклас е " + super.i); System.out.println(" i в подкласа е " + i); ) ) class MainActivity ( B subClass = new B(1, 2); subClass.show(); )

В резултат на това трябва да видим в конзолата:

I от суперклас е 1 i в подклас е 2

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

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

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

Не бъркайте пренатоварването с претоварването. Замяната на метода се извършва само ако имената и сигнатурите на типа на двата метода са идентични. В противен случай двата метода просто са претоварени.

Анотацията се появи в Java SE5 @Override;. Ако трябва да замените метод, използвайте @Override и компилаторът ще изведе грешка, ако случайно го претоварите, вместо да го замените.

В Java можете да наследявате само от един клас.

Капсулиране

В компютърните науки капсулирането (на латински: en capsula) е опаковането на данни и/или функции в един обект.

Основата на капсулирането в Java е класът. Капсулирането означава, че полетата на даден обект не са директно достъпни за неговите клиенти - те са скрити от директен достъп отвън. Капсулирането защитава данните на обекта от нежелан достъп, като позволява на обекта да контролира достъпа до своите данни.

Модификатори за достъп

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

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

Препоръчително е да използвате достъп до свойствата на класа само чрез неговите методи (принципът бобкласове, "POJO"), което ви позволява да валидирате стойности на полета, тъй като директният достъп до свойствата е изключително труден за проследяване, което означава, че може да им бъдат присвоени неправилни стойности на етапа на изпълнение на програмата. Този принцип се отнася до управлението на капсулирани данни и ви позволява бързо да промените начина, по който данните се съхраняват. Ако данните се съхраняват не в паметта, а във файлове или база данни, тогава ще трябва да се променят само няколко метода от един клас, вместо да се въвежда тази функционалност във всички части на системата.

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

Пример за просто описание на робот

Публичен клас Robot ( private double x = 0; // Текуща X координата private double y = 0; // Текуща Y координата private double course = 0; // Текущ курс (в градуси) public double getX() ( return x; ) public void setX(double x) ( this.x = x; ) public double getY() ( return y; ) public void setY(double y) ( this.y = y; ) public double getCourse() ( return course; ) // Определяне на курса public void setCourse(double course) ( this.course = course; ) // Преминаване към разстояние public void forward(int distance) ( // Достъп до обектното поле X x = x + distance * Math.cos ( курс / 180 * Math.PI); // Достъп до полето на обекта Y y = y + разстояние * Math.sin(курс / 180 * Math.PI); ) // Отпечатване на координати на робота public void printCoordinates() ( System .out .println(x + "," + y); ) )

Представеният пример за робот използва набори от методи, започващи с комплектИ получавам. Тази двойка методи често се нарича сетер/гетер. Тези методи се използват за достъп до полетата на обект. Имената на методите завършват с името на полето, започващо с ГЛАВНА буква.

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

This.course = курс ...

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

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

Полиморфизмът е една от основните концепции в обектно-ориентираното програмиране, заедно с наследяването и капсулирането. Думата полиморфизъм е от гръцки произход и означава „имам много форми“. За да разберете какво означава полиморфизъм във връзка с обектно-ориентираното програмиране, помислете за пример за създаване на векторен графичен редактор, в който е необходимо да се използват редица класове под формата на набор от графични примитиви - Квадрат, Линия, кръг, Триъгълники т.н. Всеки от тези класове трябва да има дефиниран метод рисувамза показване на съответния примитив на екрана.

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

Човек, който не е запознат с полиморфизма, най-вероятно ще създаде няколко масива: отделен масив за всеки примитивен тип и ще напише код, който итерира елементите от всеки масив последователно и извиква метода за рисуване на всеки елемент. Резултатът ще бъде приблизително следния код:

// Дефиниране на масиви от графични примитиви Square s = new Square ; Ред l = нов ред; Кръг c = нов кръг; Триъгълник t = нов триъгълник; // Попълване на всички масиви с подходящите обекти. . . // Цикъл, итериращ през всички клетки на масива. за (int i = 0; i< s.length; i++){ // вызов метода draw() в случае, если ячейка не пустая. if (s[i] != null) s.draw(); } for(int i = 0; i < l.length; i++){ if (l[i] != null) l.draw(); } for(int i = 0; i < c.length; i++){ if (c[i] != null) c.draw(); } for(int i = 0; i < t.length; i++){ if (t[i] != null) t.draw(); }

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

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

Публичен клас Shape ( public void draw() ( System.out.println("Stub"); ) )

След това създаваме различни класове наследници: квадрат, линия, кръг и триъгълник:

Публичен клас Point разширява Shape ( public void draw() ( System.out.println("Square"); ) ) public class Line extends Shape ( public void draw() ( System.out.println("Line"); ) ) публичен клас Circle разширява Shape ( public void draw() ( System.out.println("Circle"); ) ) public class Triangle разширява Shape ( public void draw() ( System.out.println("Triangle"); ) )

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

Сега нека проверим удивителната характеристика на полиморфизма:

// Дефиниране и инициализиране на масива Shape a = new Shape (new Shape(), new Triangle(), new Square(), new Circle()); // Преминаване през елементи на масив for(int i = 0; i< a.length; i++) { a[i].draw(); }

Следните редове ще бъдат отпечатани на конзолата:

Празен триъгълник квадратен кръг

По този начин всеки наследствен клас извика свой собствен метод за изтегляне, вместо да извика метода за изтегляне от родителския клас Shape.

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

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

В процедурното програмиране съществува и концепцията за полиморфизъм, която се различава от разглеждания механизъм в ООП. Процедурният полиморфизъм предполага възможността за създаване на няколко процедури или функции с едно и също име, но различен брой или типове предавани параметри. Такива функции със същото име се наричат ​​претоварени, а самото явление се нарича претоварване. Претоварване на функции също съществува в ООП и се нарича претоварване на метод. Пример за използване на претоварване на метод в езика Java е класът PrintWriter, който се използва по-специално за отпечатване на съобщения към конзолата. Този клас има много println методи, които се различават по типовете и/или броя на входните параметри. Ето само няколко от тях:

Void println() // преминаване на нов ред void println(boolean x) // отпечатва стойността на булева променлива (true или false) void println(String x) // отпечатва низ - стойността на текстов параметър

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

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

Накратко, значението на полиморфизма може да се изрази с фразата: „Един интерфейс, много реализации“.

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

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

външната общност се проявява като еднакъв набор от методи с еднакви имена и сигнатури (име на методите и типове аргументи и техния брой);

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

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

Класът поток има методи за сериен трансфер на данни. Потокът може да бъде информация, въведена от потребителя от терминал, обмен на данни през компютърна мрежа или файл (ако се изисква последователна обработка на данни, например при разбор на изходните кодове на програми).

В обектно-ориентирани езици

В обектно-ориентираните езици класът е абстрактен тип данни.[Забележка. 1] Полиморфизмът се реализира с помощта на наследяване на класове и виртуални функции. Един дъщерен клас наследява сигнатурите на методите на родителския клас, като имплементацията, в резултат на overriding на метода, на тези методи може да бъде различна, съответстваща на спецификата на наследствения клас. Други функции могат да третират обект като екземпляр на родителски клас, но ако обектът всъщност е екземпляр на дъщерен клас, методът, заменен в дъщерния клас, ще бъде извикан по време на изпълнение. Това се нарича късно свързване. [Примерен случай на употреба би бил обработката на масив, съдържащ екземпляри както на родителски, така и на дъщерен клас: очевидно такъв масив може да бъде деклариран само като масив от типа на родителския клас и само методите на този клас могат да бъдат извикан на обекти от масива, но ако в Ако някои методи са били заменени в класа наследник, тогава в режим на изпълнение за екземпляри на този клас те ще бъдат извикани, а не методите на родителския клас.]

Самият дъщерен клас може да бъде родител. Това ви позволява да изграждате сложни схеми за наследяване - дървовидни или мрежови.

Абстрактните (или чисто виртуални) методи изобщо нямат реализация (всъщност някои езици, като C++, позволяват абстрактни методи да бъдат имплементирани в родителски клас). Те са специално предназначени за наследяване. Тяхната реализация трябва да бъде дефинирана в класове наследници.

Един клас може да наследи функционалност от множество класове. Това се нарича множествено наследяване. Множественото наследяване създава добре познат проблем (в C++), когато даден клас наследява от множество прокси класове, които от своя страна наследяват от същия клас (т.нар. „Диамантен проблем“): ако общ метод на предшественик е бил заменен в проксито , не е известно коя реализация на метода трябва да бъде наследена от общ потомък. Този проблем се решава чрез отказ от множествено наследяване за класове и разрешаване на множествено наследяване за напълно абстрактни класове (тоест интерфейси) (C#, Delphi, Java) или чрез виртуално наследяване (C++).

На функционални езици

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

Полиморфизъм (програмиране)

Накратко, значението на полиморфизма може да се изрази с фразата: „Един интерфейс, много реализации“.

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

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

  • външната общност се проявява като един и същ набор от методи с еднакви имена и сигнатури (имена на методи, типове аргументи и техния брой);
  • вътрешна общност - еднаква функционалност на методите. Тя може да бъде описана интуитивно или изразена под формата на строги закони, правила, на които методите трябва да се подчиняват. Възможността за присвояване на различни функции на един метод (функция, операция) се нарича претоварване на метода (претоварване на функцията, претоварване на операциите).

Примери

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

На функционални езици

В Haskell има два вида полиморфизъм - параметричен (чист) и специален (базиран на класове). Специален също се нарича ad hoc(от латински ad hoc - специално). Те могат да бъдат разграничени, както следва:

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

Специален полиморфизъм

Haskell има разделение на класове и инстанции, което не се среща в OOP. Класът дефинира набор и сигнатури от методи (може би предоставящи имплементации по подразбиране за някои или всички от тях) и екземплярите ги прилагат. Така проблемът с множественото наследяване автоматично отпада. Класовете не наследяват или заместват методи на други класове - всеки метод принадлежи само на един клас. Този подход е по-прост от сложната диаграма на класовите връзки в ООП. Даден тип данни може да принадлежи към няколко класа; един клас може да изисква всеки от неговите типове задължително да принадлежи към друг клас или дори няколко; същото изискване може да бъде поставено от инстанция. Това са аналози на множественото наследяване. Има и някои свойства, които нямат аналози в OOP. Например, прилагането на списък като екземпляр на класа от сравними количества изисква елементите на списъка също да принадлежат към класа от сравними количества.

Програмистите, преминаващи от ООП към FP, трябва да са наясно с важната разлика между техните класови системи. Ако в OOP класът е „свързан“ с обект, т.е. с данни, тогава във FP той е обвързан с функция. Във FP информацията за членството в клас се предава, когато се извиква функция, вместо да се съхранява в полетата на обекта. Този подход, по-специално, ни позволява да решим проблема с метод от няколко обекта (в ООП методът се извиква на един обект). Пример: методът на добавяне (числа, низове) изисква два аргумента, и двата от един и същи тип.

Неявно въвеждане

Някои езици за програмиране (например Python и Ruby) използват така нареченото duck typing (други имена: латентно, имплицитно), което е вид полиморфизъм на подписа. Така например в Python полиморфизмът не е непременно свързан с наследяването.

Форми на полиморфизъм

Статичен и динамичен полиморфизъм

(споменато в класическата книга на Сътър и Александреску, която е източникът).

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

В един случай специфичното значение на даден фрагмент зависи от средата, в която е изграден кодът. Това е т.нар статичен полиморфизъм. Претоварването на функциите и шаблоните в C++ прилагат статичен полиморфизъм. Ако, например, std::sort се извика в кода на шаблонен клас, тогава истинското значение на извикването зависи от това за кой тип параметри ще бъде разгърнат този шаблон - един от std::sort ще бъде извикан .

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

Полиморфизъм на включване

Този полиморфизъм се нарича чист полиморфизъм. Чрез използването на тази форма на полиморфизъм, свързаните обекти могат да се използват по общ начин. Използвайки полиморфизъм на заместване и включване, можете да напишете един метод за работа с всички типове обекти на TPerson. Използвайки полиморфизъм на включване и заместване, можете да работите с всеки обект, който преминава теста „е-А“. Полиморфизмът на включването опростява работата по добавяне на нови подтипове към програма, тъй като няма нужда да добавяте конкретен метод за всеки нов тип; можете да използвате съществуващ, само като промените поведението на системата в него. С полиморфизма можете да използвате повторно базов клас; използвайте всеки наследник или методи, които използва базовият клас.

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

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

Параметрични методи

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

Параметрични типове

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

Отмяна на полиморфизма

Абстрактните методи често се наричат ​​​​отложени методи. Класът, в който е дефиниран този метод, може да извика метода и полиморфизмът гарантира, че подходящата версия на отложения метод се извиква в дъщерни класове. Ad hoc полиморфизмът позволява специална реализация за всеки тип данни.

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

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

Сравнение на полиморфизма във функционалното и обектно-ориентираното програмиране

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

Полиморфизмът е доста изолирано свойство на езика за програмиране. Например, класовете в C++ първоначално са били внедрени като препроцесор за C. За Haskell има алгоритъм за преобразуване на програми, които използват специален полиморфизъм, в програми с изключително параметричен полиморфизъм.

Въпреки концептуалните разлики между системите от класове в FP и OOP, те се реализират по приблизително един и същ начин - с помощта на таблици с виртуални методи.Често използвани в Java.

Вижте също

Връзки

Бележки


Фондация Уикимедия. 2010 г.

Вижте какво е "Полиморфизъм (програмиране)" в други речници:

    Този термин има други значения, вижте Полиморфизъм. Полиморфизмът на компютърните вируси (на гръцки: πολυ много + на гръцки: μορφή форма, външен вид) е специална техника, използвана от авторите на злонамерен софтуер... ... Wikipedia

    В обектно-ориентираното програмиране, способността на даден обект да избере правилния метод в зависимост от типа на данните, получени в съобщението. На английски: Polymorphism Вижте също: Обектно-ориентирано програмиране Финансов речник... ... Финансов речник

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

ДОБРЕ. Полиморфизмът никога не трябва да се разглежда отделно от други фундаментални понятия - абстракция, капсулиране и наследяване. Обектът и други подобни са прикрепени от аксиоми (въпреки че това също са аксиоми).

Всъщност, нека си представим чаша, чаша, чайник, кафе машина, велосипед и скейтборд наблизо. Какво е общото между всички тях? Е, поне че съществуват. Тоест, това са обекти, които са създадени. Но как са създадени? Най-вероятно във фабриката на производителя според чертежите. Добре, нека наречем конструктора чертеж. Е, какво ще кажете за класа? И какво е? Но не е в нашата вселена – тази същност е абстракция, която живее само в нашите мисли. В реалния свят не съществува и никога няма да съществува, такава е физиката - не я интересува, че птиците и бозайниците имат далечни роднини - тя предоставя само възможността за естествен подбор. И ние, хората, намираме роднини един за друг.

Сортирахме предмети и класове, но какво да кажем за нашите очила и велосипеди? Вече разбрахме, че всичко е обект, тоест грубо казано, всички обекти могат да бъдат наследени от някакъв суперпредшественик, суперклас, който е имплементиран в някои езици. Но какво още е общото между скейтборда и чашата например? Разбира се, можете да отидете по-дълбоко и да приемете, че всички те са направени от молекули и всички те са направени от твърди вещества. Това обаче са глупости и SRSG, така че отговорът е прост - нищо. Тоест, това са напълно различни обекти с напълно различна функционалност. Освен това, естествено, компютърните модели и йерархии ще бъдат много различни от физиката и химията. И това е нормално, въпросът за адекватността на моделите се поставя само когато моделът е неадекватен, а дотогава можеш да отрежеш всичко, стига да работи.

Тук. Имаме суперпредшественик Object, от който всички обекти наследяват по подразбиране. Нека приемем, че фактът, че обектите се състоят от атоми, е това, което всички обекти наследяват. Но всички добавки и редакции са полиморфизъм. И така, направихме колела от атоми и ги прикрепихме към дъска - добре, това е скейтборд. Можете да застанете върху него и да се търкаляте, или да се извиете силно и да полетите на три метра над земята, просто излъчвайки светлото си его. Докато стъклото е мястото, където сме оформили плътен контейнер от атоми, от който водата не се излива под въздействието на гравитацията. А директното използване на чаша е да налеете вода и да я наклоните върху устата си, така че водата да тече директно в стомаха. Това правят истинските момчета, без да се притесняват от хълцане или страх от удавяне, така че ето го - полиморфизъм.

Но какво да кажем за останалите? Имаме също абстракция, капсулиране и наследяване. Добре, да започнем с наследството, то е най-близкото. Какво общо имаме между чаша и чаша? Е, можете да налеете вода и в двете, но чашата има дръжка, за която да се хванете. Тоест можете да излезете с определен общ клас - капацитет. Какъв обаче е този клас? Например, можете да вземете чаша за този клас, тогава всички контейнери са стъкла по подразбиране, а всичко останало е модифицирани стъкла. Но някои хора предпочитат кани, например, някои мацки ги носят на главата си, като си мислят, че е удобно. Е, нека ги носят, но някак трябва да решат кое е по-важно и идеално. И така - недостижимият идеал е основният - това се нарича абстрактен клас. Тоест контейнер, който не може да бъде създаден, за който няма пълен чертеж. И всички чертежи, които са разширени до пълна форма, са наследени класове от капацитетния клас.

Тук стигаме до абстракцията. Това йерархично наследяване ни води до може би основната идея на ООП. Така че взехме и подчертахме всичко, където може да се излее вода, в отделен клас, начертахме общ чертеж, но не го завършихме конкретно, оставяйки празнина за бъдещите създатели, и нарекохме чертежа контейнер. В продължение на хиляди години те са изобретили всички светове и са създали свои собствени контейнери, един по-добър от друг. За различните хора е различно, разбира се. Но групирането на стъклени молекули по определен начин всеки път не е лесна задача. Затова занаятчиите прибягнаха до трик: създадоха таен съвет на занаятчиите по света и решиха да споделят постиженията си помежду си. Тоест, създайте малки рисунки и декларирайте като клас, например, усукана писалка във формата на лента на Мьобиус, например. Може би такава писалка е удобна само за извънземни същества, но рисунката е създадена и може да се използва при създаване на собствена рисунка. По този начин ние се абстрахираме от задачата на ниско ниво за „формиране на контейнери чрез преместване на молекули“ до „конструиране на контейнер чрез комбиниране на части и елементи“. Това е абстракция.

Но стигаме до последната точка - капсулиране. Тя е неделима от абстракцията и всъщност благодарение на нея работи. Капсулирането е вид лепило (или синя лента), което се използва за залепване на различни рисунки в едно. Тоест, комбинирането на части за създаване на ваши собствени е капсулиране. Освен това, когато комбинираме, може да не опишем детайлите на тази комбинация (т.е. членовете на класа могат да бъдат частни), като по този начин помагаме да се абстрахираме тези, които използват този чертеж. Нека да разгледаме чайника - какво е това? Това е чаша (или чаша), към която е залепен нагревателен елемент на дъното (или може би вътре в средата?). При преминаване на ток през него, съгласно закона на Ом, капсулиран в нагревателния елемент, ще се освободи топлина и водата ще се нагрее. Какво ще кажете за кафе машина? Това е много по-сложно устройство, с много помпи, контейнери, въздушни шлюзове, мелници и котли. И всичко е залепено с лепило. Или може би синя електрическа лента. Това отново е капсулиране.

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


Близо