Философия на java най-новото издание. Брус Екел - Философията на Java3. Програмиране от страна на сървъра

  • 12.04.2020

За да промените документа по подразбиране, редактирайте ръчно файла "blank.fb2".

Предговор 13

Java SE5 и SE6 14

Благодаря 14

Глава 1 Въведение в обектите 17

Развитие на абстракция 18

Обектът има интерфейс 20

Обектът предоставя услуги 22

Скрито внедряване 23

Реализация на повторно използване 24

Наследство 25

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

Йерархия с един корен 33

Контейнери 33

Параметризирани типове 35

Създаване, използване на предмети и техния живот 36

Обработка на изключения: Справяне с грешки 38

Паралелно изпълнение 38

Java и интернет 39

Глава 2. Всичко е обект 48

Всички обекти трябва да бъдат изрично създадени 49

Обектите никога не трябва да се изтриват 53

Създаване на нови типове данни 54

Методи, аргументи и връщани стойности 56

Създаване на програма Java 58

Статична ключова дума 60

Нашата първа програма Java 61

Коментари и вградена документация 64

Стил на програмен дизайн 70

Глава 3 Оператори 71

Прости команди за печат 71

Изявления на Java 72

Литерали 82

В Java липсва sizeof() 92

Резюме 100

Глава 4 Контролни структури 101

Синтаксис foreach 105

прекъсване и продължаване 108

Лоша команда goto 109

Резюме 115

Глава 5 Инициализиране и прекратяване 116

Конструкторът гарантира инициализация 116

Претоварване на метода 118

Почистване: финализиране и събиране на боклук 130

Инициализиране на членовете на класа 137

Инициализация на конструктора 140

Инициализация на масив 146

Резюме 151

Глава 6 Контрол на достъпа 152

Пакет като библиотечен модул 153

Спецификатори за достъп до Java 159

Интерфейс и реализация 163

Достъп до класове 164

Резюме 167

Глава 7 Повторно използване на класове 169

Синтаксис на състава 170

Синтаксис на наследяване 172

Делегация 176

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

Композиция срещу наследяване 184

Преобразуване на типа нагоре 186

Крайна ключова дума 188

Резюме 197

Глава 8. Полиморфизъм 198

Отново за възходящата трансформация. . . > 199

Характеристики 201

Конструктори и полиморфизъм 208

Ковариация на върнат тип 216

Развитие с наследяване 217

Резюме 220

Глава 9 Интерфейси 221

Абстрактни класове и методи 221

Интерфейси 224

Разделяне на интерфейса от изпълнението 227

Разширяване на интерфейс чрез наследяване 233

Интерфейсите като средство за адаптация 236

Вложени интерфейси 239

Интерфейси и фабрики 242

Резюме 244

Глава 10 Вътрешни класове 245

Създаване на вътрешни класове 245

Комуникация с външен клас 246

Конструкциите .this и .new 248

Вътрешни класове и преобразуване 249

Безименни вътрешни класове 253

Вътрешни класове: защо? 261

Наследяване от вътрешни класове 272

Възможно ли е да се замени вътрешен клас? 272

Местни вътрешни класове 274

Резюме 276

Глава 11 Колекции от обекти 277

Параметризирани и типизирани контейнери 277

Основни понятия 280

Добавяне на групи елементи 281

Итератори 288

Комплект 294

Опашка 298

PriorityQueue 299

Колекция и итератор 301

Идиома "метод-адаптер" 306

Резюме 309

Глава 12 Обработка на грешки и изключения 310

Основни изключения 310

Улавяне на 312 изключения

Създаване на ваши собствени изключения 314

Изключение Спецификации 319

Прихващане на произволни изключения 320

Стандартни изключения на Java 328

Прекратяване с накрая 330

Използване на finally с връщане 334

Ограничения при използване на изключения 336

Конструктори 339

Идентификация на изключение 343

Алтернативни решения 344

Резюме 351

Глава 13 Информация за типа 352

Необходимост от извод за динамичен тип (RTTI) 352

Регистрация на заводи 372

Отражение: информация за динамичен клас 376

Динамични медиатори 380

Обекти с неопределено състояние 384

Интерфейси и информация за типа 390

Резюме 394

Глава 14 Параметризиране 397

Проста параметризация 398

Параметризирани интерфейси 404

Параметризирани методи 407

Строителен комплекс модели 419

Ограничения 437

Метазнаци 440

Резюме 452

Глава 15 Масиви 454

Характеристики на масиви 454

Масив като обект 456

Връщане на масив 458

Многомерни масиви 460

Масиви и параметризация 463

Създаване на тестови данни 465

Създаване на масиви с помощта на генератори 470

Помощен набор от инструменти за масиви 474

Резюме 482

Глава 16 Входно-изходната система на Java 483

Файл 484 клас

Вход и изход 489

Добавяне на атрибути и интерфейси 491

Класовете по четец и писател 494

RandomAccessFile: сам по себе си 497

Типично използване на I/O потоци 498

Четящи и записващи устройства 505

Стандартен I/O 507

Нов I/O (nio) 510

Компресиране на данни 531

Сериализиране на обекти 536

Предпочитания 553

Резюме 555

Глава 17 Паралелно изпълнение 557

Клас на резбата 559

Художници 561

Споделяне на ресурси 578

Комуникация между нишки 598

Взаимно блокиране 602

Нови библиотечни компоненти 607

CountDownLatch 607

CyclicBarrier 609

PriorityBlockingQueue 614

Семафори 619

Моделиране 624

Резюме 629

Азбучен указател 631

Въведение в обектите

Ние разчленяваме природата, трансформираме я в понятия и им приписваме значение, както правим по много начини, защото всички сме страни по споразумение, което е валидно в общество, обвързано от речта, и което е фиксирано в структурата на езика. .. Не можем да комуникираме изобщо, освен като се съгласим с организацията и класификацията на данните, установени от това споразумение.

Бенджамин Лий Уорф (1897-1941)

Ние дължим появата на компютърната революция на машината. Затова нашите езици за програмиране се опитват да бъдат по-близо до тази машина.

Но в същото време компютрите не са толкова механизми, колкото средства за усилване на мисълта („велосипеди за ума“, както обича да казва Стив Джобс) и друго средство за себеизразяване. В резултат на това инструментите за програмиране клонят по-малко към машините и повече към нашия ум, както и към други форми на изразяване на човешките стремежи, като литература, живопис, скулптура, анимация и кино. Обектно-ориентираното програмиране (ООП) е част от превръщането на компютъра в средство за себеизразяване.

Тази глава ще ви запознае с основите на ООП, включително поглед върху основните методи за разработка на софтуер. Тя и книгата като цяло предполагат, че имате опит в програмирането на процедурен език, не непременно C. Ако смятате, че преди да прочетете тази книга, ви липсват познания по програмиране и синтаксис на C, използвайте мултимедийния семинар Thinking in C. който може да бъдат изтеглени от уебсайта

През март миналата година той кандидатства във филиал на голям международна компанияв Самара (да. Имам много арогантност и амбиция от дете). По това време знаех html, css, java, javascript (основи), pascal, visualbasic6, mysql заявки, php, Главна идея:c++. Изобщо не знаех Java. Предложиха ми работа като дизайнер по оформление, но отказах. Само като програмист! След това ми дадоха списък:

Bruce Eckel Thinking in Java (руски превод на 2-ро издание или оригинал на 4-то - прочетете и двете)
-Steve McConnell - перфектен код.
- Банда от четирима - Дизайн на шаблон. (това е почти ABC на OOP)
- бъдете възможно най-ясни относно разликата между j2se и j2ee.

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

В допълнение към литературата по-горе, взех интуитивния курс (сега разбирам, че е смешен в обхвата си, но по принцип там има основи)

В края на февруари изпратих отново автобиографията си и получих покана за интервю. Интервютата бяха общо 6 и продължиха 1,5 месеца. Две от тях са проведени чрез видеовръзка с Москва. Цялата картина напомняше на филма „Елате утре”. Но в крайна сметка получих предложение за работа. Договорът е съставен за работа на непълно работно време, т.к. Тогава нямах диплома. Миналия месец получих диплома и договорът беше подновен за редовен.

Текуща позиция Soft-Engineer. Заплащането е повече от задоволително. Вчера във връзка с преминаването на пълен работен ден го вдигнаха с 30%.

Даже в офиса на този измамник искаха примери за работа. Представих извършената от мен работа на свободна практика. Дори произведенията на други езици винаги са много по-добри от никакви.

Ps: Синя диплома по ФИЗИКА. Аз съм напълно самоук, така че всичко е във вашите ръце. Имам само английски от училище Безплатно (7 часа седмично). въпреки че американецът, който дойде при нас по време на околосветското си пътуване, не го познава добре. Едва разбрах половината от него заради акцента му. но това не е толкова критично в моя отдел. цялата документация на английски - ще научите дори и да не сте знаели)))))

Специални благодарности на този форум. Всъщност учих тук- ежедневно преподава всички теми, които се срещат)

БИБЛИОТЕКА ЗА ПРОГРАМИСТИ

Брус Екел

4-то издание

