Поиск

Полнотекстовый поиск:
Где искать:
везде
только в названии
только в тексте
Выводить:
описание
слова в тексте
только заголовок

Рекомендуем ознакомиться

'Тесты'
Внимательно читайте задания и предлагаемые варианты ответов. Старайтесь не угадывать, а логически обосновывать сделанный Вами выбор. Пропускайте незна...полностью>>
'Документ'
2 октября 1896 года – открытие Женской фельдшерской школы. Директором был назначен первый санитарный врач России И.И. Моллесон. Организации школы спос...полностью>>
'Документ'
Может быть поэтому. Я чувствую, как будто меня что-то зовет. Я пробираюсь к очень старому разваленному зданию в замке.Это старое здание стоит здесь уж...полностью>>
'Документ'
Компания ООО НПЦ «МАРСО» создана на базе Института нефтехимпереработки Республики Башкортостан ( ГУП ИНХП РБ); разработанные нами ГСО включены в госуд...полностью>>

Главная > Документ

Сохрани ссылку в одной из сетей:
Информация о документе
Дата добавления:
Размер:
Доступные форматы для скачивания:

Объединение композиции и наследования ЛЕКЦИЯ 10

Совместное использование композиции и наследования часто и широко используется при программировании. Следующий пример показывает создание более комплексного класса использующего оба метода и композицию, и наследование с необходимыми инициализациями конструкторов:

//: c06:PlS.java

// Объединение композиции и наследования.

class Pl {

Pl(int i) {

System.out.println("Pl constructor --> " + i); //i=13

}

}

class DiPl extends Pl {

DiPl(int i) {

super(i-1);

System.out.println(

"DiPl constructor --> " + i); //i=14

}

}

// Нормальный путь, сделать что-то:

class C {

C(int i) {

System.out.println("C constructor --> " + i); //i=10

}

}

public class PlS extends C {

DiPl pl;

PlS(int i) {

super(i + 1);

pl = new DiPl(i + 5);

System.out.println(

"PlS constructor --> " + i); //i=9

}

public static void main(String[] args) {

PlS x = new PlS(9);

}

} ///:~

В то время, как компилятор требует от Вас инициализировать базовые классы и требует, что бы Вы делали это в начале конструктора, он не убеждается, в том, что Вы инициализировали остальные объекты, так что Вам придется быть осторожным.

Вывод программы:

C constructor 10

Pl constructor 13

DiPl constructor 14

PlS constructor 9

Выборочная композиция против наследования

Оба метода, композиция и наследование, позволяют Вам поместить подобъект внутрь вашего нового класса. Вы можете быть изумлены различием между ними и сомневаться, какой из способов выбрать.

Композиция в основном используется, когда Вам нужно использовать возможности существующего класса, но не использовать его интерфейс. Это значит, что Вы внедряете объект, так, что можно использовать его для получения доступа к функциональности внедряемого объекта в вашем новом классе, но пользователь вашего нового класса видит интерфейс вашего нового класса раньше, чем интерфейс внедряемого объекта. Чтобы помочь пользователю вашего нового класса понять, как использовать класс, можно создавать члены класса как public. Однако этот прием только для специальных случаев, и в основном вы должны делать поля private.

При наследовании, Вы берете существующий класс и создаете специальную его версию. В основном это означает, что Вы берете главный, целевой класс и приспосабливаете его для частных нужд.

Таким образом, вы устанавливаете связь он и есть, если используется наследование, а содержит при композиции.

Приведение к базовому типу

Наиболее важный аспект наследования заключается вовсе не в снабжении нового класса новыми методами. А заключается он в отношении между новым классом и базовым классом. Данное отношение можно определить так "Новый класс имеет тип существующего класса."

Это описание, не просто причудливая форма раскрытия сущности наследования, такая форма поддерживается напрямую языком Java. В примере, рассматриваемый базовый класс называется Instrument и представляет музыкальные инструменты, а дочерний класс называется Wind (духовые инструменты). Поскольку наследование подразумевает, что все методы в базовом классе так же доступны и в дочернем классе, то любое сообщение, которое может быть послано базовому классу, так же доступно и в дочернем. Если класс Instrument имеет метод play( ), то и Wind так же может его использовать. Это означает, что мы можем точно так же сказать, что объект Wind так же и типа Instrument. Следующий пример показывает, как компилятор поддерживает это высказывание:

//: c06:Wind.java

// Наследование и приведение к базовому типу.

import java.util.*;

class Instrument {

public void play() {}

static void tune(Instrument i) {

// ...

i.play();

}

}

// Объект Wind так же Instrument

// потому что они имеют общий интерфейс:

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. Внутри tune( ), код работает с типами Instrument и с чем угодно от него произошедшим, а факт конвертации ссылки на Wind в ссылку на Instrument называется приведением к базовому типу (upcasting).

