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



страница37/73
Дата25.07.2016
Размер13.53 Mb.
#6732
1   ...   33   34   35   36   37   38   39   40   ...   73

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


Полиморфизмът е третата основна черта на един ООП език, след абстракцията на данните и наследяването.

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

Капсулирането създава нови даннови типове чрез комбиниране на ха­рак­те­ри­сти­ки и поведение. Скриването на реализацията разделя реализацията от ин­тер­фей­са чрез правенето на детайлите private. Този вид механична организация има очевиден смисъл за всеки, който се е занимавал с процедурно про­гра­ми­ра­не. Но полиморфизмът се занимава с разделяне в термините на типове. В пред­на­та глава видяхте, че наследяването прави възможно третирането на обект ка­то неговия си тип или типа на базовия клас. Тази възможност е критично важна по­неже позволява много типове (извлечени от един базов тип) да се третират ка­то че са от един тип и един и същ отрязък код да работи различно в раз­лич­ни­те случаи. Полиморфното извикване на методи позволява да се изрази раз­ли­ка­та от друг, подобен тип, доколкото и двата са извлечени от един и същ базов тип. Това различаване се изразява чрез разликите в поведението на методите, кои­то може да извикате чрез базовия клас.

В тази глава ще учите за полиморфизма (също наричан динамично свързване или късно свързване или свързване по време на изпълнение) започвайки от ос­нов­ното, с примери които махат от погледа всичко освен полиморфното по­ве­де­ние.


Upcasting


В глава 6 видяхме как обект може да се използва с неговия тип или с типа на ба­зо­вия клас. Вземането на обектов манипулатор и третирането му като ма­ни­пу­ла­тор от типа на базовия клас се нарича upcasting поради начина на изо­бра­зя­ва­не на дърветата на наследяването на хартия.

Видяхме също и възникващ проблем, който съществува и в следната програма: (Виж страница 89 ако има проблеми с пускането на програмата.)

//: c07:Music.java

// Inheritance & upcasting

package c07;
class Note {

private int value;

private Note(int val) { value = val; }

public static final Note

middleC = new Note(0),

cSharp = new Note(1),

cFlat = new Note(2);

} // Etc.


class Instrument {

public void play(Note n) {

System.out.println("Instrument.play()");

}

}


// Wind objects are instruments

// because they have the same interface:

class Wind extends Instrument {

// Redefine interface method:

public void play(Note n) {

System.out.println("Wind.play()");

}

}
public class Music {



public static void tune(Instrument i) {

// ...


i.play(Note.middleC);

}

public static void main(String[] args) {



Wind flute = new Wind();

tune(flute); // Upcasting

}

} ///:~


Методът Music.tune( ) приема Instrument манипулатор, но също и каквото и да е извлечено от Instrument. В main( ) може да се види това да става с Wind ма­нипулатора даден на tune( ), без да е необходим каст. Това е приемливо; ин­тер­фейсът в Instrument трябва да съществува в Wind, понеже Wind е наследен от Instrument. Ъпкастингът от Wind към Instrument може да “стесни” този ин­тер­фейс, но не може да го направи по-малък от интерфейса на Instrument.

Защо ъпкастинг?


Тази програма може да ви изглежда странна. Защо трябва нарочно да се забрави типът на обекта? Това се случва при ъпкастинга и много по-пра­во­ли­ней­но действие изглежда tune( ) да вземе просто Wind манипулатор като свой ар­гумент. Това изважда наяве най-важното: Ако го направите така, ще трябва да пишете нов tune( ) за всеки сорт Instrument във вашата система. Да кажем че последваме тази обосновка и добавим Stringed и Brass инструменти:

//: c07:Music2.java

// Overloading instead of upcasting
class Note2 {

private int value;

private Note2(int val) { value = val; }

public static final Note2

middleC = new Note2(0),

cSharp = new Note2(1),

cFlat = new Note2(2);

} // Etc.


class Instrument2 {

public void play(Note2 n) {

System.out.println("Instrument2.play()");

}

}


class Wind2 extends Instrument2 {

public void play(Note2 n) {

System.out.println("Wind2.play()");

}

}


class Stringed2 extends Instrument2 {

public void play(Note2 n) {

System.out.println("Stringed2.play()");

}

}


class Brass2 extends Instrument2 {

public void play(Note2 n) {

System.out.println("Brass2.play()");

}

}


public class Music2 {

public static void tune(Wind2 i) {

i.play(Note2.middleC);

}

public static void tune(Stringed2 i) {



i.play(Note2.middleC);

}

public static void tune(Brass2 i) {



i.play(Note2.middleC);

}

public static void main(String[] args) {



Wind2 flute = new Wind2();

Stringed2 violin = new Stringed2();

Brass2 frenchHorn = new Brass2();

tune(flute); // No upcasting

tune(violin);

tune(frenchHorn);

}

} ///:~


Това работи, но има голям недостатък: Трябва да се пишат специфични за типа ме­тоди за всеки Instrument2 клас който се добави. На първо място това значи по­вече програмиране, но също значи че ако добавите нови методи като tune( ) или нов вид Instrument, ще има много работа за свършване. Като добавим фак­та, че компилаторът няма да издаде никакво съобщение ако забравите да пре­на­то­варите методите си целият процес на работа с методите се вижда явно неу­прав­ляем.

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

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




Сподели с приятели:
1   ...   33   34   35   36   37   38   39   40   ...   73




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

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