- •Введение
- •Как получить исходные тексты
- •Что требуется знать для чтения книги
- •Предисловие к первому изданию
- •Благодарности
- •1.3.Новая парадигма
- •Что читать дальше
- •Упражнения
- •2.7.3.Зацепление и связность
- •2.9. Выбор представления данных
- •Упражнения
- •Глава 3 Классы и методы
- •Упражнения
- •Глава 4 Сообщения, экземпляры и инициализация
- •Упражнения
- •Глава 5 Учебный пример: задача о восьми ферзях
- •Упражнения
- •Глава 6 Учебный пример: игра «Бильярд»
- •Упражнения
- •Глава 7 Наследование
- •7.6.Издержки наследования
- •Упражнения
- •Глава 8 Учебный пример: Пасьянс
- •8.4.1.Основание SuitPile
- •8.4.2.Колода DeckPile
- •Упражнения
- •9.1.1. «Быть экземпляром» и «включать как часть»
- •Упражнения
- •Глава 10 Подклассы и подтипы
- •Упражнения
- •Глава 11 Замещение и уточнение
- •Упражнения
- •Глава 12 Следствия наследования
- •Упражнения
- •Глава 13 Множественное наследование
- •13.1.Комплексные числа
- •Литература для дальнейшего чтения
- •Упражнения
- •Глава 14 Полиморфизм
- •Полиморфные переменные
- •Виртуальное и невиртуальное переопределение
- •Параметрическая перегрузка
- •Отложенные методы в C++
- •Обобщенные функции и шаблоны
- •Полиморфные переменные
- •Отложенные методы в Object Pascal
- •Полиморфные переменные
- •Отложенные методы в Objective-C
- •Полиморфные переменные
- •Отложенные методы в Smalltalk
- •Упражнения
- •Глава 15 Учебный пример: контейнерные классы
- •Упражнения
- •Глава 16 Пример: STL
- •Упражнения
- •Глава 17 Видимость и зависимость
- •Родственные экземпляры
- •Дружественные функции
- •Пространства имен
- •Постоянные члены
- •Упражнения
- •Глава 18 Среды и схемы разработки
- •18.1.1. Java API
- •Упражнения
- •19.5.Класс application
- •19.5.1.Класс button
- •Упражнения
- •Глава 20 Новый взгляд на классы
- •20.2.2.Класс Class
- •Упражнения
- •Глава 21 Реализация объектно-ориентированных языков
- •Литература для дальнейшего чтения
- •Упражнения
- •А.1. «Задача о восьми ферзях» на языке Apple Object Pascal
- •A.3. «Задача о восьми ферзях» на языке Java
- •A.3.1. HTML-файл для апплета Java
- •A.4. «Задача о восьми ферзях» на языке Objective-C
- •A.5. «Задача о восьми ферзях» на языке Smalltalk
- •Б.1. Версия без использования наследования
- •Б.2. Версия с использованием наследования
- •Глоссарий
converted to PDF by BoJIoc
<applet code="QueenSolver.class" width=500 height=500>
Если Вы увидите этот текст, ваш броузер не обеспечивает выполнение апплетов Java.
</applet>
<hr>
<p>
Броузер первоначально изображает только одно решение. После каждого щелчка мышью будет появляться новое решение. <p>
<a href="QueenSolver.java">Исходный текст программы.</a> </body>
</html>
A.4. «Задача о восьми ферзях» на языке Objective-C
В данном примере классы Queen и SentinelQueen начинают прямо с раздела implementation без предшествующей части interface. Это вызовет предупреждающее сообщение со стороны компилятора, но не сообщение об ошибке.
/*
Задача «Восемь ферзей», язык Objective-C
Автор: Тимоти Бадд, университет штата Орегон, 1996
*/
#include
#include
/*
«Сторожевой» ферзь находится левее самого левого ферзя
*/
@implementation SentinelQueen : Object - (int) advance
{
/* ничего не делать */ return 1;
}
- (int) findSolution
{
/* ничего не делать */ return 1;
}
-(void) print
{
/* ничего не делать */
}
-(int) canAttack: (int) testRow column: (int) testColumn;
{
/* нельзя атаковать */ return 0;
}
@end
@interface Queen : Object { /* поля данных */
int row; int column; id neighbor;
}
/* методы */
converted to PDF by BoJIoc
-(void) initialize: (int) c neighbor: ngh;
-(int) advance;
-(void) print;
-(int) canAttack: (int) testRow column: (int) testColumn;
-(int) findSolution;
@end
@implementation Queen : Object
- (void) initialize: (int) c neighbor: ngh;
{
/* задать постоянные значения */ column = c;
neighbor = ngh; row = 1;
}
- (int) advance
{
/* сначала попробовать следующую горизонталь */ if (row < 8)
{
row = row + 1;
return [ self findSolution ];
}
/* дальше двигаться нельзя, подвинем соседа */ if ( ! [ neighbor advance ] )
return 0;
/* начать снова с первой горизонтали */ row = 1;
return [ self findSolution ];
}
-(void) print
{
if (neighbor)
[ neighbor print ];
print("column %d row %d\n", column, row);
}
-(int) canAttack: (int) testRow column: (int) testColumn { int columnDifference;
/* если та же горизонталь, то можно аттаковать */ if (row == testRow)
return 1;
columnDifference = testColumn — column; if ((row + columnDifference == testRow) ||
(row — columnDifference == testRow)) return 1;
return [ neighbor canAttack:testRow column: testColumn ];
}
-(int) findSolution
{
/* если сосед может атаковать, то продвинуться */ while ( [ neighbor canAttack:row column: column ] )
if ( ! [ self advance ] ) return 0;
/* в противном случае мы в безопасности */ return 1;
}
@end
main()
converted to PDF by BoJIoc
{
id lastQueen, neighbor; int i;
//создать и инициализировать ферзей neighbor = [ SentinelQueen new ]; for (i = 1; i <= 8; i++)
{
lastQueen = [ Queen new ];
[ lastQueen initialize: i neighbor: neighbor ]; [ lastQueen findSolution ];
neighbor = lastQueen;
}
//теперь напечатать решение
[ lastQueen print ];
}
A.5. «Задача о восьми ферзях» на языке Smalltalk
Класс SentinelQueen не имеет переменных экземпляра. Класс использует следующие методы:
advance
false
canAttack: row column: column false
result
List new
Класс Queen имеет три переменные экземпляра: row, column, neighbor. Определены следующие методы класса:
setColumn: aNumber neighbor: aQueen
"инициализировать поля данных " column := aNumber.
neighbor := aQueen.
"найти первое решение "
row := 1.
canAttack: testRow column: testColumn Ѕ columnDifference Ѕ columnDifference := testColumn — column.
(((row = testRow) or:
[ row + columnDifference = testRow]) or: [ row — columnDifference = testRow])
ifTrue: [ true ].
neighbor canAttack: testRow column: testColumn advance
"сначала испытать следующую строку " (row < 8)
ifTrue: [ row := row + 1. self findSolution ].
"нельзя двигаться дальше, подвинем соседа " (neighbor advance )
ifFalse: [ false ]. row := 1.
self findSolution
findSolution
[ neighbor canAttack: row column: column ] whileTrue: [ self advance
ifFalse: [ false ] ]. true
converted to PDF by BoJIoc
result
neighbor result; addLast: row
Чтобы найти решение, выполняется следующий метод: run | lastQueen |
lastQueen <- SentinelQueen new.
1 to: 8 do: [:i Ѕ lastQueen <- (Queen new) setColumn: i neighbor: lastQueen. lastQueen findSolution ].
'результат получен' print.
lastQueen result do: [:x | x print. ' ' print ]. Char newline print.
Приложение Б
Исходный код игры «Бильярд»
В этом приложении приведен полный код игры «Бильярд», описанной в главе 6. Код дается для версии языка Apple Object Pascal.
Б.1. Версия без использования наследования
*
Программа, моделирующая игру «Бильярд» Демонстрирует объектные свойства языка Object Pascal
Автор: Тимоти Бадд, университет штата Орегон, сентябрь 1995 *)
Program billiards; USES
Windows; type
Ball = object
(* данные, которые поддерживают объекты-шары *) link : Ball;
region : Rect; filler : integer; direction : real; energy : real;
(* процедура инициализации *) procedure initialization (x, y : integer);
(* методы общего характера *)
procedure draw; procedure erase; procedure update;
procedure hitBy(aBall : Ball);
procedure setCenter(newx, newy : integer); procedure setDirection(newDirection : real);
(* вернуть координаты x и y центра шара *) function x : integer;
function y : integer; end;
Wall = object
(* поля данных *)
link : Wall; region : Rect;
converted to PDF by BoJIoc
(* нечто вроде коэффициента отражения шаров при ударе *) convertFactor : real;
(* процедура инициализации *) procedure initialize
(left, top, right, bottom : integer; cf : real); (* рисуем стенку *)
procedure draw;
(* известить стенку, что в нее попал шар *) procedure hitBy(aBall : Ball);
end;
Hole = object
(* поля данных *) link : Hole;
region : Rect;
(* инициализировать положение лузы *) procedure initialize(x,y : integer);
(* нарисовать лузу *) procedure draw;
(* известить лузу, что в нее попал шар *) procedure hitBy(aBall : Ball);
end; var
cueBall : Ball; saveRack : integer; ballMoved : boolean; listOfHoles : Hole; listOfWalls : Wall; listOfBalls : Ball; theWindow : windowPtr;
procedure Wall.initialize
(left, top, right, bottom : integer; cf : real); begin
(* инициализировать коэффициент отражения *) convertFactor := cf;
(* задать прямоугольник для стенки *) SetRect(region, left, top, right, bottom);
end;
procedure Wall.draw; begin
PaintRect(region);
end;
procedure Wall.hitBy(aBall : Ball); begin
(* отразить шар от стенки *) aBall.setDirection(convertFactor — aBall.direction); end;
procedure Hole.initialize(x, y : integer); var left, top, bottom, right : integer; begin
(* идентифицировать область вокруг точки x, y *)
left |
:= |
y |
:= x — 5; |
top |
— 5; |
||
right |
:= |
y |
:= x + 5; |
bottom |
+ 5; |
SetRect(region, left, top, right, bottom); end;
procedure Hole.draw;
converted to PDF by BoJIoc
begin PaintOval(region);
end;
procedure Hole.hitBy(aBall : Ball); begin
(* забрать у шара энергию *) aBall.energy := 0.0; aBall.erase;
(* передвинуть шар *) if aBall = cueBall then
aBall.setCenter(50,100) else begin
saveRack := saveRack + 1; aBall.setCenter(10 + saveRack*15, 250);
end;
(* обновить изображение шара *) aBall.draw;
end;
procedure Ball.setCenter(newx, newy : integer); var left, top, bottom, right : integer;
begin
(* идентифицировать область вокруг точки x, y *)
left |
:= |
newy |
:= newx — 5; |
top |
— 5; |
||
right |
:= |
newy |
:= newx + 5; |
bottom |
+ 5; |
SetRect(region, left, top, right, bottom); end;
procedure Ball.initialize(x, y : integer); begin
setCenter(x,y);
setDirection(0,0); energy := 0.0;
end;
procedure Ball.setDirection(newDirection : real); begin
direction := newDirection; end;
procedure Ball.erase; begin
EraseRect(region);
end;
procedure Ball.draw; begin
if self = cueBall then
(* нарисовать окружность *) FrameOval(region)
else
(* нарисовать закрашенный круг *) PaintOval(region);
end;
procedure Ball.update; var
hptr : Hole; wptr : Wall; bptr : Ball;
dx, dy : integer;
converted to PDF by BoJIoc
theIntersection : Rect; i : integer;
begin
if energy > 0.5 then begin
ballMoved := true;
(* удалить шар с экрана *) erase;
(* уменьшить энергию *) energy := energy — 0.05;
(* передвинуть шар *)
dx := trunc(5.0*cos(direction)); dy := trunc(5.0*sin(direction)); offsetRect(region, dx, dy);
(* перерисовать шар *) for i := 1 to 25 do
draw;
(* проверить, не попали ли мы в лузу *) hptr := listOfHoles;
while (hptr <> nil) do begin
if SetRect(region, hptr.region, theIntersection) then begin
hptr.hitBy(self); hptr := nil;
end else
hptr :=hptr.link;
end;
(* проверить, не попали ли мы в стенку *) wptr := listOfWalls;
while (wptr <> nil) do begin
if SetRect(region, wptr.region, theIntersection) then begin
wptr.hitBy(self); wptr := nil;
end else
wptr :=wptr.link;
end;
(* проверить, не попали ли мы в другой шар *) bptr := listOfBalls;
while (bptr <> nil) do begin
if (bptr <> self) and
SetRect(region, bptr.region, theIntersection) then begin
bptr.hitBy(self); bptr := nil;
end else
bptr :=bptr.link;
end;
end;
end;
converted to PDF by BoJIoc
function Ball.x : integer; begin
x := (region.left + region.right) div 2; end;
function Ball.y : integer; begin
y := (region.top + region.bottom) div 2; end;
function hitAngle (dx, dy : real) : real; const
PI = 3.14159265359; var na : real;
begin
if (abs(dx) < 0.05) then na := PI/2
else
na := arctan (abs)dy/dx)); if (dx < 0) then
na := PI — na; if (dy < 0) then
na := -na; hitAngle := na;
end;
procedure Ball.hitBy(aBall : Ball); var
da : real; begin
(* уменьшить энергию ударяющегося шара вдвое *) aBall.energy := aBall.energy / 2.0;
(* и прибавить ее к собственной *) energy := energy + aBall.energy;
(* задать новое направление *)
direction := hitAngle(self.x — aBall.x, self.y — aBall.y); (* направить ударившийся шар *)
da := aBall.direction — direction; aBall.setDirection(aBall.direction + da);
(* продолжить обновление состояния *) update;
end;
procedure mouseButtonDown(x, y : integer); var
bptr : Ball; begin
(* придать белому шару некоторую начальную энергию *) cueBall.energy := 20.0;
(* и направление *)
cueBall.setDirection( hitAngle(cueBall.x — x, cueBall.y — y)); (* цикл до тех пор, пока есть что-то движущееся *)
ballMoved := true; while ballMoved do begin
ballMoved := false; bptr := listOfBalls; while bptr <> nil do begin
bptr.update;
bptr := bptr.link;
converted to PDF by BoJIoc
end;
end;
end;
procedure CreateGlobals; var
i, j : integer; newBall : Ball; newWall : Wall; newHole : Hole;
begin
saveRack := 0; listOfWalls := nil; listOfHoles := nil; listOfBalls := nil;
(* создать стенки *) new (newWall);
newWall.initialize(10, 10, 300, 15, 0.0); newWall.link := listOfWalls;
listOfWalls := newWall; new (newWall);
newWall.initialize(10, 200, 300, 205, 0.0); newWall.link := listOfWalls;
listOfWalls := newWall; new (newWall);
newWall.initialize(10, 10, 15, 200, 3.14159265359); newWall.link := listOfWalls;
listOfWalls := newWall; new (newWall);
newWall.initialize(300, 10, 305, 205, 3.14159265359); newWall.link := listOfWalls;
listOfWalls := newWall; (* создать лузы *)
new (newHole); newHole.initialize(15, 15); newHole.link := listOfHoles; listOfHoles := newHole;
new (newHole); newHole.initialize(15, 200); newHole.link := listOfHoles; listOfHoles := newHole;
new (newHole); newHole.initialize(300, 15); newHole.link := listOfHoles; listOfHoles := newHole;
new (newHole); newHole.initialize(300, 200); newHole.link := listOfHoles; listOfHoles := newHole;
(* создать шары *) new (cueBall);
cueBall.initialize(50, 96); cueBall.link := nil; listOfBalls := cueBall; for i := 1 to 5 do
begin
for j := 1 to i do begin
converted to PDF by BoJIoc
new (newBall); newBall.initialize(190 + i*8,
100 + 16*j — 8*i); newBall.link := listOfBalls; listOfBalls := newBall;
end;
end;
end;
procedure drawBoard; var
aWall : Wall;
aBall : Ball;
aHole : Hole; begin
SetPort(theWindow); aWall := listOfWalls; while (aWall <> nil) do begin
aWall.draw;
aWall := aWall.link; end;
aHole := listOfHoles; while (aHole <> nil) do begin
aHole.draw;
aHole := aHole.link; end;
aBall := listOfBalls; while (aBall <> nil) do begin
aBall.draw;
aBall := aBall.link; end;
cueBall.draw;
end;
procedure createWindow; var
name : STR255; winType : integer; windowRect : Rect;
begin
name := 'billiard game'; SetRect(windowRect, 50, 70, 500, 400);
winType := DocumentProc; theWindow :=
NewWindow(nil, windowRect, name, TRUE, winType, WindowPtr(-1), True, LongInt(09));
SelectWindow(theWindow);
showWindow(theWindow);
end;
procedure EVentLoop; var
ignore : boolean; event : eventRecord; localPoint : Point; done : boolean;