Почему "приведение к базовому типу"?

Причина этого термина кроется в недрах истории, и основана она диаграмме наследования классов имеющую традиционное начертание: сверху страницы корень, растущий вниз. Естественно, Вы можете нарисовать свою собственную диаграмму, каким угодно образом. Диаграмма наследования для Wind.java:

Преобразование (casting) дочернего к базовому происходит при движении вверх (up) по диаграмме наследования, так что получается - upcasting (приведение к базовому типу). Приведение к базовому типу всегда безопасно, поскольку Вы переходите от более общего типа, к более конкретному. Так что дочерний класс является супермножеством базового класса. Он может содержать больше методов, чем базовый класс, но он должен содержать минимум все те методы, что есть в базовом классе. Только одна вещь может случится при приведении к базовому типу, это, что могут потеряться некоторые методы. Вот по этому то компилятор и позволяет осуществлять приведение к базовому типу без каких либо ограничений на приведение типов или специальных замечаний.

Вы так же можете осуществить обратную приведению к базовому типу операцию, называемую приведение базового типа к дочернему (downcasting), но при этом возникает небольшая дилемма, которая разъяснена в главе 12.

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

В объектно-ориентированном программировании наиболее используемый способ заключается в создании и использовании кода с хранением и кода и данных в одном пакете-классе. Вы так же используете существующие классы для создания новых классов при помощи композиции. Менее часто используется наследование. Однако, наследование более выразительно при изучении ООП, но это вовсе не значит, что его нужно использовать, где только возможно. Тем не менее, Вы должны использовать наследование, там, где его использование полезно. Один из понятных путей для определения, что Вы должны использовать, наследование или композицию заключается в выяснении нужно ли будет вам приводить что-то к базовому типу или нет. Если вам необходимо приведение к базовому типу, то наследование просто необходимо, но если же Вы не нуждаетесь в этом, то стоит присмотреться, а так ли уж необходимо здесь наследование. Следующая глава (полиморфизм) предоставляет одну из наиболее непреодолимых причин для приведения к базовому типу, но если Вы вспомните вопрос "Нужно ли мне приведение к базовому типу?", то Вы получите хороший способ для решения при выборе между композицией или наследованием.

Резюме

Оба метода, и наследование и композиция позволяют Вам создать новый тип из уже существующего. Обычно, Вы используете композицию для повторного использования существующих типов как части имплементации нового типа, а наследование когда Вам необходимо повторно использовать интерфейс. В силу того, что дочерний класс имеет интерфейс базового класса, он может быть приведен к базовому типу, что весьма важно для полиморфизма, что собственно Вы и увидите в следующей главе.

Относитесь с осторожностью к наследованию в объектно-ориентированном программировании, когда Вы начинаете разработку нового проекта используйте лучше композицию, а после того, как ваш код будет доведен до совершенства измените композицию на наследование если это конечно необходимо. Композиция имеет тенденцию к большей гибкости. Вы так же можете изменять поведение объектов с композицией во время исполнения.

Повторное использование композиции и наследования оказывает огромную помощь для быстрой разработки проектов, Вы обычно хотите изменить иерархию ваших классов, до того, как другие программисты станут работать с вашим проектом и с вашими классами. Вашим преимуществом при этом будет наследование, где каждый класс не большой по размеру (но и не слишком маленький, что бы не потерять функциональность) и выполняет узкую задачу.

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

Полиморфизм предоставляет другое измерение разделения интерфейса и реализации, т.е. отделяет что от как. Полиморфизм позволяет повысить возможности по организации кода и читабельность исходных текстов, так же, как создание расширяемых программ, которые могут расти не только во время их создания, но и после, когда к ним нужно добавить новую требуемую возможность.

Инкапсуляция создает новые инкапсулированные типы данных, комбинируя их характеристики и типы поведения. Имплементация скрывает интерфейс от дальнейшей имплементации делая некоторые элементы private. Этот вид механической организации может быть нов для кого-то, кто имеет большие познания в процедурном программировании. Но полиморфизм работает с разделением типов. В предыдущей главе Вы увидели, что наследование обращается с объектом с его собственным типом или с базовым типом. Эта особенность критична, поскольку при этом могут поддерживаться многие типы (дочерних от одного и того же базового типа), которые обрабатываются, как если бы они были одного типа и один и тот же код работает одинаково с этими различными типами. Вызов полиморфного метода поддерживает один тип для выражения его отличия от другого, такого же типа, и это из-за того, что они произошли от одного базового типа. Данное различие выражено через различие в поведении методов, которые Вы можете вызвать через базовый класс.

