Przeczytałem kilka artykułów na temat polimorfizmu, które mogłem znaleźć w Internecie . Ale myślę, że nie mogłem do końca pojąć jego znaczenia i wagi. Większość artykułów nie mówi, dlaczego jest to ważne i jak mogę osiągnąć polimorficzne zachowanie w OOP (oczywiście w JavaScript).
Nie mogę podać żadnego przykładu kodu, ponieważ nie mam pomysłu, jak go zaimplementować, więc moje pytania są poniżej:
- Co to jest?
- Dlaczego tego potrzebujemy?
- Jak to działa?
- Jak mogę osiągnąć to polimorficzne zachowanie w javascript?
Mam ten przykład. Ale jest łatwo zrozumiałe, jaki będzie wynik tego kodu. Nie daje jasnego pojęcia o samym polimorfizmie.
function Person(age, weight) {
this.age = age;
this.weight = weight;
this.getInfo = function() {
return "I am " + this.age + " years old " +
"and weighs " + this.weight +" kilo.";
}
}
function Employee(age, weight, salary) {
this.salary = salary;
this.age = age;
this.weight = weight;
this.getInfo = function() {
return "I am " + this.age + " years old " +
"and weighs " + this.weight +" kilo " +
"and earns " + this.salary + " dollar.";
}
}
Employee.prototype = new Person();
Employee.prototype.constructor = Employee;
// The argument, 'obj', can be of any kind
// which method, getInfo(), to be executed depend on the object
// that 'obj' refer to.
function showInfo(obj) {
document.write(obj.getInfo() + "<br>");
}
var person = new Person(50,90);
var employee = new Employee(43,80,50000);
showInfo(person);
showInfo(employee);
Odpowiedzi:
Polimorfizm jest jednym z założeń programowania obiektowego (OOP). Jest to praktyka projektowania obiektów w celu współdzielenia zachowań i nadpisywania wspólnych zachowań konkretnymi. Polimorfizm wykorzystuje dziedziczenie, aby tak się stało.
W OOP wszystko jest zamodelowane jako obiekt. Tę abstrakcję można sprowadzić do śrub i nakrętek do samochodu lub tak szerokiego, jak po prostu typ samochodu z rocznikiem, marką i modelem.
Aby mieć polimorficzny scenariusz samochodu, istniałby podstawowy typ samochodu, a następnie byłyby podklasy, które odziedziczyłyby po samochodzie i zapewniałyby własne zachowania oprócz podstawowych zachowań, jakie miałby samochód. Na przykład podklasą może być TowTruck, która nadal będzie miała roczną markę i model, ale może również mieć dodatkowe zachowania i właściwości, które mogą być tak podstawowe jak flaga dla IsTowing, tak skomplikowane, jak specyfika windy.
Wracając do przykładu ludzi i pracowników, wszyscy pracownicy są ludźmi, ale nie wszyscy są pracownikami. To znaczy, że ludzie będą superklasą, a pracownicy podklasy. Ludzie mogą mieć wiek i wagę, ale nie mają pensji. Pracownicy to ludzie, więc z natury będą mieli wiek i wagę, ale także dlatego, że są pracownikami, będą mieli pensję.
Aby to ułatwić, najpierw napiszemy superklasę (Person)
function Person(age,weight){ this.age = age; this.weight = weight; }
Damy Person możliwość dzielenia się swoimi informacjami
Person.prototype.getInfo = function(){ return "I am " + this.age + " years old " + "and weighs " + this.weight +" kilo."; };
Następnie chcielibyśmy mieć podklasę Osoba, Pracownik
function Employee(age,weight,salary){ this.age = age; this.weight = weight; this.salary = salary; } Employee.prototype = new Person();
Zastąpimy zachowanie getInfo, definiując takie, które jest bardziej odpowiednie dla pracownika
Employee.prototype.getInfo = function(){ return "I am " + this.age + " years old " + "and weighs " + this.weight +" kilo " + "and earns " + this.salary + " dollar."; };
Można ich używać podobnie do oryginalnego kodu
var person = new Person(50,90); var employee = new Employee(43,80,50000); console.log(person.getInfo()); console.log(employee.getInfo());
Jednak nie wiele zyskuje się na dziedziczeniu, ponieważ konstruktor Pracownika jest tak podobny do osoby, a jedyna funkcja w prototypie jest nadpisywana. Siła w projektowaniu polimorficznym polega na dzieleniu zachowań.
źródło
getInfo
które będzie dotyczyło pracownika, ponieważ znajduje się wyżej w łańcuchu niż osoby. To właśnie miałem na myśli, kiedy powiedziałem „zastąpiony”.return Person.prototype.getInfo.call(this) + + "and earns " + this.salary + " dollar.";
Zamiast kopiować wklej kod ponownie użyć.Jak wyjaśniono w tej innej odpowiedzi , polimorfizm ma różne interpretacje.
Najlepszym wyjaśnieniem na ten temat, jakie kiedykolwiek czytałem, jest artykuł Luca Cardelli , uznanego teoretyka typów. Artykuł nosi tytuł O zrozumieniu typów, abstrakcji danych i polimorfizmie .
Co to jest?
Cardelli definiuje w tym artykule kilka typów polimorfizmu:
Być może w JavaScript nieco trudniej jest dostrzec skutki polimorfizmu, ponieważ bardziej klasyczne typy polimorfizmu są bardziej widoczne w statycznych systemach typów, podczas gdy JavaScript ma dynamiczny system typów.
Na przykład nie ma przeciążania metod lub funkcji ani automatycznego koercji typu w czasie kompilacji w JavaScript. W dynamicznym języku większość z tych rzeczy traktujemy jako coś oczywistego. Nie potrzebujemy też czegoś takiego jak polimorfizm parametryczny w JavaScript ze względu na dynamiczną naturę języka.
Mimo to, JavaScript ma formę dziedziczenia typów, które emulują te same idee polimorfizmu podtypów (sklasyfikowanych powyżej jako polimorfizm inkluzji przez Cardellego) w podobny sposób, jak to zwykle robimy w innych językach programowania obiektowego, takich jak Java lub C # (jak wyjaśniono w inna odpowiedź, którą udostępniłem powyżej).
Inną formą polimorfizmu, bardzo typową dla języków dynamicznych, jest typowanie kacze .
Błędem jest sądzić, że polimorfizm jest związany tylko z programowaniem obiektowym. Inne modele programowania (funkcjonalne, proceduralne, logiczne itp.) Oferują różne formy polimorfizmu w swoich systemach typów, prawdopodobnie w sposób nieco nieznany tym, które są używane tylko do OOP.
Dlaczego tego potrzebujemy?
Polimorfizm wspiera wiele dobrych cech w oprogramowaniu, między innymi sprzyja modułowości i możliwości ponownego użycia oraz sprawia, że system typów jest bardziej elastyczny i plastyczny. Bez tego naprawdę trudno byłoby wnioskować o typach. Polimorfizm zapewnia, że jeden typ można zastąpić innym kompatybilnym, pod warunkiem, że spełnia wymagania publicznego interfejsu, co sprzyja również ukrywaniu informacji i modułowości.
Jak to działa?
Odpowiedź na to pytanie nie jest łatwa, różne języki mają różne sposoby jej implementacji. W przypadku JavaScript, jak wspomniano powyżej, zobaczysz, że materializuje się on w postaci hierarchii typów wykorzystujących dziedziczenie prototypowe, a także możesz go wykorzystać za pomocą pisania kaczego.
Temat jest trochę obszerny i otworzyłeś dwa pytania w jednym poście. Być może najlepiej będzie, jeśli zaczniesz od przeczytania artykułu Cardellego, a następnie spróbujesz zrozumieć polimorfizm niezależnie od jakiegokolwiek języka lub paradygmatu programowania, a następnie zaczniesz tworzyć skojarzenia między koncepcjami teoretycznymi a tym, co ma do zaoferowania dowolny język, taki jak JavaScript, aby zrealizować te pomysły.
źródło
Jaki jest cel polimorfizmu?
Polimorfizm sprawia, że system typu statycznego jest bardziej elastyczny bez utraty (znacznego) bezpieczeństwa typu statycznego poprzez poluzowanie warunków równoważności typów. Dowód pozostaje, że program będzie działał tylko wtedy, gdy nie będzie zawierał żadnych błędów typu.
Funkcja polimorficzna lub typ danych jest bardziej ogólny niż monomorficzny, ponieważ może być używany w szerszym zakresie scenariuszy. W tym sensie polimorfizm reprezentuje ideę uogólnienia w ściśle typizowanych językach.
Jak to się ma do Javascript?
Javascript ma słaby, dynamiczny system typów. Taki system typów jest równoważny ze ścisłym systemem typów zawierającym tylko jeden typ. Możemy myśleć o takim typie jako o ogromnym typie unii (pseudo składni):
type T = | Undefined | Null | Number | String | Boolean | Symbol | Object | Array | Map | ...
Każda wartość zostanie skojarzona z jedną z alternatyw tego typu w czasie wykonywania. A ponieważ JavaScript jest napisany słabo, każda wartość może zmienić swój typ dowolną liczbę razy.
Jeśli przyjmiemy perspektywę teoretyczną typu i weźmiemy pod uwagę, że istnieje tylko jeden typ, możemy z całą pewnością stwierdzić, że system typów JavaScript nie ma pojęcia polimorfizmu. Zamiast tego mamy pisanie typu kaczego i niejawne wymuszanie typu.
Ale to nie powinno powstrzymywać nas od myślenia o typach w naszych programach. Ze względu na brak typów w Javascript musimy je wywnioskować podczas procesu kodowania. Nasz umysł musi zastępować brakujący kompilator, tj. Gdy tylko spojrzymy na program, musimy rozpoznać nie tylko algorytmy, ale także podstawowe (być może polimorficzne) typy. Te typy pomogą nam tworzyć bardziej niezawodne i solidniejsze programy.
Aby zrobić to właściwie, przedstawię przegląd najczęstszych przejawów polimorfizmu.
Polimorfizm parametryczny (inaczej rodzajowe)
Polimorfizm parametryczny mówi, że różne typy są wymienne, ponieważ typy w ogóle nie mają znaczenia. Funkcja, która definiuje jeden lub więcej parametrów typu parametrycznego polimorficznego, nie może nic wiedzieć o odpowiednich argumentach, ale traktować je tak samo, ponieważ można je zaadaptować do dowolnego typu. Jest to dość restrykcyjne, ponieważ taka funkcja może działać tylko z tymi właściwościami jej argumentów, które nie są częścią ich danych:
// parametric polymorphic functions const id = x => x; id(1); // 1 id("foo"); // "foo" const k = x => y => x; const k_ = x => y => y; k(1) ("foo"); // 1 k_(1) ("foo"); // "foo" const append = x => xs => xs.concat([x]); append(3) ([1, 2]); // [1, 2, 3] append("c") (["a", "b"]); // ["a", "b", "c"]
Polimorfizm ad-hoc (znany również jako przeciążenie)
Polimorfizm ad hoc mówi, że różne typy są równoważne tylko w określonym celu. Aby być równoważnym w tym sensie, typ musi implementować zestaw funkcji specyficznych dla tego celu. Funkcja, która definiuje jeden lub więcej parametrów typu polimorficznego ad hoc, musi wtedy wiedzieć, które zestawy funkcji są skojarzone z każdym z jej argumentów.
Polimorfizm ad-hoc sprawia, że funkcja jest zgodna z większą domeną typów. Poniższy przykład ilustruje cel „mapowania” i sposób, w jaki typy mogą implementować to ograniczenie. Zamiast zestawu funkcji, ograniczenie „mapowalne” obejmuje tylko jedną
map
funkcję:// Option type class Option { cata(pattern, option) { return pattern[option.constructor.name](option.x); } map(f, opt) { return this.cata({Some: x => new Some(f(x)), None: () => this}, opt); } }; class Some extends Option { constructor(x) { super(x); this.x = x; } }; class None extends Option { constructor() { super(); } }; // ad-hoc polymorphic function const map = f => t => t.map(f, t); // helper/data const sqr = x => x * x; const xs = [1, 2, 3]; const x = new Some(5); const y = new None(); // application console.log( map(sqr) (xs) // [1, 4, 9] ); console.log( map(sqr) (x) // Some {x: 25} ); console.log( map(sqr) (y) // None {} );
Polimorfizm podtypu
Ponieważ inne odpowiedzi obejmują już polimorfizm podtypów, pomijam je.
Strukturalny polimorfizm (inaczej podtyp strutrualny)
Polimorfizm strukturalny mówi, że różne typy są równoważne, jeśli zawierają tę samą strukturę w taki sposób, że jeden typ ma wszystkie właściwości drugiego, ale może zawierać dodatkowe właściwości. Biorąc to pod uwagę, polimorfizm strukturalny jest typowaniem typu kaczego w czasie kompilacji i z pewnością zapewnia dodatkowe bezpieczeństwo typów. Ale twierdząc, że dwie wartości są tego samego typu tylko dlatego, że mają pewne wspólne właściwości, całkowicie ignoruje semantyczny poziom wartości:
const weight = {value: 90, foo: true}; const speed = {value: 90, foo: false, bar: [1, 2, 3]};
Niestety
speed
uważany jest za podtypweight
i gdy tylko porównamyvalue
właściwości, praktycznie porównujemy jabłka z pomarańczami.źródło
len
. A możeconj
z ubrań.Poly = wiele, morfizm = zmiana formy lub zachowania.
W programowaniu jest używany, gdy chcemy, aby interfejs funkcji (powiedzmy funkcji X) był wystarczająco elastyczny, aby akceptować różne typy lub liczbę parametrów. Ponadto, w oparciu o zmianę typów parametrów lub liczb, możemy chcieć, aby funkcja X zachowywała się inaczej (morfizm).
Piszemy wiele implementacji funkcji X, w których każda implementacja akceptuje różne typy parametrów lub liczbę parametrów. Na podstawie typu lub liczby parametrów kompilator (w czasie wykonywania) decyduje, która implementacja X powinna zostać wykonana, gdy X jest wywoływany z jakiegoś kodu.
JS nie jest językiem typizowanym, więc tak naprawdę nie miał na celu używania pojęć OOP, takich jak polimorfizm. Jednak nowsza wersja JS zawiera teraz klasy i istnieje możliwość, że polimosfizm może zacząć mieć sens również w JS. Inne odpowiedzi zawierają kilka interesujących obejść.
źródło
Polimorfizm oznacza możliwość wywołania tej samej metody na różnych obiektach, a każdy obiekt reaguje w inny sposób, nazywany jest POLIMORFIZMEM .
function Animal(sound){ this.sound=sound; this.speak=function(){ return this.sound; } } //one method function showInfo(obj){ console.log(obj.speak()); } //different objects var dog = new Animal("woof"); var cat = new Animal("meow"); var cow = new Animal("humbow"); //responds different ways showInfo(dog); showInfo(cat); showInfo(cow);
źródło
JavaScript jest językiem interpretowanym, a nie kompilowanym.
Polimorfizm czasu kompilacji (lub polimorfizm statyczny) Polimorfizm czasu kompilacji to nic innego jak przeciążanie metod w java, c ++
Tak więc przeciążanie metod nie jest możliwe w javascript.
Ale polimorfizm dynamiczny (w czasie wykonywania) jest polimorfizmem istniejącym w czasie wykonywania, więc nadpisywanie metody jest możliwe w javascript
innym przykładem jest PHP.
źródło