Главная · Карта сайта · Поиск · Статьи · Компьютерные курсы · Обучающие программы · Открытые проекты · Веб-программирование · Создание интернет-сайта · Полезные ссылки · Глоссарий · Контакты · Декабрь 06 2016 22:47:53
Последнее опубликованное

Что такое Model-View-Controller
Pattern Model-View-Controller

Как создать свой веб-сайт
Как создать свой сайт в интернете

Разное
Статистика

Интерфейсы


[Следующая страница]

2.1.Описание и реализация интерфейса

Начнем, как говорится, с легенды. Предположим, что объекты определенных в предыдущем разделе классов геометрических объектов необходимо сохранять в некотором геоинформационном хранилище, а также читать их оттуда. Введем абстракцию “элемент геоинформационного хранилища”, назначение которого сходно с назначением шаблона проектирования “сувенир”. Другими словами, этот самый элемент будет в себе инкапсулировать порцию данных геоинформационного хранилища, однозначно идентифицируемую уникальным идентификатором элемента. Надо сказать, что чаще всего роль уникального идентификатора играет некий целочисленный параметр, но в нашем случае он будет текстовым, например - текстовое представление GUID.

GUID (Globally Unique Identifier) - уникальный в глобальном масштабе 128-битный идентификатор ресурса.
Плюсы использования уникальных идентификаторов в том, что они уникальны не только в рамках отдельно взятого хранилища, но и в масштабах всего мира (по крайней мере, так утверждают те, кто их разрабатывает), поэтому сложностей в процессах миграции данных из одних источников в другие возникать не должно. Обычные же целочисленные идентификаторы уникальны зачастую только в пределах одной схемы базы данных, поскольку являются ничем иным, как порядковым номером объекта в хранилище.

using System;
namespace CSharpQuickGuide
{
    //Класс 'Элемент геоинформационного хранилища'
    public class GeoStorageItem
    {
        //Конструктор, который доступен только производным классам
        //и членам текущей сборки
        internal protected GeoStorageItem(string _ID) 
        { throw new NotImplementedException(); }
        //Уникальный идентификатор элемента
        //(использован сокращенный вариант определения свойства
        //с различными модификаторами доступа для операций get и set)
        public string ID { get; internal set; }
        //Извлечение данных по имени фрагмента
        public object GetData(string _Name)
        { throw new NotImplementedException(); }
        //Сохранение данных поименованного фрагмента
        public void SetData(string _Name, object _Value)
        { throw new NotImplementedException(); }
        //Сохранение данных всего элемента в хранилище
        internal void Save()
        { throw new NotImplementedException(); }
        //Удаление данных всего элемента из хранилища
        internal void Delete()
        { throw new NotImplementedException(); }
    }

    //Контракт на сохранение данных в геоинформационном хранилище
    interface IGeoStorageSerializable
    {
        //При определении интерфейсов не указывают модификаторы доступа 
        //и прочие ключевые слова. Они будут определены в процессе 
        //реализации интерфейса в соответствующем классе

        //Чтение параметров из элемента геоинформационного хранилища
        void Load(GeoStorageItem _source);
        //Сохранение параметров в элементе геоинформационного хранилища
        void Save(GeoStorageItem _target);
    }
}

Итак, слева представлены определения класса GeoStorageItem – элемента геоинформационного хранилища, и интерфейса IGeoStorageSerializable – контракта на возможность сохранения данных экземпляра любого класса в этом самом хранилище.

Далее, по нашей легенде пусть будет так, что этот класс и интерфейс разработала команда авторов геоинформационной базы данных, к которой мы никакого отношения не имеем. Наша задача - обеспечить возможность хранения геометрии в этой базе данных путем реализации предоставленного ее авторами интерфейса (соблюдение контракта IGeoStorageSerializable).

