программирование

реклама
Объектноориентированное
программирование
7.1 Что такое ООП?
7.2 Объекты и классы
7.3 Скрытие внутреннего
устройства объектов
7.4 Иерархия классов
 К. Поляков, 2011
http://kpolyakov.narod.ru
1
2
Объектноориентированное
программирование
7.1 Что такое ООП?
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
3
Направления развития ПО
• переход от расчетов по формулам к сложным
задачам моделирования систем
• увеличение объемов обрабатываемых данных
• повышение сложности программ, увеличение их
длины (до миллионов строк!)
!
Сложность программ превышает возможности
одного человека.
В итоге:
• коллективная разработка
• каждый делает свою часть независимо от других
• части программы легко «собрать» вместе
?
 К. Поляков, 2011
Как?
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
4
Борьба со сложностью
«Техника борьбы со сложностью известна с античных
времен: “Divide et impera” (разделяй и властвуй)».
Э. Дейкстра
Алгоритмическая
декомпозиция
задача
> 100 000 строк???
подзадача 1
подзадача 2
подзадача 3
подзадача 2.1
подзадача 2.2
подзадача 2.3
Декомпозиция – это разбиение системы на
подсистемы, каждая из которых может изучаться
отдельно.
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
5
Объектный подход
Люди воспринимают мир, состоящий из объектов
(Р. Декарт).
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
6
Абстракция
Абстракция – это выделение существенных
характеристик объекта, отличающих его от
других объектов.
Иллюстрация из книги Г. Буч, Объектно-ориентированный анализ и проектирование с примерами приложений на С++.
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
7
Абстракция в программировании
• программа – это множество
взаимодействующих объектов (моделей)
• объекты имеют свойства и поведение
• объекты не «знают» внутреннее устройство
других объектов
А
Б1
Б Б2
В1
В2
В3
Б3
Г
Г2
Г1
 К. Поляков, 2011
В
Объектная
декомпозиция
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
8
Абстракция в программировании
Верхний уровень:
• определить, ЧТО делает каждый объект
• определить ИНТЕРФЕЙС – способ обмена
данными между объектами
Б
интерфейс
В
Нижний уровень:
• определить, КАК работают объекты
Б1
Б Б2
Б3
 К. Поляков, 2011
интерфейс
интерфейс
В1
В
В2
В3
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
9
Что такое ООП?
Программирование, основанное на моделировании задачи
реального мира как множества взаимодействующих
объектов, принято называть объектно-ориентированным
программированием (ООП).
уточнение следует…
 К. Поляков, 2011
http://kpolyakov.narod.ru
10
Объектноориентированное
программирование
7.2 Объекты и классы
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
11
Объектно-ориентированный анализ (ООА)
Задачи:
• выделить взаимодействующие объекты
• определить их существенные свойства
?
Какие свойства существенные?
• описать команды, которые объекты могут
выполнить (поведение)
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
12
Что такое объект?
Объектом можно назвать то, что имеет четкие
границы и обладает состоянием и
поведением.
!
Состояние объекта определяет его поведение!
Примеры:
«Подпрыгни!»
 К. Поляков, 2011
«Пли!»
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
13
Объектная модель: пример
Объекты:
• дорога
• машины
?
• светофоры
• …
Описывать каждую машину отдельно?
Класс – это множество объектов, имеющих
общую структуру и общее поведение.
Классы объектов: Дорога, Машина
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
14
Объектная модель: класс Дорога
ширина
длина
название класса
свойства
Дорога
длина
ширина
поведение
(команды, методы)
 К. Поляков, 2011
