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


Избиране на композиция vs. наследяване



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

Избиране на композиция
vs. наследяване


И композицията и наследяването позволяват да се слага подобекти в клас. Мо­же да се чудите за разликата помежду им и кога да изберете едното или дру­го­то.

Изобщо композицията се използва когато искате реализацията на един клас в друг, но не искате интерфейса. Тоест вграждате обект за да го използвате в но­вия си клас, но потребителят на новия клас вижда интерфейса който вие сте опре­делили, а не интерфейса на онзи клас. За тази цел вграждате private обекти от съществуващи класове във вашите нови класове.

Понякога има смисъл да се позволи на потребителя на новия клас направо да има достъп до композицията му; тоест да се направят член-обектите public. Член-обектите използват реализацията скривайки се, така че това е безопасно да се направи и когато потребителят знае, че събирате много части заедно това пра­ви интерфейса лесен за разбиране. car обектът е добър пример:

//: c06:Car.java

// Composition with public objects
class Engine {

public void start() {}

public void rev() {}

public void stop() {}

}
class Wheel {

public void inflate(int psi) {}

}
class Window {

public void rollup() {}

public void rolldown() {}

}
class Door {

public Window window = new Window();

public void open() {}

public void close() {}

}
public class Car {

public Engine engine = new Engine();

public Wheel[] wheel = new Wheel[4];

public Door left = new Door(),

right = new Door(); // 2-door

Car() {

for(int i = 0; i < 4; i++)



wheel[i] = new Wheel();

}

public static void main(String[] args) {



Car car = new Car();

car.left.window.rollup();

car.wheel[0].inflate(72);

}

} ///:~



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

Когато се наследява се взема съществуващ клас и се прави специална негова вер­сия. Изобщо това означава че се взема клас за обща употреба и се спе­циа­ли­зи­ра за конкретни нужди. С малко размисъл ще видите, че е безсмислено да се ком­позира кола с обект "превозно средство" – колата не съдържа превозно сред­ство, тя е превозно средство. Тази е- зависимост се изразява с на­сле­дя­ва­не­то, а има- зависимостта се изразява с композицията.


protected


Сега като сте запознати с наследяването ключовата дума protected най-накрая има значение. В идеалния свят private биха били винаги непроменимо private, но в реалните проекти понякога искаме да направим нещо скрито от широкия свят и същевременно да оставим достъп на наследниците. Ключовата дума protected е съгласие с прагматизма. Тя казва “Това е private що се отнася до по­тре­бителя на класа, но е достъпно за наследниците на класа или за друг от съ­щия package.” Тоест protected в Java автоматично е “приятелски.”

Най-добрия начин е да оставим членовете-данни private – винаги ще запазвате пра­вото си да променяте подлежащата реализация. Може тогава да се позволи на потребителите контролиран достъп чрез protected методи:

//: c06:Orc.java

// The protected keyword

import java.util.*;
class Villain {

private int i;

protected int read() { return i; }

protected void set(int ii) { i = ii; }

public Villain(int ii) { i = ii; }

public int value(int m) { return m*i; }

}
public class Orc extends Villain {

private int j;

public Orc(int jj) { super(jj); j = jj; }

public void change(int x) { set(x); }

} ///:~

Може да видите че change( ) има достъп до set( ) понеже е protected.


Постъпкова разработка


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

Малко изумяващо е колко чисто класовете се разделят. Даже не ви трябва сор­сът за да използвате кода отново. Най-много да импортирате пакет. (Това е в си­ла и за композицията и за наследяването.)

Важно е да се разбере че разработката на програми е постъпков процес, точно как­то човешкото учене. Може да направите всичкия анализ на който сте спо­соб­ни, но още не знаете всичките отговори когато седнете над проекта. Ще има­те много по-голям успех – и по-непосредствена обратна връзка – ако за­поч­не­те да “израствате” своя проект като органическо, еволюционно творение, от­кол­кото ако го направите изведнъж както се прави небостъргач от остъклени ку­тии.

Макар и наследяването за експериментиране да е полезна техника, в някаква точ­ка когато нещата се постабилизират трябва да обгърнете цялата йерархия с на­мерение да се опрости и реорганизира. Запомнете че зад наследяването се крие отношение което казва “Този нов клас е от типа на онзи стар клас.” Ва­ша­та програма не трябва да се занимава със сетване и ресетване на битове, а със създаване на обекти, които се определя какви да бъдат от същината на про­блем­ното пространство.


Upcasting


Най-важният аспект на наследяването не е, че дава методи на новия клас. Това е за­висимостта между новия и стария клас. Тази зависимост може да бъде резю­ми­рана като се каже “новият клас е от типа насъществуващия клас.”

Това описание не е просто фантазьорски начин да се изрази наследяването – то се поддържа направо от езика. Като пример да вземем базов клас наречен Instrument който представя музикалните инструменти и извлечен клас Wind. По­неже наследяването значи че всичките методи на базовия клас са достъпни и за извлечения клас, всяко съобщение изпратено до базовия клас също може да бъ­де изпратено до извлечения клас. Ако класа Instrument има play( ) метод, и Wind инструментите ще имат. Това значи че можем правилно да кажем че Wind обектът е също тип Instrument. Следващия пример показва как ком­пи­ла­то­рът поддържа това нещо:

//: c06:Wind.java

// Inheritance & upcasting

import java.util.*;
class Instrument {

public void play() {}

static void tune(Instrument i) {

// ...


i.play();

}

}


// Wind objects are instruments

// because they have the same interface:

class Wind extends Instrument {

public static void main(String[] args) {

Wind flute = new Wind();

Instrument.tune(flute); // Upcasting

}

} ///:~


Интересното в този пример е tune( ) методът, който приема Instrument ма­ни­пу­латор. Обаче в Wind.main( ) tune( ) се вика като му се дава Wind ма­ни­пу­ла­тор. Като знаем че Java е особено внимателен за проверката на типовете, из­глеж­да странно че метод, който приема един тип приема с готовност и друг тип, докато не разберем че Wind обектът е също Instrument обект и няма ме­тод който tune( ) може да извика за Instrument който не е също и Wind. Вътре в tune( ) кодът работи за Instrument и всичко извлечено от Instrument и актът на обръщане на Wind манипулаторът в Instrument манипулатор се нарича upcasting.

Защо “upcasting”?


Причината за термина е историческа и е свързана с начина на чертане на диа­гра­мите на наследяването отгоре надолу, растейки надолу. (Разбира се, може да чер­таете вашите диаграми както си искате.) Диаграмата на наследяването за Wind.java е тогава:

(диаграмата липсва в тази ревизия - б.пр.)




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

Може също да се направи обратното на ъпкастинг, наречено downcasting, но то­ва довежда до дилема която е обект на глава 11.


Отново композиция vs. наследяване


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



Сподели с приятели:
1   ...   30   31   32   33   34   35   36   37   ...   73




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

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