(^PPTER

Москва - Санкт Петербург - Нижни Новгород - Воронеж Ростов на Дон - Екатеринбург - Самара - Новосибирск Киев - Харков - Минск

BBK 32.973 2-018.1

Екел Б.

E38 Философия на Java. Библиотека на програмиста. 4-то изд. - Санкт Петербург: Питър, 2009. - 640 д.: ил. - (Поредица "Библиотека на програмиста").

ISBN 978-5-388-00003-3

Java не може да се разбере, като се разглежда само като колекция от някои harakureshki - необходимо е да се разбират задачите на този език като конкретни проблеми на програмирането като цяло. r3ia е книга за проблемите в програмирането: защо са се превърнали в проблеми и какъв подход използва Java, за да ги разреши. Следователно характеристиките на езика, обсъждани във всяка глава, са неразривно свързани с това как се използват за решаване на определени проблеми.

Тази книга, която издържа много препечатки в оригинала, поради дълбокото си и наистина философско представяне на тънкостите на езика, се смята за едно от най-добрите ръководства за Java програмисти.

ББК 32.973.2-018.1 УДК 004.3

Права за публикуване, получени по споразумение с Prentice Hall PTR.

Всички права запазени. Никоя част от тази книга не може да бъде възпроизвеждана под каквато и да е форма без писменото разрешение на притежателите на авторските права.

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

ISBN 978-0131872486 © Prentice Hall PTR, 2006 г.

ISBN 978-5-388-00003-3 © Превод на руски от Peter Press LLC, 2009 г.

© Публикация на руски език, дизайн на Piter Press LLC, 2009 г

Предговор.................................13

Java SE5 и SE6.............................14

Благодарности ............................14

Глава 1 Въведение в обектите .................................17

Развитието на абстракцията .........................18

Обектът има интерфейс.....................................20

Съоръжението предоставя услуги ............................22

Скрито внедряване.................................23

Повторно използване на внедряването.................................24

Наследство.................................25

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

Йерархия с един корен ............................33

Контейнери ............................33

Параметризирани типове.....................................35

Създаване, използване на предмети и време на техния живот.....................36

Обработване на изключения: Справяне с грешки..................................38

Паралелно изпълнение.................................38

Java и интернет.....................................39

Резюме.....................................47

Глава 2 Всичко е обект.....................................48

Всички обекти трябва да бъдат създадени изрично.................................49

Обектите никога не трябва да се изтриват.................................53

Създаване на нови типове данни.................................54

Методи, аргументи и връщани стойности..................................56

Създаване на програма на Java.....................................58

Ключова дума static............................60

Първата ни програма на Java ............................61

Коментари и вградена документация ................................64

Стил на програмиране ............................................70

Резюме.....................................70

Глава 3. Оператори.............................71

Прости команди за печат..................................71

Изявления на Java ............................72

Литерали...................................82

В Java липсва sizeof() .................................92

Резюме.................................100

Глава 4 Контролни структури .................101

Синтаксис foreach.............................105

връщане, ...................................107

прекъсване и продължаване.....................................108

Лоша команда goto .........................109

Резюме.....................................115

Глава 5. Инициализиране и прекратяване..................116

Инициализация на гаранции от конструктора.................................116

Претоварване на метода.............................118

Почистване: Финализиране и събиране на боклука.................................130

Инициализиране на членовете на класа.............................137

Инициализация на конструктора ................................140

Инициализиране на масиви.................................146

Резюме.................................151

Глава 6 Контрол на достъпа ............................152

Пакетът като библиотечен модул.................................153

Спецификатори за достъп до Java.............................159

Интерфейс и имплементация.................................163

Достъп до класове ............................164

Резюме.....................................167

Глава 7 Повторно използване на класове.............................169

Синтаксис на композицията.................................170

Синтаксис на наследяване.............................172

Делегиране.....................................176

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

Композиция срещу наследяване.................................184

защитен.................................185

Възходящо преобразуване на типа.....................................186

Крайна ключова дума.................................188

Резюме.....................................197

Глава 8 Полиморфизъм

Отново за възходящата трансформация. . . >................199

Характеристики...................................201

Конструктори и полиморфизъм.............................208

Ковариация на връщания тип.....................................216

Проектиране с наследяване............................217

Резюме.................................220

Глава 9. Интерфейси.....................221

Абстрактни класове и методи.....................................221

Интерфейси.....................................224

Разделяне на интерфейса от имплементацията .............................227

Разширяване на интерфейс чрез наследяване ..................233

Интерфейсите като средство за адаптация.....................................236

Вложени интерфейси.................................239

Интерфейси и фабрики.................................242

Резюме.................................244

Глава 10 Вътрешни класове.............................245

Създаване на вътрешни класове.............................245

Комуникация с външен клас.................................246

Конструкциите .this и .new............................248

Вътрешни класове и възходящо преобразуване..................249

Неназовани вътрешни класове.....................253

Вътрешни класове: защо? .............................. 261

Наследяване от вътрешни класове.............................272

Възможно ли е да се замени вътрешен клас?.....................272

Местни вътрешни класове.............................274

Резюме.....................................276

Глава 11 Колекции от обекти.................................277

Параметризирани и типизирани контейнери......................277

Основни понятия............................280

Добавяне на групи елементи.............................281

Списък.................................285

Итератори.............................288

LinkedList.............................291

Купчина.................................292

Много..................................................... 294

Карта.................................296

Опашка.................................298

PriorityQueue.............................299

Колекция и итератор.................................301

Идиома "метод-адаптер" .........................306

Резюме.................................309

Глава 12 Обработване на грешки и изключения 310

Основни изключения.....................................310

Улавяне на изключения.................................312

Създаване на ваши собствени изключения.............................314

Спецификации за изключение.................................319

Улавяне на произволни изключения.................................320

Стандартни изключения на Java ............................328

Прекратяване с окончателно.....................330

Използване на finally с връщане............................334

Ограничения за използване на изключения.....................................336

Конструктори............................339

Идентифициране на изключения.................................343

Алтернативни решения.....................................344

Резюме.....................................351

Глава 13 Типова информация.................................352

Необходимост от извод за динамичен тип (RTTI) .........352

Фабрична регистрация ................................372

Рефлексия: динамична информация за клас............................376

Динамични посредници............................380

Обекти с неопределено състояние............................384

Интерфейси и информация за типове ............................390

Резюме.................................394

Глава 14

Проста параметризация.....................................398

Параметризирани интерфейси.................................404

Параметризирани методи...................407

Комплексни модели на сгради................................419

Ограничения.................................437

Метасимволи................................440

Резюме............................452

Глава 15. Масиви.................................454

Характеристики на масива................................454

Масивът като обект.................................456

Връщане на масив.................................458

Многомерни масиви............................460

Масиви и параметризация.................................463

Създаване на тестови данни ............................465

Създаване на масиви с помощта на генератори.....................470

Помощен инструментариум за масиви.................................474

Резюме............................482

Глава 16 Входно-изходната система на Java ............................483

Файлов клас................................484

Вход и изход.................................489

Добавяне на атрибути и интерфейси.................................491

Часовете по четец и писател.............................494

RandomAccessFile: сам по себе си.....................497

Типично използване на входно/изходни потоци.................................498

Четящи и записващи устройства.....................................505

Вероятно не греша, ако предположа, че повечето изучаващи Java са започнали да го правят с помощта на известната книга на Bruce Eckel: „Мислене в Java“, известен в руското издание като "Философия на Java". За съжаление в в електронен формат(на руски) второто издание на тази книга е най-широко разпространеното, базирано на версията на Java 1.1, която отдавна е загубила своята актуалност. Иновациите, които се появиха в следващите версии на Java (и особено в Java SE5), бяха много значителни, което доведе до сериозна редакция на книгата в нейното четвърто издание (чийто превод беше публикуван на руски). Въпреки това, в лесен за четене (и най-важното - за бързо търсене) електронен формат руската версия на тази публикация не съществуваше. Затова реших да запълня тази празнина и да създам пълна версия на тази популярна книга във формат "wikibook". Вярвам, че тази информация ще бъде интересна и полезна не само за изучаващите езици, но и за всички, които работят с Java поради огромния брой отлични примери, илюстриращи почти всички аспекти на програмирането на този език. Особено когато става въпрос за рядко използвани функции на Java.

wikibook "Философия на Java"публикувано на:

"Пролетта в действие"

Книги от поредицата "..... в действие"(обикновено в PDF формат и обикновено на английски) са заслужено популярни в определени кръгове :) Сред тях има и обемни талмуди, като напр. „JSTL в действие“(лесен за четене и с умерени познания по английски, но подходящ за ролята на добър справочник по темата), и по-скромни занаяти, като напр. Подпори в действие(„Не всичко е злато...“). Книга "Пролетта в действие"в този списък все още е от категорията на "тежка категория" и във всеки смисъл на думата. Четенето без английски език вероятно не е лесно. И въпросът по-скоро не е в сложността на представения материал (той не е сложен), а в това, че се оказа - прекалено "англо-артистично", или нещо такова .... Пълен с лирични отклонения, крилати изрази, каламбури и други бла бла бла, езикови автори, бързо превръща четенето на това ръководство (на оригиналния език) в досаден процес. Но от друга страна, това ви позволява да знаете, че думата "рисувам"(обикновено - "нарисувай") може да се използва в значението на "извличане от" (букв. - "дърпане, плъзгане"). В резултат (като се вземе предвид общият стил на представяне, възприет в книгата), за да разберете точно значениефрази като: "...пролетно изчертаване на тези данни ...", става едновременно - и не е лесно, и е изключително необходимо. Следователно читателите на главите, които не съм превел, ще трябва сами да решат какво са искали авторите в такива случаи: да се изразят поетично за създаването (записването) на файл или закачливо да разкажат за четенето му.

Тази книга беше преобразувана от мен от PDF в wikibook като изрична справка за моя лична употреба. Следователно преводът не е тотален, а само на места - за което имаше достатъчно ентусиазъм. Останалите глави бяха просто дадени в удобна за бързо търсене форма. Публикува се, ВСИЧКО във формата - "както е", и не бива да обвинявате качеството на руския текст ... Не съм професионален преводач и нямах литературен редактор. Може би ще разочаровам някого с факта, че не преведох някои места и глави от книгата (и дори не планирам да ги превеждам), но беше необходимо да оставя резерв за бъдещите поколения

wikibook "Пролетта в действие " публикувано на:

ВЪВЕДЕНИЕ В ОБЕКТИТЕ

Ние дължим появата на компютърната революция на машината. Затова нашите езици за програмиране се опитват да бъдат по-близо до тази машина.

Но в същото време компютрите не са толкова механизми, а средства за усилване на мисълта („велосипеди за ума“, както обичаше да казва Стив Джобс) и друго средство за себеизразяване. В резултат на това инструментите за програмиране клонят по-малко към машините и повече към нашия ум, както и към други форми на изразяване на човешките стремежи, като литература, живопис, скулптура, анимация и кино. Обектно-ориентираното програмиране (ООП) е част от превръщането на компютъра в средство за себеизразяване.

Тази глава ще ви запознае с основите на ООП, включително преглед на основните техники за програмиране. Той и книгата като цяло предполагат, че имате опит в програмирането на процедурен език, което не е задължително ° С.

Тази глава съдържа подготвителни и допълнителни материали. Много читатели предпочитат първо да си представят голямата картина и едва след това да разберат тънкостите на ООП. Следователно много от идеите в тази глава служат, за да ви дадат солидно разбиране на ООП. Много хора обаче не схващат общата идея, докато не видят конкретно как работят нещата; такива хора често се заплитат в общи линии, без да имат примери пред себе си. Ако сте един от последните и сте нетърпеливи да започнете с основите на езика, можете да прескочите напред към следващата глава - пропускането на тази няма да е пречка за писане на програми или изучаване на езика. Трябва обаче да се върнете към тази глава по-късно, за да разширите хоризонтите си и да разберете защо обектите са толкова важни и къде се вписват в дизайна на програмата.

