Учебные материалы по математике | «Асимметричное» решение — именованные интерфейсы | Matematiku5
Вузы по математике Готовые работы по математике Как писать работы по математике Примеры решения задач по математике Решить задачу по математике online

«Асимметричное» решение — именованные интерфейсы


cMum Mum;

}

cSun cDad cMum

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

«Асимметричное» решение — именованные интерфейсы

Проблема:

Существует два фрагмента кода, единственным различием которых является то, что методы имеют разные параметры.

К счастью, решить подобные проблемы позволяют интерфейсы (interfaces). Они определяют, какие методы должны присутствовать в классе.

Интерфейс — такой же абстрактный класс, в котором нет полей и не определены тела у методов, т. к. интерфейс определяет список методов класса с определёнными именами, типами и параметрами.

Таким образом, интерфейсы не хранят данные, поэтому к ним нельзя добавлять поля. Дело в том, что интерфейс определяет список методов класса с определёнными именами, типами, параметрами.

Класс реализует интерфейс, если в нём реализуются все его методы.

Основная идея — возможность ссылки на интерфейс (на несколько интерфейсов).

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

Замечание: один класс может реализовывать несколько интерфейсов.

Абстрактный класс — класс, в котором нет полей и который имеет хотя бы один абстрактный метод (abstract).

Абстрактный класс по семантике (не по синтаксису и не по происхождению, отличному от чистого ООП) можно сопоставить именованному интерфейсу.

Таким образом, функционально любой интерфейс является абстрактным классом, но абстрактный класс не является интерфейсом.

Основные отличия интерфейса и абстрактного класса

1) —абстрактный класс может содержать свойства

интерфейс не содержит свойств

2) —абстрактный класс может содержать тела методов

интерфейс не содержит тел методов, только их объявление

3) —абстрактный класс наследуется (etxends)

интерфейс реализуется (implements)

4) — наследник в абстрактном классе имеет только одного родителя

— наследник может реализовывать методы нескольких интерфейсов

5) — наследник не обязан содержать реализацию методов абстрактного класса-родителя

— наследник обязан содержать реализацию всех методов реализуемого интерфейса

Следовательно, интерфейс не часть класса, а частный случай — абстрактный класс, наследование которого обязывает к реализации.

Пример: иерархия животных (основная — по строению, вторичная — по передвижению):

// Компонентно-ориентированное программирование

using System;

using System. Collections. Generic;

using System. Linq;

using System. Text;

using System. Threading. Tasks; // предоставляет типы, которые упрощают работу по написанию параллельного и асинхронного кода

namespace Hierarchy

{

class cAnimals

{

protected string Name;

public void SetName(string x)

{

this. Name = x;

}

public string GetName()

{

return Name;

}

public cAnimals() // конструктор по умолчанию

{

}

public cAnimals(string x) // конструктор

{

this. Name = x;

}

}

class cMammals : cAnimals // класс «Млекопитающие», который наследуется от класса «Животные»

{

}

class cFishes : cAnimals // класс «Рыбы»

{

}

class cBirds : cAnimals // класс «Птицы»

{

}

//Разница между интерфейсами и абстрактными классами

//предок обязан / может дать реализацию

//иными словами, здесь интерфейс не часть класса, но частный случай — абстрактный класс, наследование которого обязывает к реализации

abstract class FlyAble

{

public abstract void Fly();

}

//интерфейсы: только заголовки методов без модификаторов (public по умолчанию)

//в частности, нет полей (но может быть set-get) и невозможна инициализация

//движение с переменной скоростью

interface IMoveAble

{

void SetSpeed(int x); // фиксируем скорость

int GetSpeed(); //получаем скорость, с которой двигается животное

}

//вносится коллизия имён для демонстрации проблем множественного наследования

//ошибка проектирования?

//см. далее также обсуждение конфликта реализации и способы разрешения (склеивание, переименование)

interface IFlyAble : IMoveAble

{

void Fly();

}

interface IRunAble : IMoveAble

{

void Run();

}

interface ISwimAble : IMoveAble

{

void Swim();

}

class Elephant : cMammals, IRunAble, ISwimAble

{

//явное описание интерфейса — реализация:

void IRunAble. Run()

{

//выполнение метода:

Console. WriteLine("Elephant" + GetName() + "runs");

}

void ISwimAble. Swim()

{

//выполнение метода

Console. WriteLine("Elephant" + GetName() + "swims");

}

public Elephant()

{

}

public Elephant(string x)

{

this. Name= x;

}

//разрешение коллизии — "склеивание"

void IMoveAble. SetSpeed(int x) // сомнительно с точки зрения предметной области

{

}

int IMoveAble. GetSpeed()

{

return 0;

}

//иной вариант — переименование

//реализуем как закрытие, открываем под другими именами

// см. Duck

} //class Elephant

class Dolphin : cMammals, ISwimAble

{

//Явное описание интерфейса (реализация)

void ISwimAble. Swim()

{

//выполнение метода

}

void IMoveAble. SetSpeed(int x)// устанавливается скорость движениия

{

}

int IMoveAble. GetSpeed()

{

return 0;

}

} //class Dolphin

class Duck : cBirds, ISwimAble, IRunAble, IFlyAble

{

//Явное описание интерфейса-реализация:

void ISwimAble. Swim()

{

//Выполнение метода

}

void IRunAble. Run()

{

//выполнение метода

}

void IFlyAble. Fly()

{

//выполнение метода

}

void IMoveAble. SetSpeed(int x)

{

}

int IMoveAble. GetSpeed()

{

return 0;

}

}//class Duck

class Program

{

// запускалка

static void Main()

{

Elephant E1 = new Elephant();

E1.SetName("John");

//E1.Run_Method(); //ошибка — нужно явно выбирать интерфейс

((IRunAble)E1).Run(); //ok

IRunAble E2 = E1;

E2.Run(); // ok

//E2.SetName("???"); //ошибка

// объявление объекта интерфейсного класса

IRunAble E = new Elephant("Pete");

//E. SetName("???"); //ошибка

// объект, функционирующий по правилам метода

E. Run();

ISwimAble obj_Dolphin = new Dolphin();

obj_Dolphin. Swim;

ISwimAble obj_Duck_1= new Duck();

obj_Duck_1.Swim;

IRunAble obj_Duck_2= new Duck();

obj_Duck_2.Run();

IFlyAble obj_Duck_3= new Duck();

obj_Duck_3.Fly();

Console. ReadKey();

}

}// class Program

} //namespace Hierarchy