В этой главе, Вы узнаете о полиморфизме (так же называемом динамически связыванием или поздним связыванием или связыванием во время выполнения), начиная от основ с простыми примерами с последующим раскрытием всего поведения полиморфизма в программах.

Повторение приведения к базовому типу

В главе 6, Вы могли видеть, как можно использовать объект как своего собственного типа или в качестве базового типа. Получение ссылки на объект и привидение ее к типу базового класса называется "приведение к базовому типу", поскольку путь деревьев наследования растет сверху от базового класса.

Вы так же видели возникшую проблему истекающую из следующего:

//: c07:music:Music.java

// Наследование и приведение к базовому типу.

class Note {

private int value;

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

public static final Note

MIDDLE_C = new Note(0),

C_SHARP = new Note(1),

B_FLAT = new Note(2);

} // И т.д.

class Instrument {

public void play(Note n) {

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

}

}

// Объект Wind так же и instruments

// поскольку у них общий интерфейс:

class Wind extends Instrument {

// Переопределение метода:

public void play(Note n) {

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

}

}

public class Music {

public static void tune(Instrument i) {

// ...

i.play(Note.MIDDLE_C);

}

public static void main(String[] args) {

Wind flute = new Wind();

tune(flute); // Приведение к базовому типу

}

} ///:~

Метод Music.tune( ) принимает ссылки на Instrument, а так же на все, что произошло от Instrument. В main( ), Вы можете увидеть как это происходит, ссылка на Wind передается tune( ), без нужного преобразования типов. Интерфейс Instrument при этом должен существовать в Wind, поскольку Wind произошел от Instrument. Преобразование типа из Wind к Instrument может уменьшить интерфейс, но при этом он не будет меньше, чем весь интерфейс Instrument.

Забывание типа объекта

Это выражение может показаться странным для Вас. Почему кто-то должен намеренно забыть тип объекта? А это происходит, когда, Вы производите приведение к базовому типу, и выглядит это более прямо если бы tune( ) просто брала ссылку на Wind в качестве аргумента. Тем самым приносится еще одна неотъемлемая часть полиморфизма:

Связывание метод-вызов

Соединение вызова метода с телом метода называется связывание Когда свзяывание осуществляется до запуска программы (компилятором и компоновщиком, если такой используется), то оно (связывание) называется ранним связыванием. Вы могли даже и не слышать о таком термине, поскольку такая технология не применялась в процедурных языках. C компиляторы имеют только одну разновидность вызова, и она как раз является ранним связыванием.

В замешательство предыдущей программы находится вокруг раннего связывания, поскольку компилятор не знает правильный метод для вызова, если есть только ссылка на Instrument.

Решение называется позднее связывание, что означает, что связывание происходит во время работы программы и основывается на типе объекта. Позднее связывание так же иногда называют динамическим связыванием или связыванием во время выполнения. Когда язык поддерживает позднее связывание, то у него должен быть механизм определения типа объекта в время работы программы и вызова соответствующего метода. Все так и есть, компилятор все еще не знает какого типа этот объект, но механизм вызова методов находит его и вызывает соответствующее тело метода. Механизм позднего связывания меняется от языка к языку, но Вы можете представить себе, что в объект должна быть встроена некоторая информация о типе объекта.

Выработка правильного поведения

Как Вы уже знаете, все методы в Java имеют особенности полиморфизма, поскольку используется позднее связывание, Вы можете писать свой код для доступа к базовому классу и знаете, что с этим же кодом будут правильно работать и все классы наследники. Или, если идти другим путем, Вы посылаете сообщение объекту и последует правильная на его реакция.

Классический пример ООП - шейпы. Он используется наиболее часто, поскольку его легко нарисовать, но он так же и смущает начинающих программистов, которые думают, что ООП это только рисование таких вот схем.

В примере шейпе имеется класс Shape и множество дочерних типов: Circle, Square, Triangle и т.д. Причина этого примера проста, так же, как просто сказать "круг это всего лишь разновидность шейпа (геометрической фигуры)" и такое заявление легко понять. Диаграмма наследования показывает связи объектов:

Приведение к базовому типу происходит в выражении:

Shape s = new Circle();

Здесь, объект Circle создается и результирующая ссылка немедленно присваивается к Shape, здесь мы бы наверное получили бы ошибку (присвоение одного типа другому); но нет, все чудно прошло, поскольку Circle есть Shape через наследование. Так что компилятор согласился с выражением и не выдал никакой ошибки.

Предположим, что Вы вызываете метод базового класса (который был переопределен в дочернем классе):

s.draw();

И снова, Вы можете ожидать, что вызовется метод из Shape draw( ), поскольку это он и есть и как компилятору узнать, что это не он? А в самом деле вызовется Circle.draw( ), поскольку используется позднее связывание(полиморфизм).

Следующий пример поместит его несколько другим путем:

//: c07:Shapes.java

// Полиморфизм в Java.

class Shape {

void draw() {}

void erase() {}

}

class Circle extends Shape {

void draw() {

System.out.println("Circle.draw()");

}

void erase() {

System.out.println("Circle.erase()");

}

}

class Square extends Shape {

void draw() {

System.out.println("Square.draw()");

}

void erase() {

System.out.println("Square.erase()");

}

}

class Triangle extends Shape {

void draw() {

System.out.println("Triangle.draw()");

}

void erase() {

System.out.println("Triangle.erase()");

}

}

public class Shapes {

public static Shape randShape() {

switch((int)(Math.random() * 3)) {

default:

case 0: return new Circle();

case 1: return new Square();

case 2: return new Triangle();

}

}

public static void main(String[] args) {

Shape[] s = new Shape[9];

// Заполним массив шейпами:

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

s[i] = randShape();

// Сделаем вызов полиморфного метода:

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

s[i].draw();

}

} ///:~

Базовый класс Shape предоставляет общий интерфейс для всех наследников от Shape, это означает, что все шейпы могут быть нарисованы и стерты. Дочерние классы перекрывают эти определения для обеспечения уникального поведения в зависимости от типа шейпа.

Главный класс Shapes содержит static метод - randShape( ), который возвращает ссылку на случайно выбранный объект Shape каждый раз, когда Вы вызываете его. Заметьте, что приведение к базовому типу происходит каждый раз при return-е, который ссылается на Circle, Square или Triangle и посылает их из метода, как возвращаемый параметр. Так что, когда Вы вызываете этот метод Вы не можете узнать, какого типа возвращается параметр, поскольку всегда возвращается базовый тип Shape.

main( ) содержит массив из ссылок Shape заполненный вызовами randShape( ). На этом этапе Вы знаете, что Вы имеете некоторое множество ссылок на объекты типа Shape, но Вы не знаете ничего о них больше (и не больше, чем знает компилятор). В любом случае, когда Вы перемещаетесь по этому массиву и вызываете draw( ) для каждого элемента, то автоматически проставляется правильный тип, как Вы можете посмотреть это на примере:

Circle.draw()

Triangle.draw()

Circle.draw()

Circle.draw()

Circle.draw()

Square.draw()

Triangle.draw()

Square.draw()

Square.draw()

Естественно, поскольку шейпы вызываются случайным образом, то и результаты вывода могут быть различны. Причина вызова шейпов случайным образом обусловлена тем, что бы была возможность показать, что компилятор не имеет специальных знаний, для создания правильных вариантов при компиляции. Все вызовы draw( ) сделаны посредством динамической связи.



Похожие документы:

  1. Лекция Предмет и методология теории государства и права

    Документ
    ... выкупу (композиции) была возможность ... единоличного правителя; 2) династическое наследование власти; 3) пожизненность ... теоретическим кон­цепциям. Лекция 10. Политическая система и ... общественные объединения; 2) вступать в существующие объединения; 3) ...
  2. Сборник лекций по курсу биологии Астана -2011

    Реферат
    ... Лекция10 Законы Г.Менделя................................................................52 Лекция №11-12 Генетика пола. Скрепленное с полом наследование ... нескольких десятков объединенных рибосом. ... исследованию пере композиции наследственных задатков ...
  3. Методические рекомендации программа всеобщая история: с древнейших времен до конца XIX века 10 класс, базовый уровень (30 ч), профильный уровень (58 ч)

    Методические рекомендации
    ... 4. Средневековое общество и государство Лекция 10 Расцвет и кризис западноевропейского ... указанная деталь композиции свидетельствует об ... так как наследование шло не ... буферное» Нидерландское королевство       9. Объединение Германии связано с именем ...
  4. Учебно-методический комплекс по дисциплине «История государства и права зарубежных стран» по специальности 030501 «Юриспруденция и по направлению подготовки бакалавра 030500. 62 «Юриспруденция» Составитель

    Учебно-методический комплекс
    ... Корецкого. М. 1961. Лекция 10. Феодальное государство и ... , в) положений о порядке наследования престола, г) запрещающих занимать ... Малогерманский путь объединения» - объединение Германии под ... Д) ведомство финансов 10. В государстве франков композиция – это ...
  5. В Автоматизированные информационные системы (аис) и Базы данных (БД). Определение бд и банков данных (БнД)

    Документ
    ... (слайд 7) - бинарные операции: - объединение - пересечение \ - разность - декартово ... на слайде 12. Лекция 10 (DB_l10.ppt). ... на части. Композиция - разновидность ассоциации ... его методы. Наследование (inheritance). Наследования дочерним классом атрибутов ...

Другие похожие документы..