Развитието на абстракцията

Всички езици за програмиране са изградени върху абстракция. Може би трудността на задачите, които трябва да бъдат решени, зависи пряко от вида и качеството на абстракцията. Под "тип" имам предвид: "Какво точно абстрахираме?" Асемблерният език е малка абстракция от компютъра, на който работи. Много така наречени "командни" езици, създадени след него (като напр Fortran, BASICи ° С) бяха абстракции от следващото ниво. Тези езици имаха значително предимство пред асемблерния език, но тяхната основна абстракция все още ви кара да мислите за структурата на компютъра, а не за проблема, който се решава. Програмистът трябва да установи връзка между модела на машината (в "пространството на решението", което представлява мястото, където е реализирано решението - например компютър) и модела на проблема, който трябва да бъде решен (в "пространството на проблема" , което е мястото, където съществува проблемът - например приложена област). Установяването на връзка изисква усилие, отделено от действителния език за програмиране; резултатът е програми, които са трудни за писане и трудни за поддръжка. Не само това, той също създаде цяла индустрия от "методологии за програмиране".

Алтернатива на моделирането на машина е моделирането на проблема, който се решава. Ранните езици като LISPи APL, избра специален подход към моделирането на света наоколо (съответно „Всички проблеми се решават чрез списъци“ или „Алгоритмите решават всичко“). ПРОЛОГтретира всички проблеми като вериги от решения. Бяха създадени езици за програмиране, базирани на система от ограничения, и специални езици, в които програмирането се извършваше чрез манипулиране на графични структури (обхватът на последния се оказа твърде тесен). Всеки от тези подходи е добър в определена област на проблемите, които трябва да бъдат решени, но след като напуснете тази област, става трудно да ги използвате.

Обектният подход отива крачка напред, като предоставя на програмиста средство за представяне на задача в неговото пространство. Този подход е доста общ и не налага ограничения върху вида на проблема, който се решава. Елементите на проблемното пространство и техните представяния в пространството на решението се наричат ​​"обекти". (Вероятно ще имате нужда от други обекти, които нямат аналози в проблемното пространство.) Идеята е, че програмата може да се адаптира към спецификата на проблема, като създава нови типове обекти, така че докато четете кода, който решава проблема, вие едновременно вижте думите, които я описват. Това е по-гъвкава и мощна абстракция, надминаваща всичко съществувало преди по своите възможности. Някои езикови дизайнери вярват, че обектно-ориентираното програмиране само по себе си не е достатъчно за решаване на всички проблеми с програмирането и се застъпват за комбинация от различни програмни парадигми на един език. Такива езици се наричат многопарадигма(мултипарадигми). Вижте книгата на Timothy Budd Multiparadigm Programming in Leda (Addison-Wesley, 1995).. По този начин OOP ви позволява да опишете задачата в контекста на самата задача, а не в контекста на компютъра, на който ще се изпълни решението. Връзката с компютъра обаче все още е запазена. Всеки обект е като малък компютър; има състояние и операции, които позволява. Такава аналогия пасва добре на външния свят, който е „реалност, дадена ни в обекти“, които имат характеристики и поведение.

Алън Кей обобщи и изведе петте основни характеристики на езика Общи приказки- първият успешен обектно-ориентиран език, един от предшествениците Java. Тези характеристики представляват "чист", академичен подход към обектно-ориентираното програмиране:

  • Всичко е обект. Мислете за обект като за прецизирана променлива; той съхранява данни, но можете да "заявите" обект, като го помолите да извършва операции върху себе си. Теоретично, абсолютно всеки компонент на решавания проблем (куче, сграда, услуга и т.н.) може да бъде представен като обект.
  • Програмата е група от обекти, които си казват какво да правят чрез съобщения. За да направите заявка към даден обект, вие му „изпращате съобщение“. По-визуално едно съобщение може да бъде представено като извикване на метод, принадлежащ на определен обект.
  • Всеки обект има своя собствена "памет", състояща се от други обекти. С други думи, вие създавате нов обектчрез вграждане на съществуващи обекти в него. По този начин е възможно да се конструира произволно сложна програма, скривайки цялостната сложност зад простотата на отделните обекти.
  • Всеки обект има тип. С други думи, всеки обект е екземпляр на клас, където "клас" е еквивалентът на думата "тип". Най-важната разлика между класовете се крие именно в отговора на въпроса: „Какви съобщения могат да се изпращат до обект?“
  • Всички обекти от определен тип могат да получават едни и същи съобщения. Както скоро ще видим, това е много важно обстоятелство. Тъй като обект от тип "кръг" също е обект от тип "форма", вярно е, че "кръг" със сигурност може да получава съобщения за "форма". А това означава, че можете да пишете код за форми и да сте сигурни, че ще работи за всичко, което попада в понятието форма. Взаимозаменяемостта е една от най-мощните концепции в ООП.

Booch предложи още по-сбито описание на обекта:

Един обект има състояние, поведение и идентичност.

Основното е, че един обект може да има вътрешни данни (които са състоянието на обекта), методи (които определят поведението) и всеки обект може да бъде уникално разграничен от всеки друг обект - по-конкретно, всеки обект има уникален адрес в ум Това е вярно с някои ограничения, тъй като обектите може действително да съществуват на други машини и в различни адресни пространства и могат също да се съхраняват на диск. В тези случаи идентичността на даден обект трябва да се определя от нещо различно от адрес на паметта..

Обектът има интерфейс

Вероятно Аристотел е първият, който внимателно изучава понятието тип; той говори за "клас риби и клас птици". Концепцията, че всички обекти, макар и уникални, са в същото време част от клас обекти с подобни характеристики и поведение, беше използвана в първия обектно-ориентиран език, Simula-67, с въвеждането на фундаменталната ключова дума клас , който въведе нов тип в програмата.

език Симула, както подсказва името му, е създаден за разработване и симулиране на ситуации, подобни на класическия проблем "банков касиер". Имате групи от касиери, клиенти, сметки, плащания и валути - много "обекти". Обектите, които са идентични във всичко освен във вътрешно състояние, докато програмата работи, се групират в "обектни класове". Оттук дойде ключова дума клас . Създаването на абстрактни типове данни е фундаментална концепция във всяко обектно-ориентирано програмиране. Абстрактните типове данни действат почти по същия начин като вградените типове: можете да създавате променливи на типа (наречени обекти или екземпляри в термините на ООП) и да ги манипулирате (наричани съобщения или заявки; правите заявка и обектът решава какво да прави с то. ). Членовете (елементите) на всеки клас имат прилики: всяка сметка има баланс, всеки касиер приема депозити и т.н. В същото време всички членове се различават по своето вътрешно състояние: всяка сметка има индивидуален баланс, всеки касиер има човешко име. Следователно всички касиери, клиенти, сметки, преводи и т.н. могат да бъдат представени от уникални субекти вътре компютърна програма. Това е същността на обекта и всеки обект принадлежи към определен клас, който определя неговите характеристики и поведение.

Така че докато създаваме нови типове данни в обектни езици, почти всички тези езици използват ключовата дума „клас“. Когато видите думата "тип", помислете за "клас" и обратното Някои хора правят разлика между двете, като посочват, че типът дефинира интерфейс, докато класът е конкретно изпълнение на интерфейс..

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

Обектно-ориентираният подход не се ограничава до изграждане на модели. Независимо дали сте съгласни или не, че всяка програма е модел на системата, която разработвате, използването на OOP технология лесно намалява голям набор от проблеми до просто решение.

След като бъде дефиниран нов клас, можете да създадете произволен брой обекти от този клас и след това да ги манипулирате, сякаш са част от проблема. Всъщност една от основните трудности в ООП е установяването на съответствие едно към едно между обекти в пространството на проблема и обекти в пространството на решението.

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

Light lt = new Light() ;
lt.on();

Интерфейсът определя какви заявки можете да направите към определен обект. Въпреки това, кодът, който изпълнява заявките, също трябва да съществува някъде. Този код, заедно със скритите данни, съставлява изпълнението. От гледна точка на процедурното програмиране това, което се случва, не е толкова трудно. Типът съдържа метод за всяка възможна заявка и когато се получи конкретна заявка, се извиква съответният метод. Процесът обикновено се комбинира в едно: "изпращане на съобщение" (предаване на заявка) към обект и обработката му от обект (изпълнение на код).

В този пример има име на тип (клас). светлина (лампа), бетонен предмет от вид светлина С име То , а класът поддържа различни обектни заявки светлина : Изключете крушката, включете я, направете я по-ярка или я затъмнете. Вие създавате обект светлина , определяйки "препратка" към него ( То ) и се обадете на оператора нов за създаване на нов екземпляр от този тип. За да изпратите съобщение до обект, трябва да посочите името на обекта и да го свържете с желаната заявка с точка. От гледна точка на потребителя на предварително дефиниран клас, това е напълно достатъчно за работа с неговите обекти.

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

Често в класациите UMLпоказват се само името на класа и публичните методи, а средната част липсва. Ако се интересувате само от името на класа, можете да пропуснете и долната част.

Обектът предоставя услуги

Когато се опитвате да проектирате или разберете структурата на програма, често е полезно да мислите за обектите като за „доставчици на услуги“. Вашата програма предоставя услуги на потребителя и го прави чрез услуги, предоставяни от други обекти. Вашата цел е да създадете (или още по-добре да намерите в библиотеките на класовете) набор от обекти, които ще бъдат оптимални за решаване на вашия проблем.

За да започнете, запитайте се: „Ако можех магически да премахна предмети от шапка, кои биха могли да решат проблема ми в момента?“ Да предположим, че разработвате счетоводна програма. Човек може да си представи набор от обекти, които предоставят стандартни прозорци за въвеждане на счетоводна информация, друг набор от обекти, които извършват счетоводни изчисления, обект, който отпечатва чекове и фактури на различни принтери. Може би някои от тези обекти вече съществуват, а за други обекти си струва да разберете как могат да изглеждат. Какви услуги биха могли да предоставят тези съоръжения и какви съоръжения ще са им необходими, за да вършат работата си? Ако продължите така, рано или късно ще кажете: „Този ​​обект е достатъчно прост, за да можем да седнем и да го напишем“ или „Със сигурност такъв обект вече съществува“. Това е разумен начин да се разпредели решението на проблема на отделни обекти.