?
Какое «поведение» могло
бы быть у дороги?
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
15
Объектная модель: класс Машина
Y
V
X
метод
Машина
X (координата)
Y (полоса)
V (скорость)
двигаться
узнать длину
Дорога
длина
ширина
Метод – это процедура или функция,
принадлежащая классу объектов.
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
16
Класс Дорога в программе
Объявление (описание) класса:
от type
type TRoad = class
Length: real;
Width: integer;
end;
поля
Поле – это переменная, принадлежащая объекту.
!
Объект в памяти не создается!
Аналогия: чертёж детали.
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
17
Класс Дорога в программе
Переменная для работы с объектом:
var road: TRoad;
!
road
12345
?
Объект в памяти не создается!
Ссылка – это переменная, в которую можно
записать адрес объекта заданного типа.
адрес 12345
road := TRoad.Create;
Length
0
Width
0
Конструктор – это метод класса, который
вызывается для создания объектов этого класса.
!
Конструктор не нужно объявлять!
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
18
Создание объекта
type TRoad = class
Length: real;
Width: integer;
end;
var road: TRoad;
begin
road := TRoad.Create;
road.Length := 60;
road.Width := 3;
end.
 К. Поляков, 2011
описание класса
переменная-ссылка
вызов конструктора
значения полей
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
19
Новый конструктор
Цель – определить начальные значения при
создании объекта. длина ширина
road := TRoad.Create (60, 3);
Описание класса:
type TRoad = class
Length: real;
Width: integer;
constructor Create(length0: real;
width0: integer);
end;
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
20
Новый конструктор
Реализация (программа) конструктора:
метод класса TRoad
constructor TRoad. Create(length0: real;
width0: integer);
begin
if
length0
> 0 then
Length
:= length0;
:= length0
WidthLength
:= width0;
else Length := 1;
if width0 > 0 then
Width := width0
else Width := 1;
end;
?
!
Что плохо?
Можно обрабатывать ошибки!
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
21
Класс Машина в программе
Машина
X (координата)
Y (полоса)
V (скорость)
двигаться
узнать длину
Дорога
длина
ширина
type TCar = class
X, V: real; { координата, скорость }
Y: integer; { номер полосы }
road: TRoad; { ссылка на дорогу }
procedure move; { двигаться вперед }
end;
адрес объекта
класса
Дорога
Как машина
сможет
узнать длину дороги?
?
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
22
Класс Машина в программе
Равномерное движение: X  X 0  V  t
V – перемещение за 1 интервал.
procedure TCar.move;
begin
X := X + V; { при Δt = 1 }
if X > road.Length then
X := 0;
{ «перескочить» в начало }
end;
длина дороги, по
которой едет машина
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
23
Класс Машина в программе
Добавление конструктора:
адрес дороги
type TCar = class
...
constructor Create(road: TRoad;
y0: integer; v0: real);
end;
полоса
?
 К. Поляков, 2011
скорость
Чему будет равна координата X?
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
24
Основная программа моделирования
const N = 3;
var road: TRoad;
cars: array [1..N] of TCar;
i: integer;
begin
road := TRoad.Create(60, N);
for i:=1 to N do
cars[i] := TCar.Create(road, i, 2.0*i);
repeat
for i:=1 to N do cars[i].move;
{ вывод расположения машин }
until keypressed;
end.
?
 К. Поляков, 2011
Важен ли порядок создания объектов?
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
25
Плюсы и минусы ООП
 основная программа простая и понятная
 каждый класс может разрабатывать
отдельный программист независимо
 можно использовать готовые классы в
других программах
 увеличение длины полной программы (с
учетом описания классов)
 замедление работы
 К. Поляков, 2011
http://kpolyakov.narod.ru
26
Объектноориентированное
программирование
7.3 Скрытие внутреннего
устройства объектов
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
27
Зачем скрывать внутреннее устройство?
 борьба со сложностью
 безопасность внутренних данных
 возможность изменять «внутренности», не
меняя интерфейс
Иллюстрация из книги Г. Буч, Объектно-ориентированный анализ и проектирование с примерами приложений на С++.
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
28
Инкапсуляция
Инкапсуляция («помещение в капсулу») – это
скрытие внутреннего устройства объектов от
других объектов.
Класс Перо (цвет и стиль линий):
type TPen = class
color: string;
end;
!
var pen: TPen;
...
pen := TPen.Create;
pen.color := '#%';
writeln(pen.color);
 К. Поляков, 2011
