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


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



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

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


Много често композицията и наследяването се използват заедно. Следният при­мер показва създаване на по-сложен клас чрез използване на наследяване и ком­по­зиция, заедно с необходимата инициализация в конструкторите:

//: c06:PlaceSetting.java

// Combining composition & inheritance
class Plate {

Plate(int i) {

System.out.println("Plate constructor");

}

}


class DinnerPlate extends Plate {

DinnerPlate(int i) {

super(i);

System.out.println(

"DinnerPlate constructor");

}

}


class Utensil {

Utensil(int i) {

System.out.println("Utensil constructor");

}

}


class Spoon extends Utensil {

Spoon(int i) {

super(i);

System.out.println("Spoon constructor");

}

}
class Fork extends Utensil {



Fork(int i) {

super(i);

System.out.println("Fork constructor");

}

}


class Knife extends Utensil {

Knife(int i) {

super(i);

System.out.println("Knife constructor");

}

}
// A cultural way of doing something:



class Custom {

Custom(int i) {

System.out.println("Custom constructor");

}

}


public class PlaceSetting extends Custom {

Spoon sp;

Fork frk;

Knife kn;

DinnerPlate pl;

PlaceSetting(int i) {

super(i + 1);

sp = new Spoon(i + 2);

frk = new Fork(i + 3);

kn = new Knife(i + 4);

pl = new DinnerPlate(i + 5);

System.out.println(

"PlaceSetting constructor");

}

public static void main(String[] args) {



PlaceSetting x = new PlaceSetting(9);

}

} ///:~



Докато компилаторът ви кара да инициализирате базовите класове и иска това да стане в самото начало на конструктора, той не ви следи дали инициализирате член-обектите, така че трябва да помните и да внимавате за това.

Гарантиране на провилно почистване


Java няма концепцията на C++ за деструктор, метод който автоматично се ви­ка когато обектът се разрушава. Причината вероятно е че Java практиката е про­сто да забравим за обектите наместо да ги разрушаваме, позволявайки на бо­клу­чаря да освободи паметта когато е необходимо.

Често това е добре, но има случаи когато даден клас може да има активности, кои­то после да изискват почистване на нещо. Както се спомена в глава 4, не се знае кога боклучарят ще се активира и дали въобще ще се активира. Така че ако ис­кате нещо да се почиства, трябва да напишете специален метод който да пра­ви това и да осигурите използването му от страна на клиент-програмиста. От­го­ре на това, както е описано в глава 9 (exception handling), трябва да се пред­па­зи­те от изключения слагайки го във finally клаузата.

Да вземем пример с CAD система която чертае нещо на екран:

//: c06:CADSystem.java

// Ensuring proper cleanup

import java.util.*;


class Shape {

Shape(int i) {

System.out.println("Shape constructor");

}

void cleanup() {



System.out.println("Shape cleanup");

}

}


class Circle extends Shape {

Circle(int i) {

super(i);

System.out.println("Drawing a Circle");

}

void cleanup() {



System.out.println("Erasing a Circle");

super.cleanup();

}

}
class Triangle extends Shape {



Triangle(int i) {

super(i);

System.out.println("Drawing a Triangle");

}

void cleanup() {



System.out.println("Erasing a Triangle");

super.cleanup();

}

}
class Line extends Shape {



private int start, end;

Line(int start, int end) {

super(start);

this.start = start;

this.end = end;

System.out.println("Drawing a Line: " +

start + ", " + end);

}

void cleanup() {



System.out.println("Erasing a Line: " +

start + ", " + end);

super.cleanup();

}

}


public class CADSystem extends Shape {

private Circle c;

private Triangle t;

private Line[] lines = new Line[10];

CADSystem(int i) {

super(i + 1);

for(int j = 0; j < 10; j++)

lines[j] = new Line(j, j*j);

c = new Circle(1);

t = new Triangle(1);

System.out.println("Combined constructor");

}

void cleanup() {



System.out.println("CADSystem.cleanup()");

t.cleanup();

c.cleanup();

for(int i = 0; i < lines.length; i++)

lines[i].cleanup();

super.cleanup();

}

public static void main(String[] args) {



CADSystem x = new CADSystem(47);

try {


// Code and exception handling...

} finally {

x.cleanup();

}

}



} ///:~

Всичко в тази система е някакъв вид Shape (което самото е вид Object понеже не­явно е наследено от него клас). Всеки клас предефинира cleanup( ) на Shape в добавка на това че вика същия метод на базовия клас чрез super. Спе­ци­фич­ни­те Shape класове Circle, Triangle и Line всички имат конструктори които “чер­таят,” а и всеки метод извикан по време на живота на програмата може да бъ­де подозиран че прави нещо, което иска почистване после. Всеки клас има свой собствен cleanup( ) метод за реставриране на нещата, които не са памет, до тяхното състояние преди създаването на обекта.

В main( ) може да се видят две нови ключови думи, които няма официално да се въвеждат до глава 9: try и finally. Ключовата дума try показва че блокът кой­то следва (отделен с фигурни скоби) е пазен регион, което значи че се третира спе­циално. Една част от това специално третиране е че finally клаузата в този ре­гион винаги се изпълнява, без значение как завършва try блокът. (С из­клю­че­ния­та е вазможно try да завърши по необичайни начини.) Тук finally клаузата каз­ва “винаги викай cleanup( ) за x, без значение какво се случва.” Тези клю­чо­ви думи са обяснени напълно в глава 9.

Забележете, че вашият почистващ метод трябва да се грижи за реда на из­вик­ва­ния­та, ако подобектът зависи от друг обект. Изобщо ще следвате формата прие­та в C++ за деструкторите: Първо се извършва всичко специфично за ва­шия клас (което може да изисква елементите на базовия клас да са още жиз­не­спо­собни) и тогава да се извика почистващия метод на базовия клас, както е в при­мера.

Може да има много случаи, когато почистването не стои като проблем; просто ос­та­вяте боклучарят да си свърши работата. Но когато трябва да се намесите вие, прилежание и старание са необходими.

Ред при почистването на боклука


Не е много това, на което може да се разчита, що се отнася до събирането на бо­клука. Боклучарят може никога да не се извика. Ако сработи, може да чисти обек­тите във всякакъв ред както си иска. В добавка реализациите на боклучаря в Java 1.0 често не викат finalize( ) методите. Най-добре е да не се разчита за ни­що друго на боклучаря освен за освобождаването на паметта. Ако искате да пра­вите почистване, направете си собствени методи и не разчитайте на finalize( ). (Както се спомена по-рано Java 1.1 може да бъде заставен да вика всич­ките финализатори.)

Скриване на имената


Само C++ програмистите може да се изненадат от скриването на имената, по­не­же то работи различно в него език. Ако Java базов клас има име на метод което е претоварвано няклко пъти, повторното дефиниране на това име в извлечен клас не скрива никоя от тези версии в базовия клас. Така претоварването ра­бо­ти независимо дали е станало сега или по-рано:

//: c06:Hide.java

// Overloading a base-class method name

// in a derived class does not hide the

// base-class versions
class Homer {

char doh(char c) {

System.out.println("doh(char)");

return 'd';

}

float doh(float f) {



System.out.println("doh(float)");

return 1.0f;

}

}
class Milhouse {}


class Bart extends Homer {

void doh(Milhouse m) {}

}
class Hide {

public static void main(String[] args) {

Bart b = new Bart();

b.doh(1); // doh(float) used

b.doh('x');

b.doh(1.0f);

b.doh(new Milhouse());

}

} ///:~



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




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




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

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