Представяне на обект като доставчик на услуги допълнителна полза: помага за подобряване на свързаността ( сплотеност) обект. Добра свързаност - съществено качество софтуерен продукт: означава, че различни аспекти на софтуерен компонент (като обект, въпреки че може да се отнася и до метод или библиотека с обекти) се вписват добре. Един от често срещани грешкиразрешено при проектирането на обект е пренасищането му с голям брой свойства и възможности. Например, когато разработвате модул, който отпечатва чекове, може да искате той да „знае“ всичко за форматирането и печатането.

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

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

Скрито изпълнение

Полезно е програмистите да се разделят на създатели на класове(тези, които създават нови типове данни) и клиентски програмистиБлагодарен съм за този мандат на моя приятел Скот Майерс.(потребители на класове, които използват типове данни в своите приложения). Целта на втория е да събере възможно най-много класове, за да се включи в бързо разработване на програми. Целта на създателя на клас е да изгради клас, който излага само това, от което клиентският програмист се нуждае, и скрива всичко останало. Защо? Клиентският програмист няма да има достъп до скритите части, което означава, че създателят на класовете си запазва възможността да ги променя произволно, без да се страхува, че ще нарани някого. „Скритата“ част обикновено е най-„крехката“ част от обекта, която може лесно да бъде повредена от небрежен или невеж клиентски програмист, така че скриването на изпълнението намалява броя на грешките в програмите.

Във всяка връзка е важно да има някакви граници, които не се преминават от никой от участниците. Създавайки библиотека, вие установявате връзка с клиентския програмист. Той е програмист точно като вас, но ще използва вашата библиотека, за да създаде приложение (и може би библиотека от по-високо ниво). Ако дадете достъп до всички членове на класа на всеки, клиентският програмист може да прави каквото си поиска с класа и няма начин да го накарате да "играе по правилата". Дори ако по-късно трябва да ограничите достъпа до определени членове на вашия клас, това не може да стане без механизъм за контрол на достъпа. Цялата структура на класа е отворена за всички желаещи.

По този начин първата причина за ограничаване на достъпа е необходимостта да се защитят "крехките" детайли от клиентския програмист - части от вътрешната "кухня", които не са част от интерфейса, с който потребителите решават проблемите си. Всъщност това е полезно и за потребителите - те веднага ще видят какво е важно за тях и какво могат да пренебрегнат.

Втората причина за появата на ограничение на достъпа е желанието да се позволи на разработчика на библиотеката да промени вътрешните механизми на класа, без да се притеснява как това ще се отрази на клиентския програмист. Например, можете да внедрите определен клас набързо, за да ускорите разработката на програма, и след това да го пренапишете, за да подобрите скоростта. Ако правилно сте разделили и защитили интерфейса и изпълнението, това изобщо не би трябвало да е трудно.

Javaизползва три изрични ключови думи, характеризиращи нивото на достъп: публичен, частен и защитени . Тяхното предназначение и използване е много просто. Тези спецификатори за достъп определят кой има право да използва дефинициите, които ги следват. Слово публичен означава, че следните определения са достъпни за всички. Напротив, думата частен означава, че клаузите след него са достъпни само за създателя на типа, в неговите методи. Срок частен - "крепостна стена" между вас и програмиста-клиент. Ако някой се опита да използва частен -членове, то ще бъде спряно от грешка при компилиране. спецификатор защитени действа подобно на частен , с едно изключение - производните класове имат достъп до маркираните членове защитени но нямат достъп до частен -членове (ще разгледаме наследяването скоро).

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

Повторно използване на внедряването

Генерираният и тестван клас трябва (в идеалния случай) да бъде полезен блок от код. Оказва се обаче, че постигането на тази цел е много по-трудно, отколкото много хора си мислят; разработването на предмети за многократна употреба изисква опит и разбиране на същността на въпроса. Но след като го разберете правилно добро строителство, той просто ще иска внедряване в други програми. Повторното използване на код е едно от най-впечатляващите предимства на обектно-ориентираните езици.

Най-лесният начин за повторно използване на клас е чрез директно създаване на неговия обект, но можете също да поставите обект от този клас в нов клас. Ние наричаме това инжектиране на обект (създаване на обект-член). Новият клас може да съдържа произволен брой обекти от други типове във всяка комбинация, която е необходима за постигане на необходимата функционалност. Тъй като компилираме нов класот вече съществуващи класове, този метод се извиква състав(когато композицията се изпълнява динамично, тя обикновено се наименува агрегиране). Композицията често се нарича връзка "има" ( има), както например в изречението „Колата има двигател“.

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

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

Наследяването играе важна роля в обектно-ориентираното програмиране, така че често му се обръща много внимание и начинаещият може да си помисли, че наследяването трябва да се прилага навсякъде. И това е изпълнено със създаването на тромави и ненужно сложни решения. Вместо това, когато създавате нови класове, първо трябва да оцените възможността за композиция, тъй като е по-проста и по-гъвкава. Ако приемете препоръчания подход, вашите програмни конструкции ще станат много по-ясни. И когато придобиете практически опит, няма да е трудно да разберете къде трябва да се използва наследяването.

Наследство

Сама по себе си идеята за обект е изключително удобна. Обектът ви позволява да комбинирате данни и функционалност на концептуално ниво, тоест можете да представите желаната концепция от пространството на задачите, вместо да я конкретизирате с помощта на диалект на машината. Тези понятия формират фундаменталните единици на езика за програмиране, описани с ключовата дума class.

Но трябва да признаете, че би било жалко да създадете някакъв клас и след това да свършите цялата работа отново за подобен клас. Много по-рационално е да вземете готов клас, да го „клонирате“ и след това да направите допълнения и актуализации към получения клонинг. Това е точно това, което получавате в резултат на наследяване, с едно изключение - ако оригиналният клас (наричан още базов * клас, суперклас или родителски клас) се промени, тогава всички промени се отразяват в неговия "клонинг" (наричан производен клас , наследен клас, подклас или дъщерен клас).

(Стрелката (празен триъгълник) в UML диаграмата сочи от производния клас към базовия клас. Както скоро ще видите, може да има повече от един производен клас.)

Типът дефинира повече от свойствата на група обекти; свързва се и с други видове. Двата типа могат да споделят прилики и поведение, но се различават по броя на характеристиките, както и способността да обработват повече съобщения (или да ги обработват по различен начин). За да изрази тази общност на типовете, наследяването използва концепцията за базови и производни типове. Базовият тип съдържа всички характеристики и действия, общи за всички типове, получени от него. Вие създавате базов тип, който да представлява основата на вашето разбиране за някои от обектите във вашата система. Други типове се извличат от основния тип, изразявайки други реализации на този обект.

Например машина за рециклиране сортира отпадъците. Базовият тип ще бъде „боклук“ и всяко парче боклук има тегло, цена и т.н. и може да бъде смачкано, разтопено или разложено. Въз основа на това се наследяват по-специфични видове боклук, които имат допълнителни характеристики (бутилката има цвят) или поведенчески черти ( алуминиева кутияможе да се смачка, стоманената кутия се привлича от магнит). В допълнение, някои поведения могат да варират (цената на хартията зависи от нейния тип и състояние). Наследяването ви позволява да създадете йерархия на типове, която описва решавания проблем в контекста на неговите типове.

Вторият пример е класически пример с геометрични форми. Основният тип тук е „форма“ и всяка форма има размер, цвят, местоположение и т.н. Всяка форма може да бъде начертана, изтрита, преместена, боядисана и т.н. След това се създават (наследяват) специфични типове форми: кръг, квадрат, триъгълник и т.н., всеки от които има свои собствени допълнителни характеристики и поведение. Например, някои фигури поддържат огледална операция. Индивидуалните характеристики на поведението могат да се различават, както в случая с изчисляването на площта на фигура. Йерархията на типове въплъщава както подобни, така и различни свойства на формите.

Намаляването на решението до понятията, използвани в примера, е изключително удобно, тъй като не се нуждаете от много междинни модели, които свързват описанието на решението с описанието на проблема. Когато работите с обекти, йерархията на типовете става основният модел, така че преминавате от описване на системата в реалния свят директно към описание на системата в код. Всъщност една от трудностите при обектно-ориентираното планиране е, че за вас е много лесно да преминете от началото на проблема до края на решението. Ум, обучен за сложни решения, често се забива, когато използва прости подходи.

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

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

Има два начина за промяна на новия клас от базовия клас. Първият е доста очевиден: напълно нови методи се добавят към производния клас. Те вече не са част от интерфейса на базовия клас. Очевидно базовият клас не е направил всичко, което изисква задачата, така че сте добавили няколко метода. Въпреки това, такъв прост и примитивен подход към наследяването понякога се оказва идеалното решение на проблема. Въпреки това, едно нещо, което трябва да обмислите внимателно, е, че базовият клас може също да се нуждае от тези добавени методи. Процесът на идентифициране на модели и преразглеждане на архитектурата е ежедневна рутина в обектно-ориентираното програмиране.

Докато наследяването понякога предполага, че интерфейсът ще бъде разширен с нови методи (особено в Java, където наследяването се обозначава с ключовата дума се простира , тоест "разширяване"), това изобщо не е необходимо. Вторият, по-важен начин за модифициране на клас е да промянаповедение вече съществуващи методибазов клас. Това се нарича замяна (или замяна) на метод.

За да замените метод, просто създавате нова дефиниция на този метод в производен клас. Искате да кажете "Използвам същия метод на интерфейс, но искам той да прави различни неща за моя нов тип."

Когато използвате наследяване, възниква очевидният въпрос: трябва ли наследяването да замени методите самобазов клас (а не добавяне на нови методи, които не съществуват в базовия клас)? Това би означавало, че производният клас ще бъде точно от същия тип като базовия клас, тъй като имат същия интерфейс. В резултат на това можете свободно да замените обекти от базов клас с обекти от производен клас. Можете да говорите за пълна подмяна и това често се нарича принцип на заместване. В известен смисъл този начин на наследяване е идеален. Този вид връзка между базов клас и производен клас често се нарича връзка. "е нещо", тъй като може да се каже "кръгът е фигура". За да се определи колко подходящо ще бъде наследяването, е достатъчно да се провери дали има връзка "е" между класовете и колко е оправдана.

В други случаи интерфейсът на производния клас се допълва с нови елементи, което води до разширяването му. Новият тип все още може да се използва вместо основния тип, но сега тази замяна не е идеална, защото новите методи не са налични от основния тип. Такава връзка се описва с израза "подобен на" (това е моят термин); новият тип съдържа интерфейса на стария тип, но включва и новите методи и не може да се каже, че тези типове са абсолютно еднакви. Да вземем за пример един климатик.

