Polimorfizm (informatyka)

W informatyce i typu teorii , polimorfizm , od starożytnych greckich Polus (wielu) i morphe (formularz), jest koncepcja zapewniając jednolity interfejs do podmiotów, które mogą mieć różne typy . Na przykład operacje takie jak mnożenie można w ten sposób rozszerzyć na skalary na wektory lub macierze , dodawanie, skalary na funkcje lub łańcuchy itp. Istnieje kilka zasadniczo różnych rodzajów polimorfizmów:

Rodzaje polimorfizmu
Sortować Nazwa w programowaniu obiektowym Nazwa w programowaniu funkcjonalnym Opis
polimorfizm ad hoc przeciążać Pojedynczy interfejs jest realizowane kilka procedur mających ten sam identyfikator , potencjalnie heterogenicznej, to znaczy każdy szablon mający różne kombinacje różnych typów na jej parametry , czy jest on członkiem klasy lub nie (Global rutynowych).
sparametryzowany polimorfizm programowanie ogólne wielopostaciowość Pojedynczy interfejs jest realizowane przez jednego rutyny używając typu rodzajowego dla co najmniej jednego z jego parametrów , czy procedura jest członkiem klasy czy nie (globalny rutyna).
polimorfizm przez podtypowanie ( dziedziczenie ) polimorfizm przez dziedziczenie polimorfizm inkluzji Unikalny interfejs jest realizowane przez człon rutyny posiadające ten sam identyfikator w każdej klasie należącego do tej samej hierarchii dziedziczenia.

W zależności od używanego języka komputerowego , polimorfizm można osiągnąć różnymi środkami, nieodłącznie związanymi z językiem lub poprzez wykorzystanie wzorców projektowych .

Polimorfizm ad hoc

Polimorfizm ad hoc lub przeciążanie funkcji polega na zadeklarowaniu kilku funkcji lub metod o tej samej nazwie, ale różnych parametrach (według ich liczby lub rodzaju). W zależności od tego, czy język jest typowany statycznie czy dynamicznie, określenie poprawnej implementacji dla danych parametrów odbywa się w czasie kompilacji lub w czasie wykonywania. Oto przykład przeciążenia w C++  :

#include <string> #include <iostream> void fonctionSurchargee(std::string str) { std::cout << "Implémentation 1 (string)" << std::endl; } void fonctionSurchargee(int x) { std::cout << "Implémentation 2 (int)" << std::endl; } int main(int argc, char *argv[]) { fonctionSurchargee("Hello World!"); // Affiche : Implémentation 1 (string) fonctionSurchargee(42); // Affiche : Implémentation 2 (int) return 0; }

Sparametryzowany polimorfizm

Sparametryzowany polimorfizm polega na zdefiniowaniu funkcji, które można zastosować do sparametryzowanych typów . Na przykład, możliwe jest zdefiniowanie tej samej funkcji concat pozwalającej na łączenie dwóch list niezależnie od rodzaju zawartych w nich danych. W tym przypadku odnotowywany jest typ funkcji concat :

gdzie α jest parametrem reprezentującym typ danych zawartych na listach. α może odpowiadać typom całkowitym, rzeczywistym, łańcuchowym, listowym β itp.

Sparametryzowany polimorfizm jest używany przez języki Caml , Haskell , PureScript i Scala . Na przykład w Haskell funkcja takezwraca pierwszych n elementów listy. Specyfikacja funkcji zwracanej przez interpreter to:

take :: Int -> [a] -> [a]

Typ a jest zmienną typu, która pasuje do dowolnego typu, więc jest to funkcja obsługująca polimorfizm parametryczny.

Kilka języków obiektowych implementuje sparametryzowany polimorfizm, na przykład Java z typami generycznymi i C++ z szablonami . W tym ostatnim możemy wyrazić funkcję concat jako:

#include <list> template<class a> std::list<a> concat(std::list<a> list1, std::list<a> list2) { return list1.insert(list1.end(),list2.begin(),list2.end()) }

Polimorfizm przez podtypowanie (dziedziczenie)

Tworzenie typów

Chodzi o to, aby zacząć od typu i zmodyfikować go. Na przykład możemy stworzyć klasę bazową , a następnie stworzyć klasy pochodne.

Koncepcja ta związana jest z podejściem obiektowym .