Вполне естественно, что в процессе чтения и сохранения геометрии в базе данных удобно иметь дело с интерфейсом базового объекта Geometry2D и пользоваться благами полиморфизма, запрашивая ссылку на IGeoStorageSerializable непосредственно у него, не прибегая к приведениям типов. Если мы будем связывать обязательствами контракта IGeoStorageSerializable не базовый класс Geometry2D, а его производные классы, то возникнут следующие трудности:

  1. Хотя компилятор и позволяет запрашивать интерфейс у экземпляра класса, даже если тот его и не реализует (попытка явного приведения типов), в таком случае сложно гарантировать, что все производные объекты реализуют требуемый интерфейс. Если кто-то из этих объектов требуемый интерфейс не реализует - будут возникать ошибки во время выполнения (см. пример кода).

    using System;
    namespace CSharpQuickGuide
    {
        //Произвольный интерфейс
        interface IMyInterface
        {
            void MyMethod();
        }
        //Произвольный базовый класс
        public class MyBaseClass { }
        //Реализует интерфейс IMyInterface
        public class MyInheritorClass : MyBaseClass, IMyInterface
        {
            public void MyMethod()
            { throw new NotImplementedException(); }
        }
        //НЕ реализует интерфейс IMyInterface
        public class MyInheritorClass2 : MyBaseClass { }
        //Пример корректного и некорректного обращения к интерфейсу
        public static class Invoke
        {
            static void Main()
            {
                //Создаем класс, который реализует интерфейс
                MyBaseClass o1 = new MyInheritorClass();
                //Вызов выполняется
                ((IMyInterface)o1).MyMethod();
                //Создаем класс, который НЕ реализует интерфейс
                MyBaseClass o2 = new MyInheritorClass2();
                //Код с точки зрения компилятора корректный, 
                //но в ходе выполнения происходит ошибка:
                //Unable to cast object of type 'MyInheritorClass2'
                //to type 'IMyInterface'
                ((IMyInterface)o2).MyMethod();
            }
        }
    }
    

  2. В каждом производном классе придется отдельно определять модификаторы доступа и прочие ключевые слова при реализации интерфейса.

В этой связи придется реализовывать интерфейс в два этапа: на первом этапе создаем спецификации методов (абстрактные методы) интерфейса в базовом классе Geometry2D, а затем обеспечиваем их реализацию во всех производных классах. Пример кода ниже.

Важно: Есть два варианта синтаксиса реализации интерфейса: неявная (Implicity) и явная (Explicity). Второй вариант нужно использовать в том случае, если ваш класс реализует несколько интерфейсов, и есть пересечения по именам и сигнатурам методов. В случае явной реализации к имени методов будет добавлен префикс - имя интерфейса: InterfaceName.Method.

Для тех, кто использует Microsoft Visual Studio, есть возможность автоматически добавить реализацию методов интерфейса: для этого достаточно выбрать нужный интерфейс в списке реализуемых классом интерфейсов, вызвать контекстное меню и указать нужный пункт раздела Implement Interface.

using System;
namespace CSharpQuickGuide
{
    public abstract class Geometry2D : IGeoStorageSerializable 
    {
        #region IGeoStorageSerializable Members
        //Реализация интерфейса предполагает не только реализацию тела метода
        //или свойства, но и определение модификатора доступа и других ключевых
        //слов, если это необходимо

        //Реализация абстрактного (abstract) метода Load обязана присутствовать 
        //в производных классах
        public abstract void Load(GeoStorageItem _source);
        //Реализация абстрактного (abstract) метода Save обязана присутствовать 
        //в производных классах
        public abstract void Save(GeoStorageItem _target);

        #endregion
    }

    public class PointGeometry : Geometry2D
    {
        //Реализация абстрактного (abstract) метода Load
        public override void Load(GeoStorageItem _source)
        { throw new NotImplementedException(); }

        //Реализация абстрактного (abstract) метода Save
        public override void Save(GeoStorageItem _target)
        { throw new NotImplementedException(); }
    }
}

Любой класс может реализовывать любое число интерфейсов, перечисленных через запятую после имени класса и двоеточия. Я уже упоминал в предыдущей главе, что часто возникает дилемма, что лучше использовать: абстрактный класс или интерфейс? Ниже несколько соображений на эту тему:

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

  • Если есть возможность выделить не только общую спецификацию, но и реализовать некоторую общую функциональность, то следует использовать абстрактный класс.

  • Если реализуемое поведение свойственно всем производным классам, то, скорее всего, следует использовать базовый абстрактный класс. В противном случае, необходимо объединить те производные классы, для которых это поведение характерно введением нового звена в иерархии наследования, и это новое звено наградить нужным интерфейсом.



[Следующая страница]

Компьютерные курсы и курсы программирования
Основы программирования

Курс для начинающих программистов на C# и VB.NET.

SQL 25™

Построение SQL запросов и работа с базой данных.

C# Quick Guide™

Программирование на C#. Краткое руководство.

RegEx

Применение регулярных выражений.

Plug-in архитектура

Примеры программной Plug-in архитектуры.

XML и его расширения

Язык разметки XML и его расширения с примерами.

HTML и разметка гипертекста

Языки HTML, XHTML и CSS с примерами разметки.

Основы веб-дизайна

Основы веб-дизайна: решения типовых задач верстки.

Программирование на PHP

Руководство по программированию на PHP для начинающих.

Справочные материалы

Шаблоны проектирования
Каталог шаблонов проектирования программных компонентов.

Рефакторинг кода
Каталог приемов рефакторинга программного кода.

Гость
Имя

Пароль



Забыли пароль?
Запросите новый здесь
.
Coding Craft. Все права защищены © 2011. Проект Инициативного Народного Фронта Образования - ИНФО-проект.