Да предположим, че къщата ви е оборудвана с всичко необходимо оборудванеза контрол на процеса на охлаждане. Сега си представете, че климатикът се е повредил и сте го заменили с нагревател, който може да отоплява и охлажда. Нагревателят е „като“ климатик, но може и повече. Тъй като контролната система на вашия дом може да контролира само охлаждането, тя е ограничена в комуникацията с охладителната част на новото съоръжение. Интерфейсът на новия обект е разширен и съществуващата система не разпознава нищо друго освен оригиналния интерфейс.

Разбира се, разглеждайки тази йерархия, става ясно, че базовият клас "охладителна система" не е достатъчно гъвкав; трябва да се преименува на "система за контрол на температурата", така че да включва и отопление - и след това принципът на заместване ще работи. Тази диаграма обаче дава пример какво може да се случи в действителност.

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

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

Когато се използват йерархии на типове, често е необходимо да се третира обект от определен тип като основен тип. Това ви позволява да пишете код, който не зависи от конкретни типове. По този начин, в примера с фигурите, методите просто манипулират форми, независимо дали са кръгове, правоъгълници, триъгълници или някои все още недефинирани форми. Всички фигури могат да бъдат рисувани, изтривани и премествани, а методите просто изпращат съобщения до обекта на формата; те не се интересуват как обектът обработва съобщението.

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

Въпреки това, когато се опитвате да посочите обекти от производни типове като основни типове (кръгове като фигура, велосипед като превозно средство, корморан като птица и т.н.), възниква един проблем. Ако даден метод ще каже на обща фигура да се начертае, или на превозно средство да следва определен курс, или на птица да лети, компилаторът не може да знае точно коя част от кода ще бъде изпълнена. Това е целият смисъл – когато се изпрати съобщение, програмистът не иска да знае какъв код се изпълнява; методът на рисуване може да се приложи към кръг, правоъгълник и триъгълник с еднакъв успех и обектът ще изпълни правилния код, в зависимост от своя характерен тип.

Ако не е необходимо да знаете коя част от кода се изпълнява, тогава, когато добавите нов подтип, неговият код за изпълнение може да бъде различен, без да се изискват промени в метода, от който е извикан. Ако компилаторът не знае точно какъв код да изпълни, какво прави?

AT следващ примерпредмет BirdController (контрол на птици) може да работи само с общи обекти птица (птица), без да се знае точния им вид. От гледна точка BirdController това е удобно, защото не е необходимо да пишете специален код за проверка на вида на обекта, който се използва за това птица , за да се справи с някакво специално поведение. Как става така, че когато извикате метода move (), без да посочите точния тип птица , се извършва правилното действие – обектът гъска (гъска) тича, лети или плува и обектът Пингвин (пингвин) бягане или плуване?

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

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

За да приложите късно свързване, Javaизползва специални кодови фрагменти вместо абсолютно извикване. Този код изчислява адреса на тялото на метода въз основа на информацията, съхранена в обекта (процесът е разгледан много подробно в Глава 7 за полиморфизма). Така всеки обект може да се държи различно в зависимост от съдържанието на тази специална част от кода. Когато изпращате съобщение, обектът всъщност решава какво да прави с него.

Някои езици изискват изрично да посочите, че даден метод трябва да използва гъвкав механизъм за късно свързване (в C++има ключова дума за това виртуален). В тези езици методите не са динамично свързани по подразбиране. AT Javaкъсното свързване се извършва по подразбиране и не е необходимо да помните да добавяте ключови думи, за да осигурите полиморфизъм.

Припомнете си примера с фигурите. Семейство от класове (базирани на същия интерфейс) беше показано на диаграма по-рано в тази глава. За да демонстрираме полиморфизъм, ще напишем кодов фрагмент, който игнорира спецификите на типа и работи само върху базовия клас. Този код е отделен от спецификата на типа, така че е по-лесен за писане и разбиране. И ако чрез наследяване се добави нов тип (например шестоъгълник), тогава кодът, който пишете за новия тип фигура, ще работи също толкова добре, колкото кодът за съществуващите типове. Така програмата става разширяема.

Да кажем, че си написал Javaследния метод (скоро ще научите как да го направите):

void doSomething(Форма на формата) (
shape.erase(); // изтрива
//...
shape.draw(); // рисувам
}

Методът работи с обобщена фигура ( форма ), тоест не зависи от конкретния тип обект, който се рисува или изтрива. Сега използваме извикването на метода направи нещо() в друга част на програмата:

Кръг кръг = нов кръг () ; // кръг
Триъгълник triangle = new Triangle() ; // триъгълник
Линия линия = нов ред(); // линия
doSomething(кръг);
doSomething(триъгълник);
doSomething(ред);

Извиквания на метод направи нещо() автоматично работят правилно, независимо от действителния тип на обекта.

Това всъщност е доста важен факт. Помислете за реда:

doSomething(кръг);

Тук се случва следното: към метод, който очаква обект форма , обектът „кръг“ се предава ( кръг ). Тъй като кръгът ( кръг ) е едновременно фигура ( форма ), след това методът направи нещо() и се отнася с нея като с фигура. С други думи, всяко съобщение, което методът може да изпрати форма , също се приема кръг . Това действие е напълно безопасно и също толкова логично.

Ние се отнасяме към този процес на работа с производен тип, сякаш е основен тип. upcasting. Слово трансформацияозначава, че обектът се третира като принадлежащ към различен тип и възходящтова е така, защото в диаграмите на наследяване базовите класове обикновено са отгоре, а производните класове са разпръснати отдолу. Това означава, че преобразуването към основния тип е движение нагоре по диаграмата и следователно е "нагоре".

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

shape.erase();
// ...
shape.draw();

Обърнете внимание, че не се казва „ако сте обект кръг , направете го и ако сте обект Квадрат направи това и това." Такъв код с отделни действия за всеки възможен тип форма ще бъде объркващо и ще трябва да се променя всеки път, когато се добавя нов подтип форма . И така, вие просто казвате: „Ти си фигура и знам, че можеш да рисуваш и изтриваш себе си, добре, направи го и се погрижи сам за детайлите.“

В кода на метода направи нещо() Интересното е, че всичко се получава както трябва. При повикване рисувам() за обект кръг се изпълнява друг код, а не този, който работи при извикване рисувам() за обекти Квадрат или линия , и когато рисувам() приложен към неизвестна фигура форма , правилното поведение се осигурява чрез използване на реалния тип форма . Вътре е най-високата степенинтересно, защото, както беше отбелязано малко по-рано, когато компилаторът генерира код направи нещо() , не знае точно с какви типове работи. Съответно би могло да се очаква да бъдат извикани версиите на метода рисувам() и изтрива() от базов клас форма , а не техните варианти от конкретни класове кръг , Квадрат или линия . И все пак всичко работи правилно благодарение на полиморфизма. Компилаторът и системата за изпълнение се грижат за всички подробности; всичко, което трябва да знаете е, че това ще се случи... и по-важното, как да създавате програми, използвайки този подход. Когато изпратите съобщение до обект, обектът ще избере правилното поведение, използвайки upcast.

Йерархия с един корен

Малко след появата C++въпросът от OOP започна активно да се обсъжда - трябва ли задължително всички класове да бъдат наследени от един базов клас? AT Java(както в почти всички други ООП езици с изключение на C++) На този въпрос беше отговорено утвърдително. Цялата йерархия на типове се основава на един основен клас Обект . Оказа се, че йерархията с един корен има много предимства.

Всички обекти в йерархия с един корен споделят някакъв общ интерфейс, така че като цяло всички те могат да се разглеждат като един основен тип. AT C++беше избран друг вариант - общ прародител в този език не съществува. По отношение на съвместимостта със стария код, този модел е по-скоро в съответствие с традицията. ° Си може би си мислите, че е по-малко ограничен. Но веднага щом възникне необходимостта от пълноценно обектно-ориентирано програмиране, ще трябва да създадете своя собствена йерархия на класовете, за да получите същите предимства, които са вградени в други ООП езици. И във всяка нова библиотека с класове може да срещнете някакъв несъвместим интерфейс. Включването на тези нови интерфейси в архитектурата на вашата програма ще изисква допълнителни усилия (и вероятно множествено наследяване). Заслужава ли си допълнителната „гъвкавост“? C++подобни разходи? Ако имате нужда (напр. големи инвестицииза разработване на код ° С), тогава няма да сте губещ. Ако развитието започне от нулата, подходът Javaизглежда по-продуктивен.

Гарантирано е, че всички обекти в една основна йерархия споделят някои общи функции. Знаете, че някои основни операции могат да се извършват върху всеки обект в системата. Всички обекти се създават лесно в динамичната купчина и предаването на аргументи е значително опростено.

Йерархията с един корен прави много по-лесно внедряването на събиране на боклук - едно от най-важните подобрения Javaв сравнение с C++. Тъй като информацията за типа е гарантирано, че присъства във всеки от обектите по време на изпълнение, никога няма да има обект в системата, чийто тип не може да бъде определен. Това е особено важно при извършване на системни операции като обработка на изключения и за по-голяма гъвкавост на програмирането.

Контейнери

Често не се знае предварително колко обекта ще са необходими за решаването на определен проблем и колко дълго ще съществуват. Също така не е ясно как да се съхраняват такива обекти. Колко памет трябва да се разпредели за съхраняване на тези обекти? Не е известно, тъй като тази информация ще стане достъпна само докато програмата работи.

Много проблеми в обектно-ориентираното програмиране са решени просто действие: Вие създавате друг тип обект. Новият тип обект, който решава този конкретен проблем, съдържа препратки към други обекти. Разбира се, масивите, които се поддържат в повечето езици, също могат да играят тази роля. Въпреки това, новият обект, обикновено т.нар контейнер(или колекция, но в Javaтерминът се използва в различен смисъл) по необходимост ще се разшири, за да побере каквото и да поставите в него. Следователно няма да е необходимо да знаете предварително за колко обекта е предназначен капацитетът на контейнера. Просто създайте контейнер и той ще се погрижи за детайлите.

За щастие, един добър ООП език идва с набор от готови контейнери. AT C++това е част от стандартната библиотека C++, понякога наричана библиотека стандартни шаблони (Библиотека със стандартни шаблони, STL). Общи приказкиидва с много широка гама от контейнери. Javaсъщо съдържа контейнери в стандартната си библиотека. За някои библиотеки се счита за достатъчно да има един единствен контейнер за всички нужди, но в други (например в Java) има различни контейнери за всички случаи: няколко различни типа списъци списък (за съхраняване на последователности от елементи), карти Карта (известни също като асоциативни масиви, позволяват ви да свързвате обекти с други обекти), както и набори комплект (осигуряване на уникални стойности за всеки тип). Библиотеките с контейнери могат също да съдържат опашки, дървета, стекове и т.н.

