
- •Программирование и алгоритмические языки. Курс за третий семестр.
- •Введение в объектно-ориентированное программирование (ооп)
- •Идеологический обзор
- •Базовые принципы ооп
- •Наследование имён. Наследование значений.
- •Коллизия
- •Что такое имя процедуры?
- •Ооп как оперирование типами
- •Именование типов в c# Тип данных «класс»
- •Конструкторы и деструкторы
- •И нкапсуляция данных
- •Наследование (краткое введение)
- •Наследование реализации как уточнение семантики типа
- •Пополнение и переопределение методов Отношение пополнения интерфейса
- •Открытие реализации для проектирования классов - опция protected
- •Полиморфизм Полиморфизм как уточнение семантики типа переменной
- •Динамические методы как поддержка полиморфизма- опции static, virtual, override
- •Событийное программирование Обработка событий
- •Идея: Когда что-то вставляется, срабатывает некоторый предикат. Например, после вызывается команда проверки, каскадного удаления или какая-нибудь другая команда. Генераторы и приёмники сообщений
- •Событийное программирование нужно:
- •Событийный стиль в процедурном программировании - управление данными (на примере)
- •Событийное программирование в c# (delegate, event)
- •Введение в компонентное программирование
- •Объекты, реализующие интерфейсы
- •Частный случай общей проблемы взаимодействия программного обеспечения разных производителей на уровне исполняемого кода
- •Базовый интерфейс компонент
- •Проблема множественности иерархий
- •Коллизия имён
- •Коллизия реализации
- •«Симметричное» решение – агрегаты (декартовы произведения классов)
- •Множественное наследование
- •«Асимметричное» решение - именованные интерфейсы
- •Основные отличия интерфейса и абстрактного класса
- •Наследование интерфейса (компонентный подход)
- •Обработка исключений в ооп
- •Определение и генерация исключений в c#
- •Выбрасывание исключений
- •Захват исключения
- •Блок finally
- •Коды программ
И нкапсуляция данных
Инкапсуляция - кибернетика – функциональное описание систем, слишком сложных для классического анализа.
В этом понятии скрыта идея чёрного и серого ящиков. Строение интересует настолько, насколько оно определяет поведение. Это нужно для того, чтобы скрыть сложность реализации.
Принцип инкапсуляции не является программистским, а является общеметодическим (функциональным, бихевиористским). Его сформулировал Норберт Винер.
Идея инкапсуляции - идея явного разделения логики и реализации с целью сокрытия сложности не нова в программировании:
языки высокого уровня скрывают идеи реализации той машинной программы, в которой они транслируются (такие как Фортран, Кобол, Алгол, Pascal, Java, C, C++, C#, Objective C, Smalltalk, Delphi и т.д.)
пользовательские процедуры и функции скрывают детали алгоритма определения функции от самой функции
любое наименование скрывает детали алгоритма
в модульном программировании это определяется так:
Implementation (private)-
относится к функциям (операторам)-
не является доступной пользователю |
э
то
старая идея локализации
Таким образом, идея локализации в объектно – ориентированном программировании приобретает ОТНОСИТЕЛЬНЫЙ СМЫСЛ: не только что прятать, но и от кого прятать.
Для реализации ограничения доступа применяются модификаторы доступа- PUBLIC, PRIVATE, PROTECTED, INTERNAL.
-public- расширяет область видимости (можно обращаться из любого места программы)
-private- обеспечивает самую жёсткую локализацию, ограничивая доступ (область видимости) пределами описания данного класса (т.е. пользователь не видит private- полей и методов, а обращение к этим полям является синтаксической ошибкой)
-protected- виден классам-наследникам. Это даёт возможность скрыть от наследников переменную так, чтобы они не «напортачили» в ней.
-internal- модификатор доступа, который используется для методов, доступных всем классам, определённым в конкретной сборке
Public и private нужны для разделения логики и реализации (сокрытие реализации) - идея «чёрного ящика»
Организация памяти:
Процедуры и функции хранятся в памяти и именуются точно также как данные (т.е. явное пользовательское именование в языках высокого уровня, а адреса на нижнем уровне).
//Пример: Описать класс РАЦИОНАЛЬНЫЕ ЧИСЛА.
using System; //стандартная библиотека - ввод-вывод, базовые классы
namespace Fraction //логически связанное определение имён (Пространства имён (namespace) предоставляют возможность логической взаимосвязи классов и других типов.)
{
class tRational
{//поля - см. далее Сокрытие данных (set,get- подход)
public Int32 numerator; //числитель
public Int32 denominator;// знаменатель
//не в нормализованном виде, если нужно, то напиши метод
//методы:
//консольный ввод и вывод
private Int32 ReadInt(string s)
{
Console.WriteLine(s); //выводим на консоль информацию, которая содержалась в s
string input = Console.ReadLine(); // нет параметров, т.к. системный оператор
return Int32.Parse(input); // преобразование типов (читается строковое, а преобразуется в int)
}
public void read(string s)
{
Console.WriteLine(s);
numerator = ReadInt("Числитель: ");
denominator = ReadInt("Знаменатель: ");
}
private void WriteInt(string s, Int32 x)
{
Console.WriteLine(s);
Console.WriteLine(x.ToString()); //возвращает строку, представляющую текущий объект.
}
public void write(string s)
{
Console.WriteLine(s);
WriteInt("Числитель:", numerator);
WriteInt("Знаменатель:", denominator);
Console.ReadKey(); //задержка - получает следующий нажатый пользователем символ или функциональную клавишу. Нажатая клавиша отображается в окне консоли.
}
public void assign(tRational a)//присваивание значений- "клонирование"
{
this.numerator = a.numerator;
this.denominator = a.denominator;
}
public void add(tRational a) //добавить к this
{
this.numerator = this.numerator * a.denominator + a.numerator * this.denominator;
this.denominator = a.denominator * this.numerator;
}
public void subtract(tRational a) //вычесть из this
{
this.numerator = this.numerator * a.denominator - a.numerator * this.denominator;
this.denominator = a.denominator * this.numerator;
}
public void multiply(tRational a) //домножить
{
this.numerator = this.numerator * a.numerator;
this.denominator = a.denominator * this.denominator;
}
public void divide(tRational a)//поделить
{
this.numerator = this.numerator * a.denominator;
this.denominator = this.denominator * a.numerator;
}
} //tRational
class Program
{//программа-запускалка
//метод:
static void main()
{
//сложение двух дробей y<--y+x
tRational y, x; //указатели (объявление)
x = new tRational(); // вызов конструктора (выделение памяти)
y = new tRational();
y.read("Введите первое слагаемое:");
x.read("Введите второе слагаемое:");
y.add(x);
y.write("Сумма равна:");
}
}// Program
}// Fraction
Теперь возникает проблема сокрытия реализации данных.
Set-get подход
Set-get подход- управление доступом к полю объекта- семантика присваивания.
Set-get подход разделяет фактическое хранение данных и пользовательское представление о данных, возвращает к вопросу: «Что такое данные? »
Разделение логических и физических данных знакомо по СУБД (view).
С точки зрения процедурного программирования, состояние- это значения переменных, а объектного - несколько иное. К примеру, работаем с декартовыми координатами, а пользователю представляем как полярные.
Идея пользовательского представления данных отражается в виде set-get- подхода, которые подразумевает виртуальные (пользовательские) поля.
Тем самым гарантируется, что при изменении реализации данных, не «посыпятся» программы, которые зависят от этого типа.
Есть поле private, которое недоступно, а есть парочка функций его обслуживающих, где одна записывает, другая возвращает значение
Типы рассматриваются как значения.
//set-get:
class cFraction
{
private int numerator;
private int denominator;
public void add(cFraction a)
{
//...
}
//set-переопределение
//get-вычисляемая функция в точке
// конструктор- эквивалентно write:
cFraction(int x, int y) //set
{
//неявно вызывается системныый конструктор
this.denominator = x;
this.numerator = y;
}
//эквивалентно read:
public int get_denominator()
{
return denominator;
}
public int get_numerator()
{
return denominator;
}
}