W C++ #include <numbers> class Forme { public: // Méthode virtuelle pure virtual float Aire() = 0; // Une classe de base d’une hiérarchie polymorphe // doit toujours (ou presque) avoir un destructeur virtuel virtual ~Forme() {} }; class Carre : public Forme { public: float Aire() override { return m_cote * m_cote; } private: float m_cote; }; class Cercle : public Forme { public: float Aire() override { return std::numbers::pi<float> * m_rayon * m_rayon; } private: float m_rayon; }; w języku jawajski public abstract class Forme { public abstract float aire() ; } public class Carre extends Forme { private float cote; @override public float aire() { return cote * cote; } } public class Cercle extends Forme { private float rayon; @override public float aire() { return (float) Math.PI*rayon*rayon; } } W Pythonie import math class Forme: def aire(self): raise NotImplementedError() class Carre(Forme): def __init__(self, cote): self.cote = cote def aire(self): return self.cote * self.cote class Cercle(Forme): def __init__(self, rayon): self.rayon = rayon def aire(self): return math.pi * self.rayon * self.rayon W języku C # public abstract class Forme { public abstract float Aire(); } public class Carre : Forme { private float cote; public override float Aire() { return (float) Math.Pow(cote, 2); } } public class Cercle : Forme { private float rayon; public override float Aire() { return (float) ( Math.PI * Math.Pow(rayon, 2) ); } } w Eiffla deferred class FORME feature aire: REAL deferred end end class CARRE inherit FORME feature cote: REAL aire: REAL do Result := cote^2 end end class CERCLE inherit FORME MATH feature rayon: REAL aire: REAL do Result := Pi * rayon^2 end end

Korzystanie z podtypów

Dzięki funkcjom wirtualnym możemy stworzyć algorytm wykorzystujący tylko klasę bazową, która automatycznie wywoła funkcje klas pochodnych. Możemy powiedzieć po prostu, że lista polimorficzna zawiera różne obiekty i zgodnie z typem tych obiektów, gdy używana jest metoda, zostanie ona zastosowana do jednego z obiektów tej listy, zgodnie z jego funkcją.

W C++ template<typename Size> float AireTotal(std::array<Forme*, Size> arr) { float f = 0; for (auto forme : arr) f+= forme->Aire(); // le programme détermine automatiquement quelle fonction appeler return f; } // ... std::array<Forme*, 3> tableau { new Carre, new Cercle, new Carre }; AireTotal(tableau); // ... w języku jawajski float aireTotal(Forme[] tabl) { float s=0; for(Forme forme : tabl) { s += forme.aire(); // le programme détermine automatiquement quelle fonction appeler } return s; } // ... Forme[] tableau = { new Carre(), new Cercle(), new Carre() }; aireT = aireTotal(tableau); //aireT aura été défini comme float // ... W języku C # //... private float _aireT; readonly Forme[] _tableau = { new Carre(), new Cercle(), new Carre() }; //... float AireTotal(Forme[] tabl) { float s = 0; foreach (Forme form in tabl) { s += form.Aire(); // le programme détermine automatiquement quelle fonction appeler } return s; } //... _aireT = AireTotal(_tableau); //... w Eiffla aireTotal (tab: ARRAY [FORME]): REAL do -- Result = 0.0 par défaut from tab.start until tab.after loop Result := Result + tab.item.aire tab.forth end end

Zainteresowanie polimorfizmem

Proponując użycie tej samej nazwy metody dla kilku różnych typów obiektów, polimorfizm umożliwia znacznie bardziej ogólne programowanie. Programista nie musi znać podczas programowania metody dokładnego typu obiektu, na którym metoda zostanie zastosowana. Musi tylko wiedzieć, że ten obiekt zaimplementuje metodę.

Tak więc program do naliczania odsetek dla kont bankowych wyglądałby tak w klasycznym programowaniu (pseudo kod):

case MonCompteBancaire PEA : MonCompteBancaire.calculeInteretPEA PEL : MonCompteBancaire.calculeInteretPEL LivretA : MonCompteBancaire.calculeInteretLivretA end case

Jeśli pojawi się nowy typ konta bankowego PERP (a wraz z nim nowa kalkulacja), konieczne będzie z jednej strony napisanie nowej metody kalkulacjiInteretPERP, ale także zmodyfikowanie wszystkich wywołań powyższej kalkulacji. W najlepszym przypadku zostanie on wyizolowany i udostępniony, dzięki czemu konieczna będzie tylko jedna modyfikacja. W najgorszym przypadku mogą być setki połączeń do modyfikacji.

W przypadku polimorfizmu wszystkie metody będą miały tę samą nazwę „oblicz odsetki”, ale będą miały różne kody (po jednym na typ konta).
Zaproszenie będzie miało formę:

MonCompteBancaire.calculeInteret

Gdy pojawi się nowe konto, nie będzie konieczna modyfikacja tego kodu. Wybór rzeczywistej metody do użycia zostanie dokonany automatycznie w czasie wykonywania przez język, podczas gdy w poprzednim przypadku to programista musiał zaprogramować ten wybór. Ale programista będzie musiał stworzyć dodatkową klasę obiektową, aby zaimplementować odpowiedni kod, który zostanie wywołany przez przypisanie tablicy wirtualnych metod, które rzeczywiście wykonają pudełko zgodnie z tym komponentem.

<img src="https://fr.wikipedia.org/wiki/Special:CentralAutoLogin/start?type=1x1" alt="" title="" width="1" height="1" style="border: none; position: absolute;">