От гледна точка на дизайна всичко, от което наистина се нуждаете, е контейнер, който може да реши проблема ви. Ако един тип контейнер отговаря на всички нужди, няма причина да се използват други видове. Има две причини, поради които трябва да избирате от наличните контейнери. Първо, контейнерите осигуряват разнообразие от интерфейси и взаимодействия. Поведението и интерфейсът на стека са различни от тези на опашка, която се държи различно от набор или списък. Един от тези контейнери е в състояние да осигури повече от ефективно решениевашата задача в сравнение с останалите. Второ, различните контейнери изпълняват едни и същи операции по различни начини. най-добър пример- това е ArrayList и LinkedList . И двете са прости последователности, които могат да имат идентични интерфейси и поведение. Но някои операции се различават значително във времето за изпълнение. Да кажем времето за вземане на проби от произволен елемент в ArrayList винаги остава същият, независимо кой елемент е избран. Въпреки това, в LinkedList неблагоприятно е да се работи с произволен достъп - колкото по-надолу в списъка е даден елемент, толкова по-голямо забавяне предизвиква търсенето му. От друга страна, ако трябва да вмъкнете елемент в средата на списъка, LinkedList направете го по-бързо от ArrayList . Тези и други операции са различна ефективноств зависимост от вътрешна структураконтейнер. На етапа на планиране на програмата можете да изберете списък LinkedList , а след това, по време на процеса на оптимизация, преминете към ArrayList . Поради абстрактния характер на интерфейса списък такъв преход ще изисква минимални промени в кода.

Параметризирани типове (генерични)

Преди освобождаване Java SE5контейнерите могат да съхраняват само данни Обект - единственият универсален тип Java. Йерархията с един корен означава, че всеки обект може да се разглежда като Обект , така че контейнерът с елементите Обект подходящ за съхранение на всякакви предмети Примитивните видове не могат да се съхраняват в контейнери, но благодарение на механизма автоматично опаковане(автобокс) Java SE5това ограничение не е съществено. Тази тема ще бъде разгледана по-подробно по-нататък в книгата..

Когато работите с такъв контейнер, вие просто поставяте препратки към обекти в него и по-късно ги извличате. Но ако контейнерът може да съхранява само Обект , тогава когато в него се постави препратка към обект от друг тип, преобразуване към Обект , тоест загуба на "индивидуалността" на обекта. Когато го извличате обратно, получавате препратка към Обект , а не препратка към типа, който е поставен в контейнера. Как да го конвертирате в конкретен тип обект, поставен в контейнер?

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

Опасността обаче не е толкова голяма - понижаването на грешен тип ще доведе до извикана грешка по време на изпълнение изключение(виж отдолу). Но когато извличате препратки към обекти от контейнер, трябва по някакъв начин да запомните действителния тип на техните обекти, за да извършите правилното прехвърляне.

Понижаването и проверката на типа по време на изпълнение изискват допълнително време и допълнителни усилия от програмиста. Или може би можете по някакъв начин да създадете контейнер, който знае типа на съхранените обекти и по този начин елиминира необходимостта от преобразуване на типа и потенциални грешки? Решението се нарича механизъм тип параметризация. Параметризираните типове са класове, които компилаторът може автоматично да адаптира за работа с определени типове. Например, компилаторът може да конфигурира параметризиран контейнер да съхранява и извлича само форми ( форма ).

Една от най-важните промени Java SE5е поддръжка за параметризирани типове ( генерични лекарства). Параметризираните типове са лесно разпознаваеми по ъгловите скоби, които ограждат имената на типовете параметри; например контейнер ArrayList , предназначени за съхранение на предмети форма , се създава така:

ArrayList< Shape >форми = нов ArrayList< Shape > () ;

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

Създаване, използване на обекти и техния живот

Един от критични аспектиработа с обекти - организацията на тяхното създаване и унищожаване. За съществуването на всеки обект са необходими някои ресурси, предимно памет. Когато даден обект вече не е необходим, той трябва да бъде унищожен, така че ресурсите, които заема, да могат да бъдат предоставени на други. В прости ситуации задачата не изглежда трудна: създавате обект, използвате го толкова дълго, колкото е необходимо, и след това го унищожавате. На практика обаче често се срещат по-сложни ситуации.

Да кажем, например, че разработвате система за управление на въздушния трафик. (Същият модел се прилага и за контрол на тара в склад, или система за отдаване под наем на видео, или развъдник за бездомни животни.) Първоначално всичко изглежда просто: създава се самолетен контейнер, след което се изгражда нов самолет, който се поставя в контейнер от определена зона за контрол на въздушното движение. Що се отнася до освобождаването на ресурси, съответният обект просто се унищожава, когато самолетът напусне зоната за проследяване.

Но може би има друга система за регистрация на въздухоплавателни средства и тези данни не изискват толкова голямо внимание като Главна функцияуправление. Може би това са записите на полетните планове на всички малки самолети, напускащи летището. Така се появява вторият контейнер за малки самолети; всеки път, когато в системата се създава нов самолетен обект, той също се включва във втория контейнер, ако самолетът е малък. След това някакъв фонов процес работи с обектите в този контейнер в моментите на минимална натовареност.

Сега задачата става по-сложна: как да разберете кога да изтриете обекти? Дори и да сте готови с обекта, възможно е друга система все още да взаимодейства с него. Същият въпрос възниква в редица други ситуации и в софтуерни системи, където е необходимо изрично изтриване на обекти след приключване на работата с тях (например в C++), става доста сложно.

Къде се съхраняват данните за обекта и как се определя техният живот? AT C++Ефективността е на първо място, така че на програмиста е даден избор. За постижение максимална скоростмястото на изпълнение и продължителността на живота могат да бъдат определени по време на писане на програмата. В този случай обектите се изтласкват в стека (такива променливи се извикват автоматичен) или към статичната зона за съхранение. Така че основният фактор е скоростта на създаване и унищожаване на обекти, а това може да бъде безценно в някои ситуации. Това обаче идва с цената на гъвкавост, тъй като броят на обектите, техният живот и типовете трябва да бъдат известни точно на етапа на проектиране на програмата. При решаване на проблеми с по-широк профил - разработване на системи за автоматизирано проектиране
(CAD), контрол на инвентара или контрол на въздушното движение - този подход може да е твърде ограничен.

Вторият начин е динамично създаване на обекти в област от паметта, наречена "купчина" ( купчина). В този случай броят на обектите, техните точни типове и живот остават неизвестни до стартирането на програмата. Всичко това се определя "в движение", докато програмата работи. Ако имате нужда от нов обект, просто го създавате в купчината, когато е необходимо. Тъй като купчината се управлява динамично, заделянето на памет от купчината по време на изпълнение на програмата отнема много повече време, отколкото при разпределянето на памет в стека. (Необходима е само една машинна инструкция за разпределяне на памет в стека, преместване на указателя на стека надолу и освобождаването му се извършва чрез преместване на този указател нагоре. Времето, необходимо за разпределяне на памет в стека, зависи от структурата на хранилището.)

Динамичният подход предполага, че обектите са големи и сложни, така че допълнителното време, необходимо за разпределяне и освобождаване на памет, няма да окаже значително влияние върху процеса на тяхното създаване. Тогава допълнителната гъвкавост е много важна за решаването на основни проблеми с програмирането.

AT Javaизползва се само вторият подход Примитивните типове, които ще бъдат обсъдени по-нататък, са специален случай.. Ключовата дума се използва всеки път, когато се създава обект. нов за изграждане на динамичен екземпляр.

Има обаче още един фактор, а именно продължителността на живота на обекта. В езици, които поддържат създаване на обекти в стека, компилаторът определя колко време се използва даден обект и може автоматично да го унищожи. Въпреки това, когато обект е създаден в купчината, компилаторът няма представа за живота на обекта. На езици като C++, унищожаването на обекта трябва да бъде изрично рамкирано в програмата; ако това не бъде направено, възниква изтичане на памет (често срещан проблем в програмите C++). AT Javaима механизъм т.нар събиране на боклук; той автоматично открива, когато даден обект вече не се използва и го унищожава. Събирачът на отпадъци е много удобен, защото спестява на програмиста много проблеми. По-важното е, че събирачът на боклук ви дава много повече увереност, че коварният проблем с изтичането на памет не се е промъкнал във вашата програма (което донесе повече от един проект в C++).

AT Javaсъбирачът на боклук е проектиран така, че да може сам да се справи с проблема за освобождаване на памет (това не засяга други аспекти на края на живота на обекта). Събирачът на отпадъци "знае" кога даден обект вече не се използва и използва знанията си, за да освободи автоматично паметта. Поради този факт (заедно с факта, че всички обекти наследяват от един базов клас Обект и се създават само в купчината) Javaмного по-лесно от програмирането C++. Разработчикът трябва да вземе по-малко решенияи преодолявайте по-малко препятствия.

Обработка на изключения: Справяне с грешки

От ранните дни на езиците за програмиране обработката на грешки е една от най-трудните теми. Много е трудно да се разработи добър механизъм за обработка на грешки, така че много езици просто игнорират този проблем, оставяйки го на разработчиците на софтуерни библиотеки. Последните предоставят половинчати решения, които работят в много ситуации, но често могат просто да бъдат заобиколени (обикновено като просто не им се обръща внимание). Основният проблем с много механизми за обработка на изключения е, че те разчитат на съзнателното придържане на програмиста към правила, които не се прилагат от езика. Ако програмистът е невнимателен - а това често се случва, когато работата бърза - той лесно може да забрави за тези механизми.

Механизмът за обработка на изключения вгражда обработката на грешки направо в езика за програмиране или дори в операционна система. Изключение е обект, който е хвърлен на мястото на грешка, който след това може да бъде "уловен" от подходящ манипулатор на изключения, предназначен за определени типове грешки. Обработката на изключения изглежда дефинира паралелен път за изпълнение на програмата, който влиза в сила, когато нещо не върви по план. И тъй като дефинира отделен път за изпълнение, кодът за обработка на грешки не се смесва с нормалния код. Това опростява писането на програми, тъй като не е необходимо постоянно да проверявате за възможни грешки. В допълнение, изключението не е като цифров код за грешка, върнат от метод, или флаг, зададен в случай на проблемна ситуация, последният може да бъде игнориран. Изключението не може да бъде пренебрегнато, то е гарантирано, че ще бъде обработено някъде. И накрая, изключенията позволяват възстановяване на нормалната работа на програмата след неправилна операция. Вместо просто да прекратите програмата, можете да коригирате ситуацията и да продължите да я изпълнявате; като по този начин повишава надеждността на програмата.