Наследование интерфейса (компонентный подход)

-Могут ли разные интерфейсы обязывать реализовывать методы с одинаковыми именами?

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

— Что является значением метода?

-С точки зрения логики — реализация (определение).

-С точки зрения реализации – ссылка на определение

Например, можно реализовать метод присваиванием ему имени, уже определённого к этому времени.

New_Method=Old_ Method — такое присваивание называется отображением или maping.

Наряду с агрегированием в качестве множественного наследования этот способ может трактоваться как определение наследования по умолчанию.

Обработка исключений в ООП

Исключения являются полезным инструментом, который находит код, работающий неожиданным для нас образом.

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

Ошибка — это исключение из правил, выдача желаемого за действительное (вещь в программировании неизбежная по определению).

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

Устойчивость — способность адекватно реагировать на исключительные ситуации (выход за границы области).

Любая процедура имеет два результата: желаемый и error.

P

P

 

 

X P(X)

 

error

 

Рассмотрим контекст операционной среды, в которой работает программа – машина-обработчик. Эта машина не делает ошибок (в идеале), все её функции всюду определены.

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

P

P

 

X P(X)

 

Машина-обработчик (функции обработки исключений)

 

 

С другой стороны, сама программа есть входной аргумент для машины-обработчика (инструкция для неё).

Таким образом, можно расширить эти инструкции собственными операторами перехвата и обработки исключений.

Прагматика:

Когда этим пользоваться?

Использование обработчиков исключений, как и любых других событий, предполагает явно или неявно деление «клиент-сервер».

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

Определение и генерация исключений в C#

Синтаксис конструкции try — catch-finally:

try {… }

catch (T1 e1) {…}

catch(Tk ek)

{

}

finally

{…}

Ключевое слово try определяет охраняемый блок.

Вслед за try-блоком могут следовать catch-блоки.

Catch-блоки называются блоками-обработчиками исключительных ситуаций. Их может быть несколько (могут и отсутствовать).

finally-блок — блок завершения (финализации), который также может отсутствовать.

Вся эта конструкция может быть вложенной, т. е. в состав try-блока может входить конструкция try-catch-finally.

Выбрасывание исключений

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

throw – оператор, выбрасывающий исключения.

Но throw может быть частью программного текста try-блока и выполняться, когда в результате проведенного анализа становится понятным, что дальнейшая нормальная работа невозможна.

Синтаксически оператор throw имеет вид:

throw [выражение]

Выполнение оператора throw приводит к тому, что нормальный процесс вычислений на этом прекращается. Если это происходит в охраняемом try-блоке, то начинается этап «захвата» исключения одним из обработчиков исключений.

