Книга е още в много ранна фаза на написване


Взаимозаменяеми обекти с полиморфизъм



страница11/73
Дата25.07.2016
Размер13.53 Mb.
#6732
1   ...   7   8   9   10   11   12   13   14   ...   73

Взаимозаменяеми обекти с полиморфизъм


Наследяването обисновено цели създаване на фамилия класове с един интерфейс. Изразяваме това с диаграма на обърнато дърво:5


Едно от най-важните неща, които могат да се правят с такава фамилия от клесове е, че може да се третира обект от извлечен клас като обект от базов клас. Това е важно, защото значи, че може да се напише единствено парче код, кой­то да игнорира детайлите на типа и да "разговаря" с базовия клас. Кодът е раз­вързан от информацията, кпецифична за типа и с това е по-лесен за на­пис­ва­не и разбиране. И ако нов тип –Triangle, например – се добави чрез на­сле­дя­ва­не, вашият код ще работи също така добре с новия тип (произлязъл от - б.пр.) Shape както и със съществуващите типове. Така програмата е разширяема.

Да видим горния пример. Ако напишете функция на Java:

void doStuff(Shape s) {

s.erase();

// ...


s.draw();

}

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



Circle c = new Circle();

Triangle t = new Triangle();

Line l = new Line();

doStuff(c);

doStuff(t);

doStuff(l);

Извикванията на doStuff( ) автоматично работят точно, без значение точния тип на обекта.

Това е наистина вълнуващ трик. Да видим линията:

doStuff(c);

Тук става следното: манипулатора на Circle е даден на функция, която очаква Shape манипулатор. Доколкото Circle е Shape той може да се третира като едно doStuff( ). Тоест каквото и да е съобщение, което doStuff( ) може да из­прати на Shape, Circle може да го възприеме. Така че написаното е напълно си­гур­но и логично нещо.

Наричаме този процес на третиране на производния клас като базовия upcasting. Думата cast е използвано в смисъла на леене в калъп и up идва от ти­пич­ния начин на рисуване на дървото на наследяванията - с корена нагоре. Така casting-ът към базовия тип е преместване по диаграмата на наследяванията на­го­ре: upcasting. (някои вероятно биха предпочели да използуваме "превръщане към тип" вместо casting -б.пр.)

Една ООП съдържа upcasting някъде, понеже това е начинът по който се раз­върз­вате от необходимостта да знаете на конкретния тип с който се работи. По­глед­нете кода в doStuff( ):

s.erase();

// ...


s.draw();

Забележете че той не казва “Ако е Circle, прави това, ако е Square, прави друго и т.н.” Ако се пише код от този тип, в който се проверява винаги точния тип на Shape, за да се изпълни нещо, такъв код е тежък и трябва да се променя с всяко до­бавяне на тип. В този пример просто казва: “То е Shape, знае се как да го erase( ) , прави се коректно.”


Динамично свързване


Поразителното в doStuff( ) е, че някак си стават правилните неща. Извикването на draw( ) за Circle предизвиква изпълнението на код различен от случая на draw( ) за Square или Line, но когато draw( ) съобщението е изпратено на без­именен Shape, осигурява се коректно поведение според конкретния вид на Shape манипулатора. Това е забележително, защото по време на компилацията doStuff( ) не се знае точния тип. Така че би трябвало да се очаква да се извика erase( ) за Shape, draw( ) за Shape а не за специфичните Circle, Square или Line. И все пак правилните неща се случват. Ето как става това.

Когато се изпрати съобщение на обект без да се знае точният му вид и се по­лу­чи всичко както трябва, това се нарича полиморфизъм. Процесът, изролзуван от ООП езика за да се постигне това се нарича дзинамично свързване. Ком­пи­ла­то­рът и run-time поддръжката осигуряват детайлите; всичко, което трябва да се знае е, че това става и по-важното - как да се организира по този начин.

Някои езици изискват употребата на специална ключова дума за да се получи ди­намично свързване. В C++ тя е virtual.В Java няма нужда да се помни по­доб­на дума, понеже винаги се свързва динамично. Така че може да се смята, че всич­ко ще е както трябва, даже и в случая на upcasting.

Абстрактни базови класове и интерфейси


Често при проектирането се иска да се използува базов клас само заради ин­тер­фейса му. Тоест няма да се създават обекти от него, а само ще се наследи от кла­сове, за да се използува интерфейсът му. Това се постига като се направи кла­сът абстрактен чрез използуването на ключовата дума abstract. Ако някой се опитва да направи обект от клас, който е abstract, компилаторът попречва на то­ва. Това е инструмент с който се налага определен начин на проектиране.

Също може да се използува abstract за да се означи метод, който още не е на­пи­сан - все едно да се каже “ето (име на - бел.пр.) функция за всички на­след­ни­ци, но в този момент още не съм я написал.” Един abstract може да бъде за­да­ден само в abstract клас. Когато такъв клас е наследен, абстрактният метод тряб­ва да се напише, иначе наследникът става също абстрактен (abstract). Съз­да­ването на abstract позволява да се сложи метод в интерфейса без да се пише (въз­можно) безполезен код в него.

Ключовата дума interface придвижва концепцията за abstract клас стъпка на­пред, предотвратявайки въобще всякакви дефиниции на функции. interface е мно­го полезен и често използуван инструмент, понеже дава перфектно от­де­ля­не на интерфейса и приложението. Освен това може да комбинирате много интер­фейси заедно, ако искате. (Не може да наследявате повече от един обик­но­вен class или abstract class.)




Сподели с приятели:
1   ...   7   8   9   10   11   12   13   14   ...   73




©obuch.info 2024
отнасят до администрацията

    Начална страница