Механизъм за обработка на изключения в Javaсе отличава от останалите езици, защото е вграден в езика от самото начало и е отговорност на разработчика да го използва. Това е единственият приемлив начин за докладване на грешки. Ако не напишете код за правилно обработване на изключения, ще получите грешка по време на компилиране. Такъв последователен подход понякога значително опростява обработката на грешки.

Струва си да се отбележи, че обработката на изключения не е характеристика на обектно-ориентиран език, въпреки че в тези езици изключението обикновено се представя от обект. Такъв механизъм е съществувал още преди появата на обектно-ориентираните езици.

Паралелно програмиране

Една от основните концепции на програмирането е идеята да се правят няколко неща едновременно. Много задачи изискват програмата да прекъсне текущата си работа, да реши някаква друга задача и след това да се върне към основния процес. Проблемът беше решен по различни начини.
Първоначално програмистите, които познаваха архитектурата на машината, написаха процедури за обработка на прекъсвания, тоест спирането на основния процес беше извършено на хардуерно ниво. Това решение работи добре, но беше сложно и немобилно, което направи много по-трудно пренасянето на такива програми към нови типове компютри.

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

Обикновено нишките просто определят как се разпределя времето на един процесор. Но ако операционната система поддържа множество процесори, всяка нишка може да бъде присвоена на отделен процесор; така се постига истински паралелизъм. Една от хубавите характеристики на паралелизма на езиково ниво е, че програмистът не трябва да знае дали има един или повече процесори в системата. Програмата е логично разделена на нишки и ако машината има повече от един процесор, тя работи по-бързо без специални настройки.

Всичко това създава впечатлението, че потоците са много лесни за използване. Но има уловка: споделени ресурси. Ако множество нишки се опитат да получат достъп до един и същи ресурс едновременно, възникват проблеми. Например, два процеса не могат едновременно да изпращат информация към принтер. За да се предотврати конфликт, споделените ресурси (като принтер) трябва да бъдат заключени, докато се използват. Нишката заключва ресурса, завършва операцията си и след това освобождава заключването, така че някой друг да има достъп до ресурса.

Поддръжка на едновременност, вградена в езика Java, и с изхода Java SE5добави значителна поддръжка на ниво библиотека.

Javaи Интернет

Ако Javaе друг език за програмиране, възниква въпросът защо е толкова важен и защо се представя като революционна стъпка в разработката на софтуер? От гледна точка на традиционните програмни задачи отговорът не е очевиден. Въпреки че езикът Javaполезен при изграждане на самостоятелни приложения, най-важното му приложение беше и си остава програмирането за мрежата световната мрежа.

Какво е мрежата?

На пръв поглед мрежата изглежда доста мистериозна поради изобилието от новомодни термини като „сърфиране“, „присъствие“ и „начални страници“. За да разберете какво е това, е полезно да разберете голямата картина - но първо трябва да разберете взаимодействието на клиент/сървър системите, които са едни от най- предизвикателни задачикомпютърни изчисления.

Клиент/сървър компютри

Основната идея зад системите клиент/сървър е, че имате централизирано хранилище на информация - обикновено под формата на база данни - и тази информация се доставя при поискване от някаква група хора или компютри. В система клиент/сървър, централизирано хранилище на информация играе ключова роля, което обикновено позволява данните да бъдат модифицирани по такъв начин, че тези промени да се разпространят до потребителите на информацията. Всички заедно: хранилище на информация, софтуер, който разпространява информация, а компютърът(ите), на който се съхраняват софтуерът и данните, се нарича сървър. Софтуерът на машината на потребителя, който комуникира със сървъра, получава информацията, обработва я и след това я показва по подходящ начин, се нарича клиент.

Така че основната концепция за изчисления клиент/сървър не е толкова сложна. Проблемите възникват, защото един сървър се опитва да обслужва много клиенти едновременно. Обикновено в решението е включена система за управление на база данни и разработчикът се опитва да "оптимизира" структурата на данните, като ги разпределя в таблици. Освен това системата често позволява на клиента да добавя нова информация към сървъра. А това означава, че новата информация на клиента трябва да бъде защитена от загуба при съхранение в базата данни, както и от възможността да бъде презаписана с данни от друг клиент. (Това се нарича обработка на транзакции.) Когато промените клиентския софтуер, трябва не само да го компилирате и тествате, но и да го инсталирате на клиентски машини, което може да бъде много по-трудно и скъпо, отколкото си мислите. Особено трудно е да се организира поддръжка за много различни операционни системи и компютърни архитектури. И накрая, необходимо е да се вземе предвид най-важният факторпроизводителност: сървърът може да получава стотици заявки едновременно и най-малкото забавяне заплашва със сериозни последствия. За да намалят забавянето, програмистите се опитват да разпространяват изчисления, често дори на клиентската машина, а понякога и да ги прехвърлят на допълнителни сървърни машини, използвайки т.нар. междинен софтуер (междинен софтуер). (Прокси програмите също улесняват поддържането на програми.)

Простата идея за разпространение на информация между хората има толкова много нива на сложност в изпълнението си, че като цяло решението й изглежда непостижимо. И все пак е жизненоважен: около половината от всички програмни задачи са базирани на него. Той участва в решаването на проблеми, вариращи от обработка на поръчки и транзакции с кредитни карти до разпространение на всякакъв вид данни - научни, държавни, борсови котировки ... списъкът е безкраен. В миналото трябваше да създадете отделно решение за всяка нова задача. Тези решения не са лесни за създаване, още по-трудни за използване и потребителят трябваше да научи нов интерфейс с всяка нова програма. Задачата на изчисленията клиент/сървър се нуждае от по-широк подход.

Мрежата е като гигантски сървър

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

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

Инструментите за сърфиране в мрежата (браузърите) бяха голяма крачка напред: те въведоха концепцията за информация, която се показва по един и същи начин на всеки тип компютър. Първите браузъри обаче бяха все още примитивни и бързо престанаха да отговарят на изискванията. Те се оказаха неособено интерактивни и забавяха работата както на сървърите, така и на интернет като цяло – за всяко действие, изискващо програмиране, трябваше да изпратите информация на сървъра и да изчакате той да я обработи. Понякога трябваше да изчакате няколко минути само за да разберете, че сте пропуснали една буква в заявката. Тъй като браузърът беше само програма за преглед, той не можеше да изпълнява дори най-простите програмни задачи. (От друга страна, това гарантира сигурност - потребителят е защитен от стартиране на програми, съдържащи вируси или грешки.)

Предприети са различни подходи за решаване на тези проблеми. Като начало стандартите за показване на графики са подобрени, така че браузърите да могат да показват анимации и видеоклипове. Останалите задачи изискваха възможност за стартиране на програми на машината на клиента, в браузъра. Това се нарича програмиране от страна на клиента.

Програмиране от страна на клиента

Първоначално системата за взаимодействие сървър-браузър е проектирана за интерактивно съдържание, но поддръжката на тази интерактивност е изцяло поверена на сървъра. Сървърът генерира статични страници за браузъра на клиента, който просто ги обработва и показва. Стандартен HTMLподдържа най-простите въвеждания: текстови полета, радио бутони, квадратчета за отметка, списъци и падащи менюта, заедно с бутони, които могат да правят само две неща: да нулират данните от формуляра и да ги изпращат на сървъра. Изпратената информация се обработва от интерфейса CGI (общ интерфейс на шлюза), поддържан от всички уеб сървъри. Текстът на заявката показва CGIкак да се справя с данните. Най-често при поискване се стартира програма от директорията cgi-bin на сървъра. (В реда с адреса на страницата в браузъра, след като изпратите данните от формуляра, понякога можете да видите подниза в бъркотията от знаци cgi-bin.) Такива програми могат да бъдат написани на почти всички езици. Често използван Perl, тъй като е текстово ориентиран, а също и интерпретиран език, следователно може да се използва на всеки сървър, независимо от типа процесор или операционна система. Обаче езикът Python(любимият ми език - отидете на www.Python.org) постепенно отвоюва "територия" от него поради силата и простотата си.

Много мощни уеб сървъри днес работят изцяло на базата на CGI; по принцип тази технология ви позволява да решите почти всеки проблем. Въпреки това, уеб сървъри, изградени върху CGIпрограмите са трудни за поддръжка и имат проблеми с отзивчивостта. Време за реакция CGI-програма зависи от количеството изпратена информация, както и от натоварването на сървъра и мрежата. (Поради всички споменати стартирания CGIпрограмата може да отнеме много време). Първите дизайнери на мрежата не са предвидили колко бързо ще бъдат изчерпани ресурсите на системата, докато се използва в различни приложения. Например, в него е почти невъзможно да се показват графики в реално време, тъй като при всяка промяна в ситуацията е необходимо да се създаде нов GIF файл и да се прехвърли на клиента. Без съмнение сте имали собствени горчиви преживявания - например от простото изпращане на данни от формуляр. Натискате бутон за изпращане на информация; сървърът стартира CGI- програма, която открива грешка, генерира HTML- страница, която ви разказва за това и след това изпраща тази страница във вашата посока; трябва да въведете отново данните и да опитате отново. Това не само е бавно, но е просто неелегантно.

Проблемът се решава чрез програмиране от страна на клиента. По правило браузърите работят на мощни компютри, способни да решават широк спектър от задачи и със стандартен подход, базиран на HTMLкомпютърът просто чака да му бъде сервирана следващата страница. С програмирането от страна на клиента браузърът получава цялата работа, която може да свърши, а за потребителя това се превръща в по-бързо сърфиране в мрежата и по-добра интерактивност.

Обсъждането на клиентското програмиране обаче не се различава много от дискусиите за програмирането като цяло. Условията са същите, но платформите са различни: браузърът прилича на силно съкратена операционна система. Така или иначе трябва да програмирате, така че програмирането от страна на клиента създава шеметен набор от проблеми и решения. Този раздел завършва с преглед на някои от проблемите и подходите, присъщи на програмирането от страна на клиента.

Разширителни модули

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

Скриптови езици