Захват исключения

Catch- блок — обработчик исключения имеет следующий синтаксис:

catch (T e)

{

}

Класс T, указанный в заголовке catch-блока, должен принадлежать классам исключений.

Блок catch с формальным аргументом e класса T потенциально способен захватить текущее исключение, если и только если объект текущего исключения совместим по присваиванию с объектом e.

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

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

Цепочка вызовов, хранящаяся в стеке вызовов

(цепочка вызовов начинается с процедуры Main).

Исключение возникло в последнем вызванном методе цепочки — на рисунке метод r5. Если у этого метода не нашлось обработчиков события, способных обработать исключение, то это пытается сделать метод r4, вызвавший r5. Если вызов r5 находится в охраняемом блоке метода r4, то начнет проверяться список обработчиков в охраняемом блоке метода r4. Этот процесс подъема по списку вызовов будет продолжаться, пока не будет найден обработчик, способный захватить исключение, или не будет достигнута начальная точка — процедура Main. Если и в ней нет потенциального захватчика исключения, то сработает стандартный обработчик, прерывающий выполнение программы с выдачей соответствующего сообщения.

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

Блок finally

Синтаксис:

Что делать, когда не видно метода обработки исправления ошибки?

Как минимум, завершить процедуру с наименьшими потерями, в идеале, деинициализировать, сделать откат, не навредить (понятие транзакции).

try P

finally Pfin

Процедура Pfin работает в любом случае, вне зависимости от того, произошла ошибка или нет.

Освобождение ресурсов, занятых try-блоком, выполняет finally-блок. Если try-блок присутствует, то finally-блок выполняется всегда (сразу же после завершения работы try-блока (как бы последний ни завершился)).

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

Впрочем, выполнение try-блока может быть:

— прервано оператором throw

— управление может быть передано другому блоку из-за выполнения таких операторов, как goto, return

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

Схема обработки исключительных ситуаций, предложенная в языке C#, обладает одним существенным изъяном — ее можно применить некорректно. Она позволяет, в случае возникновения исключительной ситуации, уведомить о ее возникновении и спокойно продолжить работу, что, в конечном счете, приведет к неверным результатам. Из двух зол — прервать вычисление с уведомлением о невозможности продолжения работы или закончить вычисления с ошибочным результатом вычисления — следует выбирать первое. Некорректно применённая схема C# приведет к ошибочным результатам.

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

//Задача: обработка исключительной ситуации

using System;

using System. Collections. Generic;

using System. Linq;

using System. Text;

using System. Threading. Tasks;

namespace ConsoleApplication1

{

class MyException : Exception // класс Exception- возможность получать пользоватеьские исключения

{

string Info; // некая информация об ошибке (исключительной ситуации)

public MyException(); // конструктор по умолчанию

public MyException(string Message) // конструктор для порождения ошибок

{

}

}

class ExceptionHandlingPattern //стандартная схема обработки исключительных ситуаций

{

//Заглушки. По умолчанию параметр — текущее состояние

//В конкретной ситуации — относящаяся к ней часть

private void MakeJob() { } // сделать некую работу

private void MakeOtherJob() { } // продолжить работу

private bool CheckState() { return true; } // проверить состояние

private void CorrectState() { Console. WriteLine("Попытка самостоятельно исправить ситуацию"); }//исправить состояние

public void TryToMakeJob() //попытаться сделать работу

{

bool Success; //успех выполнения

bool Danger; // риск невыполнения

int MaxCount = 0; //максимальное число попыток выполнения

int Count = 0; // фактическое число попыток выполнения

do

{

Success = true; //успех

try

{

MakeJob();

Danger = CheckState(); //возможно ли продолжение

if (Danger)

{

MyException ThisException = MyException(); //оформирование исключения

throw (ThisException);

}

MakeOtherJob(); //нормальное продолжение

}

catch (MyException me)

{

Success = false;

if (Count <= MaxCount)

{

Count++;

//level+=1; стек вызовов

CorrectState(); //корректировка ситуации

}

else

{

//level-=1 //откат

MyException ThisException = new MyException();

Наташа

Автор

Наташа — контент-маркетолог и блогер, но все это не мешает ей оставаться адекватным человеком. Верит во все цвета радуги и не верит в теорию всемирного заговора. Увлекается «нефрохиромантией» и тайно мечтает воссоздать дома Александрийскую библиотеку.

Распродажа дипломных

 Скидка 30% по промокоду Diplom2020

А ты боишься COVID-19?

 Пройди опрос и получи промокод