'FF00FF'
По умолчанию все поля
общедоступные!
доступно всем!
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
29
Инкапсуляция
Скрытие полей:
type TPen = class
private
частный,
FFColor: string;
закрытый
end;
от Field – «поле»
Проблема – нет доступа к полю!
!
pen.color := 'FFFF00';
writeln(pen.color);
!
Для доступа к закрытому полю нужны
методы чтения и записи!
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
30
Методы доступа к полю
type TPen = class
общедоступный
private
метод чтения
FColor: string;
public
function getColor: string;
procedure setColor(newColor: string);
end;
метод записи
Метод чтения:
• функция без параметров
• возвращает значение того же типа, что и поле
Метод записи:
• процедура
• принммает значение того же типа, что и поле
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
31
Методы доступа к полю
function TPen.getColor: string;
begin
Result := FСolor;
end;
procedure TPen.setColor(newColor: integer);
begin
FColor := newColor;
end;
поле
методы
getColor
другой
объект
FColor
setColor
TPen
 К. Поляков, 2011
?
Стало ли лучше?
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
32
Проверка при изменении поля
В случае ошибочного значения установить черный цвет:
procedure TPen.setColor(newColor: integer);
begin
if Length(newColor)
{ ошибочное значение
<> 6} then
FColor := '000000' { чёрный цвет }
else FColor := newColor;
end;
Использование:
pen.setColor('FF00');
{ ошибка: черный }
pen.setColor('FFFF00'); { запись }
writeln(pen.getColor); { чтение }
?
 К. Поляков, 2011
Какие ошибки не обработаны?
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
33
Свойство
pen.сolor := 'FFFF00'; { запись }
writeln(pen.color);
{ чтение }
Свойство – это способ доступа к внутреннему
состоянию объекта, имитирующий обращение к
его внутренней переменной.
поле
методы
свойство
getColor
FColor
TPen
 К. Поляков, 2011
color
setColor
другой
объект
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
34
Свойство
type TPen = class
private
FColor: string;
function getColor
getColor: string ;
procedure setColor
setColor(newColor: string );
public
property сolor: string
read getColor write setColor
setColor;
end;
свойство
тип свойства
метод чтения
метод записи
pen.сolor := 'FFFF00'; { запись }
writeln(pen.color);
{ чтение }
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
35
Изменяем способ хранения данных
FColor: string;
'FF00FF'
'001234'
'0000A5'
методы
поле
FColor: integer;
FF00FF16 = 16711935
123416 = 4460
A516 = 165
свойство
getColor
FColor
color
setColor
TPen
реализация
!
другой
объект
интерфейс
Можно менять реализацию, не меняя интерфейс!
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
36
Изменяем способ хранения данных
function TPen.getColor: string;
begin
Result := IntToHex (FColor,6);
end;
6 знаков: '0000FF'
X10  X16 (строка)
procedure TPen.setColor(newColor: string);
begin
if Length(newColor) <> 6 then
FColor := 0 { чёрный цвет }
else
FColor := StrToInt ('$' + newColor);
end;
строка  число
 К. Поляков, 2011
шестнадцатеричное
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
37
Свойства «только для чтения»
Машина
…
V (скорость)
двигаться
?
Могут ли другие объекты менять
скорость автомобиля?
type TCar = class
private
Fv: real;
свойство «только для чтения»
...
public
property v: real read Fv;
...
end;
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
38
Скрытие внутреннего устройства: итог
свойства
методы
!
 К. Поляков, 2011
 защита данных объекта
 изменение внутреннего
устройства не требует
изменения других
объектов, если сохранить
интерфейс
 длина программы 
 замедление работы
Выгодно для больших программ, где
требуется надежность!
http://kpolyakov.narod.ru
39
Объектноориентированное
программирование
7.4 Иерархия классов
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
40
Классификации
базовый
класс
?
Фрукт
сочный съедобный плод
дерева или кустарника
Как выглядит фрукт?
классы-наследники
Яблоко – это разновидность фруктов.
Яблоко наследует все свойства фруктов.
Фото: www.statesymbolsusa.org, xenianova.files.wordpress.com, spoon.com.ua, gerbl.ru
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
41
Примеры наследования свойств
базовый
класс
Человек
Работник школы
Администратор
Директор
 К. Поляков, 2011
Завуч
Ученик
Учитель
Уборщик
Родитель
Технический работник
Слесарь
Электрик
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
42
Что такое наследник?
Класс Б является наследником класса А, если можно
сказать, что Б – это разновидность А.
водоем
осёл
!
цветок
животное
ромашка
автомобиль
двигатель
Двигатель – это часть автомобиля, а
не разновидность!
автомобиль
косточка
 К. Поляков, 2011
озеро
грузовик
ягода
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
43
Иерархия логических элементов
Логический элемент
с одним входом
НЕ
 К. Поляков, 2011
с двумя входами
И
ИЛИ
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
44
Определение ООП
Объектно-ориентированное
программирование – это такой подход к
программированию, при котором
1) программа представляет собой множество
взаимодействующих объектов,
2) … каждый из которых является экземпляром
определенного класса, и …
3) классы образуют иерархию наследования.
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
45
Класс «Логический элемент»
ЛогЭлемент
In1 (вход 1)
In2 (вход 2)
Res (результат)
сalc (вычислить)
type
TLogElement = class
In1, In2: boolean;
Res: boolean;
procedure calc;
end;
Сравните (плюсы, минусы):
вычислить результат
при изменении входов
type
TLogElement = class
In1, In2: boolean;
function res: boolean;
end;
вычислить результат по
известным входам
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
46
Скрываем данные (инкапсуляция)
type
TLogElement = class
private
FIn1, FIn2: boolean;
FRes: boolean;
procedure calc;
procedure setIn1(newIn1: boolean);
procedure setIn2(newIn2: boolean);
public
property In1: boolean read FIn1 write setIn1;
property In2: boolean read FIn2 write setIn2;
property Res: boolean read FRes;
читать прямо из поля
end;
свойство «только для чтения»
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
47
Процедура изменения входа
procedure TLogElement.setIn1(newIn1: boolean);
begin
FIn1 := newIn1;
calc;
end;
!
Сразу после изменения входа нужно
пересчитать значение выхода!
Проблемы:
 как написать метод calc?
 для элемента «НЕ» доступ ко второму
входу не нужен!
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
48
Метод calc
• невозможно написать для базового класса
Абстрактный метод – это метод, который
объявляется, но не реализуется в классе.
• разный у всех логических элементов
• наследники должны его переопределять!
Виртуальный метод – это метод, который
могут переопределять классы-наследники.
• недоступен для других объектов
!
 К. Поляков, 2011
Calc – это виртуальный (virtual) и
абстрактный (abstract) метод!
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
49
Метод calc
type
TLogElement = class
private
защищенный
виртуальный
абстрактный
...
protected
procedure calc; virtual; abstract;
public
...
end;
!
protected – доступно только наследникам!
Абстрактный класс – это класс, содержащий
абстрактный метод («фрукт»).
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
50
Как «скрыть» свойство In2?
type
TLogElement = class
private
...
protected
property In2: boolean read FIn2 write setIn2;
procedure calc; virtual; abstract;
public
...
end;
!
Наследники могут «открыть» защищенное
свойство (перевести в public)!
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
51
Классы-наследники: TNot
наследник от …
type
переопределить
TNot = class(TLogElement)
procedure calc; override;
end;
procedure TNot.calc;
begin
FRes := not FIn1;
end;
!
TNot – не абстрактный класс!
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
52
Классы-наследники: TLog2In
наследник от …
TLog2In = class(TLogElement)
public
свойство становится
property In2;
общедоступным
end;
?
 К. Поляков, 2011
TLog2In – абстрактный класс?
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
53
Классы-наследники: TAnd
наследник от …
type
переопределить
TAnd = class(TLog2In)
procedure calc; override;
end;
procedure TAnd.calc;
begin
FRes := FIn1 and FIn2;
end;
?
 К. Поляков, 2011
TAnd – абстрактный класс?
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
54
Вернемся к setIn1…
procedure
TLogElement.setIn1 (newIn1: boolean);
begin
FIn1 := newIn1;
calc;
какой метод
end;
вызывается?
• вызвать процедуру = перейти по заданному
адресу памяти
• в классе TLogElement метод calc не
реализован (нет кода, нет адреса)
• каждый наследник имеет свой метод calc
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
55
Обычный вызов процедуры
Паскаль
Ассемблер
процедура
...
calc;
...
...
call AB12
...
AB12 ...
AB14 ...
AB16 ...
...
Статическое связывание: адрес процедуры
известен и сразу записывается в машинный
код!
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
56
Динамическое связывание (при выполнении)
методы классов
Паскаль
...
calc;
...
виртуальный
метод
?
TNot.calc
Каков класс
объекта?
TAnd.calc
TOr.calc
Виртуальный метод – это метод базового
класса, который могут переопределить классынаследники так, что конкретный адрес
вызываемого метода определяется только при
выполнении программы.
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
Основная программа («И-НЕ»)
57
| A | B | not(A&B)
-----------------| 0 | 0 | 1
| 0 | 1 | 1
| 1 | 0 | 1
| 1 | 1 | 0
var elNot: TNot;
{ ссылки }
elAnd: TAnd;
A, B: boolean;
begin
elNot := TNot.Create; { создание объектов }
elAnd := TAnd.Create;
writeln('| A | B | not(A&B) ');
writeln('-------------------');
for A:=False to True do begin { перебор вариантов }
elAnd.In1 := A;
for B:=false to True do begin
elAnd.In2 := B;
elNot.In1 := elAnd.res;
{ связь элементов }
writeln('| ', integer(A), ' | ', integer(B),
' | ', integer(elNot.res));
end;
integer(False) = 0
end;
end.
integer(True) = 1
!
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
58
Классы – в модуль!
unit log_elem;
режим работы с объектами
{$mode objfpc}
interface
{ доступно другим модулям }
type
TLogElement = class
...
end;
... { объявление всех классов }
implementation
{ скрыто от других модулей }
procedure TLogElement.setIn1(newIn1: boolean);
begin
FIn1 := newIn1; calc;
end;
...
end.
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
59
Основная программа
program logic;
{$mode objfpc}
uses log_elem ;
var elNot: TNot;
elAnd: TAnd;
...
begin
elNot := TNot.Create;
elAnd := TAnd.Create;
...
end.
unit log_elem ;
{$mode objfpc}
interface
{ доступно другим модулям }
...
implementation
{ скрыто от других модулей }
...
end.
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
60
Обмен данными между объектами
«И»
«НЕ»
&
1
?
Как может один объект
передать данные другому?
2
• объект 1 должен «знать» адрес объекта 2
• у объекта 2 должен быть метод, доступный
объекту 2, с помощью которого он может
передать данные
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
61
Обмен данными между объектами
TLogElement = class
private
FNextEl: TLogElement; { адрес объект 2 }
FNextIn: integer;
{ номер входа объекта 2 }
...
public
procedure Link(nextElement: TLogElement;
nextIn: integer);
...
end;
метод для установки связи выхода
с входом другого элемента
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
62
Обмен данными между объектами
procedure TLogElement.Link
(nextElement: TLogElement;
nextIn: integer);
begin
запомнить адрес и
FNextEl := nextElement;
номер входа
FNextIn := nextIn;
следующего элемента
end;
?
Когда нужно передать данные «следующему»?
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
63
Обмен данными между объектами
procedure TLogElement.setIn1(newIn1: boolean);
begin
FIn1 := newIn1;
если связь
calc;
установлена…
if FNextEl <> nil then
case FNextIn of
1: FNextEl.In1 := res;
передать новые
2: FNextEl.In2 := res;
данные
end;
end;
аналогично для setIn2…
?
Почему не включить передачу в метод calc?
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
64
Обмен данными между объектами
elNot := TNot.Create;
установить
elAnd := TAnd.Create;
связь
elAnd.Link(elNot, 1);
...
for A:=False to True do begin
elAnd.In1 := A;
for B:=False to True do begin
elAnd.In2 := B;
...
end;
объект elAnd САМ сообщает объекту
end;
elNot, что его выход изменился
 К. Поляков, 2011
http://kpolyakov.narod.ru
Объектно-ориентированное программирование
65
Конец фильма
ПОЛЯКОВ Константин Юрьевич
д.т.н., учитель информатики высшей категории,
ГОУ СОШ № 163, г. Санкт-Петербург
[email protected]
 К. Поляков, 2011
http://kpolyakov.narod.ru
Скачать