Развитието на плъгините доведе до появата на много скриптови езици. Използвайки скриптов език, вие вграждате клиентската програма директно в HTML-страница, а модулът, който обработва този език, се активира автоматично при прегледа му. Скриптовите езици обикновено са доста лесни за научаване; по същество кодът на скрипта е текстът, който е част от HTML-страници, така че се зарежда много бързо като част от една заявка към сървъра по време на извличане на страница. Цената за това е, че всеки може да види (и открадне) вашия код. Въпреки това е малко вероятно да напишете нещо емулирано и сложно на скриптови езици, така че проблемът с копирането на код не е толкова лош.

Скриптов език, който се поддържа от почти всеки браузър без инсталиране на допълнителни модули JavaScript(има много малко общо с Java; името беше използвано, за да се "грабне" парче успех Javaна пазара). За съжаление, оригиналните изпълнения JavaScriptв различните браузъри бяха доста различни един от друг и дори между различни версии на един и същ браузър. Стандартизация JavaScriptвъв формата на ECMAScriptбеше полезен, но отне време, докато поддръжката му се появи във всички браузъри (в допълнение, компанията Microsoftактивно насърчават собствения си език VBScript, смътно наподобяващ JavaScript). В общия случай разработчикът трябва да се ограничи до минимум възможности JavaScriptтака че кодът да работи гарантирано във всички браузъри. Относно обработката на грешки и кода за отстраняване на грешки JavaScript, тогава това в най-добрия случай е трудна задача. Едва наскоро разработчиците успяха да създадат наистина сложна системанаписано в JavaScript(търговско дружество Google, обслужване Gmail), и изискваше най-висок ентусиазъм и опит.

Това показва, че скриптовите езици, използвани в браузърите, са проектирани за набор от специфични задачи, главно за създаване на по-богат и по-интерактивен графичен потребителски интерфейс ( GUI). Скриптовият език обаче може да се използва за 80% от клиентските програмни задачи. Вашата задача може да е само в тези 80%. Тъй като скриптовите езици улесняват и ускоряват създаването програмен код, първо трябва да обмислите точно такъв език, преди да преминете към по-сложни технологични решения като Java.

Java

Ако скриптовите езици поемат 80% от задачите на клиентското програмиране, тогава кой може да се справи с останалите 20%? За тях най-популярното решение днес е Java. Той не само е мощен език за програмиране, проектиран с оглед на сигурността, съвместимостта с платформата и интернационализацията, но също така е постоянно развиващ се инструмент с нови функции и библиотеки, които елегантно пасват на традиционно сложни програмни задачи: многозадачност, достъп до бази данни, мрежово програмиране и разпределено изчисление. Клиентско програмиране Javaсе свежда до разработването на аплети, както и до използването на пакета Java Web Start.

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

Алтернативи

Честно казано, аплети Javaне оправда първоначалния ентусиазъм. При първа поява Javaвсички бяха много ентусиазирани относно аплетите, защото позволяваха сериозно програмиране от страна на клиента, повишена отзивчивост и намалена честотна лента за интернет приложения. Предсказваше се, че аплетите ще имат страхотно бъдеще.

Наистина, редица много интересни аплети могат да бъдат намерени в мрежата. И все пак масовият преход към аплети не се състоя. Вероятно основният проблем беше изтеглянето на пакета от 10 MB за инсталиране на средата Java Runtime Environment (JRE)твърде плашещо за обикновения потребител. Фактът, че компанията Microsoftне се включи JREв доставката Internet Explorer, най-накрая реши съдбата на аплетите. Както и да е, аплети Javaне са широко използвани.

Въпреки това, аплети и приложения Java Web Startса много полезни в някои ситуации. Ако конфигурацията на компютрите на крайните потребители е под контрол (например в организации), използването на тези технологии за разпространение и актуализиране на клиентски приложения е напълно оправдано; спестява много време, труд и пари (особено при чести актуализации).

.NET и C#

За известно време основният претендент Java-аплетите се считат за компоненти ActiveXот компания Microsoft, въпреки че изискваха за работата си присъствие на машината на клиента Windows. Сега Microsoftпротивопоставени Javaпълноправни конкуренти: това е платформа .NETи език за програмиране ОТ#. Платформа .NETе приблизително същото като виртуална машина Java(JVM) и библиотеки Java, и езика ОТ#има ясна прилика с езика Java. Без съмнение това е най-доброто, което Microsoft е създал в областта на програмните езици и среди. Разбира се, разработчиците Microsoftимаше известно предимство; те видяха това в Javaуспяха и какво не, и можеха да надграждат върху тези факти, но резултатът се оказа доста достоен. За първи път от раждането си, Javaимаше истински конкурент. Разработчици от слънцетрябваше да разгледам ОТ#, разберете защо програмистите биха искали да преминат към този език и положете всички усилия да го подобрите сериозно Javaв Java SE5.

AT този моментосновният въпрос е дали Microsoft ще позволи пълно пренасяне на .NETкъм други платформи. AT Microsoftтвърдят, че няма проблем в това и в проекта моно()осигурява частично изпълнение .NETза linux. Въпреки това, тъй като това внедряване е незавършено, засега Microsoftне решава да изхвърли някоя част от него, за да заложи .NETкато крос-платформена технология е все още рано.

Интернет и интранет

Мрежата предоставя най-общото решение за задачи клиент/сървър, така че има смисъл да се използва същата технология за решаване на конкретни проблеми; това е особено вярно за класическото взаимодействие клиент/сървър в компанията. Традиционният подход клиент/сървър има проблеми с разликите в типовете клиентски компютри, добавяйки към тях трудността при инсталиране на нови програми за клиенти; и двата проблема се решават чрез браузъри и програмиране от страна на клиента. Когато уеб технологията се използва за формиране на информационна мрежа в рамките на една компания, такава мрежа се нарича интранет. Интранетът осигурява много по-голяма сигурност от интернет, защото можете физически да контролирате достъпа до сървърите на вашата компания. По отношение на обучението, за някой, който разбира концепцията на браузъра, е много по-лесно да разбере различни страници и аплети, така че времето за овладяване на нови системи да бъде намалено.

Въпросът за сигурността ни отвежда до една от посоките, които автоматично възникват в клиентското програмиране. Ако вашата програма работи в Интернет, тогава не знаете на каква платформа ще работи. Трябва да се обърне специално внимание, за да се избегне разпространението на неправилен код. Тук имаме нужда от кросплатформени и сигурни решения, като например Javaили скриптов език.

Интранетите имат други ограничения. Доста често всички машини в мрежата работят на платформата Intel/Windows. В интранет вие носите отговорност за качеството на вашия код и можете да коригирате грешки, когато бъдат открити. В допълнение, може вече да имате колекция от решения, които са доказали, че работят в по-традиционни системи клиент/сървър, докато новият софтуер трябва да се инсталира ръчно на клиентската машина с всяко надграждане. Времето, прекарано в актуализации, е най-силният аргумент в полза на браузърните технологии, при които актуализациите се правят невидимо и автоматично (същото може да се направи Java Web Start). Ако се занимавате с поддръжка на интранет, по-разумно е да поемете по пътя, който ви позволява да използвате това, което вече имате, без да се налага да пренаписвате програми на нови езици.

Когато се сблъскате с огромното количество задачи по програмиране на клиента, които могат да спънат всеки дизайнер, най-добре е да ги оцените по отношение на съотношението цена/полза. Помислете за ограниченията на вашия проблем и се опитайте да си представите най-краткия начин за разрешаването му. Тъй като клиентското програмиране все още е програмиране, технологиите за разработка, които обещават най-бързото решение, винаги са актуални. Тази проактивна нагласа ще ви даде възможност да се подготвите за неизбежните проблеми на програмирането.

Програмиране от страна на сървъра

Нашата дискусия заобиколи темата за програмирането от страна на сървъра, което мнозина смятат за най-много силна страна Java. Какво се случва, когато изпратите заявка до сървъра? По-често заявката се свежда до проста заявка „изпратете ми този файл“. След това браузърът обработва файла по подходящ начин: as HTML-страница като снимка като Java-аплет, като скрипт и др.

По-сложната заявка към сървъра обикновено включва достъп до базата данни. В най-честия случай се изисква сложно търсене в база данни, резултатите от което след това се преобразуват от сървъра в HTML-страница и ви изпраща. (Разбира се, ако клиентът може да извърши някакво действие с помощта на Javaили скриптов език, данните могат да бъдат обработени от него, което ще бъде по-бързо и ще намали натоварването на сървъра.) Или може да се наложи да се регистрирате в базата данни, когато се присъединявате към група, или плащане, което ще изисква промени в базата данни. Такива заявки трябва да бъдат обработени от някакъв код на сървъра; като цяло това се нарича програмиране от страна на сървъра. Традиционно програмирането на сървъра се извършва на Perl, Python, C++или друг език, който ви позволява да създавате програми CGI, но има и по-интересни варианти. Те включват тези, базирани на Javaуеб сървъри, които ви позволяват да правите програмиране от страна на сървъра Javaизползвайки така наречените сервлети. Сервлети и тяхното потомство JSPs, са двете основни причини, поради които компаниите за уеб съдържание преминават към Java, главно защото решават проблеми с несъвместимостта на различни браузъри.

Въпреки всички приказки за Javaкато интернет език за програмиране, Javaвсъщност това е пълноправен език за програмиране, способен да решава почти всички проблеми, решени на други езици. Предимства Javaне се ограничават до добра преносимост: това е пригодност за решаване на проблеми с програмирането, и устойчивост на грешки, и голяма стандартна библиотека, и множество разработки на трети страни - както съществуващи, така и постоянно възникващи.

Резюме

Знаете как изглежда една процедурна програма: дефиниции на данни и извиквания на функции. Откриването на целта на такава програма изисква усилие, като разгледате функциите и създадете голяма картина в ума си. Именно поради това създаването на такива програми изисква използването на междинни инструменти - сами по себе си те са по-ориентирани към компютъра, а не към решаваната задача.

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

Обектно-ориентирано програмиране и език Javaне са подходящи за всеки. Много е важно първо да разберете вашите нужди, за да решите дали да преминете към Javaили е по-добре да изберете друга система за програмиране (включително тази, която използвате в момента). Ако знаете, че в обозримо бъдеще ще се сблъскате с много специфични нужди или работата ви ще бъде обект на ограничения, които Javaне се справя, по-добре е да обмислите други възможности (по-специално препоръчвам да разгледате по-отблизо езика Python,). Избор Java, трябва да разберете какви други опции са налични и защо сте избрали този път.

Размяна на съобщения:

Изпратете директно на тази страна: http://website/javabooks/Thinking_in_Java_4th_edition.html Изпратете BB кодове: Thinking_in_Java_4th_edition
html-съобщение: