Студопедия

КАТЕГОРИИ:

АвтоАвтоматизацияАрхитектураАстрономияАудитБиологияБухгалтерияВоенное делоГенетикаГеографияГеологияГосударствоДомЖурналистика и СМИИзобретательствоИностранные языкиИнформатикаИскусствоИсторияКомпьютерыКулинарияКультураЛексикологияЛитератураЛогикаМаркетингМатематикаМашиностроениеМедицинаМенеджментМеталлы и СваркаМеханикаМузыкаНаселениеОбразованиеОхрана безопасности жизниОхрана ТрудаПедагогикаПолитикаПравоПриборостроениеПрограммированиеПроизводствоПромышленностьПсихологияРадиоРегилияСвязьСоциологияСпортСтандартизацияСтроительствоТехнологииТорговляТуризмФизикаФизиологияФилософияФинансыХимияХозяйствоЦеннообразованиеЧерчениеЭкологияЭконометрикаЭкономикаЭлектроникаЮриспунденкция

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




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

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

Наследование интерфейсов действительно очень гибкое. Так, если есть два интерфейса A и B, причем B наследуется от A, то новый интерфейс C может наследоваться от них обоих. Указание наследования от A является избыточным, все элементы этого интерфейса и так будут получены по наследству через интерфейс B.

 

public interface Drawble extends Colorable, Resizable

{

}

Класс наследует от прямых суперклассов и прямых суперинтерфейсов все методы (abstract или нет) этих суперклассов и суперинтерфейсов, которые доступны для кода класса и которые не перегружены и не скрыты в объявлении данного класса.

Если объявление метода перегружает или скрывает объявление другого метода, тогда происходит ошибка времени компиляции, если они имеют различные типы возвращения или если один имеет тип возвращения, а другой не возвращает никаких данных (т.е. в его объявлении содержится слово "void"). Более того, объявление метода не должно содержать предложение throws, которое противоречит с этим методом, и которое его замещает или скрывает; иначе происходит ошибка времени компиляции. В этом аспекте замещение методов отличается от сокрытия полей для него допустимо для поля скрыть поле другого типа.

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

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

Если замещемый или скрываемый метод объявлен как protected, тогда замещение или сокрытие метода должно быть protected или public; иначе происходит ошибка времени компиляции.

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

Заметьте, что private-метод никогда не доступен для подклассов и поэтому не может быть скрытым или замещенным в техническом смысле этого понятия. Такой способ, что подкласс может объявлять метод с той же самой сигнатурой как private-метод в одном из суперклассов, и не существует ограничения, чтобы тип возвращения или метод throws такого метода имел какую-либо взаимосвязь с private-методами в суперклассе.

 



Правила наследования типов. Принцип «Open-Closed Principle».

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

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

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

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

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

Примеры

Без абстракций

Проблема

Самый простой пример нарушения принципа открытости/закрытости – использование конкретных объектов без абстракций. Предположим, что у нас есть объект SmtpMailer. Для логирования своих действий он использует Logger, который записывает информацию в текстовые файлы.

1: public class Logger

2: {

3: public void Log(string logText)

4: {

5:     // сохранить лог в файле

6: }

7: }

8:  

9: public class SmtpMailer

10: {

11: private readonly Logger logger;

12:  

13: public SmtpMailer()

14: {

15:     logger = new Logger();

16: }

17:  

18: public void SendMessage(string message)

19: {

20:     // отсылка сообщения

21:  

22:     logger.Log(string.Format("Отправлено '{0}'", message));

23: }

24: }

И тоже самое происходит в других классах, которые используют Logger. Такая конструкция вполне жизнеспособна до тех, пока мы не решим записывать лог SmptMailer'a в базу данных. Для этого нам надо создать класс, который будет записывать все логи не в текстовый файл, а в базу данных:

1: public class DatabaseLogger

2: {

3: public void Log(string logText)

4: {

5:     // сохранить лог в базе данных

6: }

7: }

А теперь самое интересное. Мы должны изменить класс SmptMailer из-за изменившегося бизнес-требования:

1: public class SmtpMailer

2: {

3: private readonly DatabaseLogger logger;

4:  

5: public SmtpMailer()

6: {

7:        logger = new DatabaseLogger();

8: }

9:  

10: public void SendMessage(string message)

11: {

12:     // отсылка сообщения

13:  

14:     logger.Log(string.Format("Отправлено '{0}'", message));

15: }

16: }

Но ведь по принципу единственности ответственности не SmptMailer отвечает за логирование, почему изменения дошли и до него? Потому что нарушен наш принцип открытости/закрытости. SmptMailer не закрыт для модификации. Нам пришлось его изменить, чтобы поменять способ хранения его логов.

Решение

В данном случае защитить SmtpMailer поможет выделение абстракции. Пусть SmtpMailer зависит от интерфейса ILogger:

1: public interface ILogger

2: {

3: void Log(string logText);

4: }

5:  

6: public class Logger : ILogger

7: {

8: public void Log(string logText)

9: {

10:     // сохранить лог в файле

11: }

12: }

13:  

14: public class DatabaseLogger : ILogger

15: {

16: public void Log(string logText)

17: {

18:     // сохранить лог в базе данных

19: }

20: }

21:  

22: public class SmtpMailer

23: {

24: private readonly ILogger logger;

25:  

26: public SmtpMailer(ILogger logger)

27: {

28:     this.logger = logger;

29: }

30:  

31: public void SendMessage(string message)

32: {

33:     // отсылка сообщения

34:  

35:     logger.Log(string.Format("Отправлено '{0}'", message));

36: }

37: }

Теперь смена логики логирования уже не будет вести к модификации SmtpMailer'а.

Проверка типа абстракции

Проблема

Этот пример в разных вариациях все не раз видели в коде. Его хоть в рамку можно вешать, как самое популярное нарушение проектирования. У нас есть иерархия объектов с абстрактным родительским классом AbstractEntity и класс Repository, который использует абстракцию. При этом вызывая метод Save у Repository мы строим логику в зависимости от типа входного параметра:

1: public abstract class AbstractEntity

2: {

3: }

4:  

5: public class AccountEntity : AbstractEntity

6: {

7: }

8:  

9: public class RoleEntity : AbstractEntity

10: {

11: }

12:  

13: public class Repository

14: {

15: public void Save(AbstractEntity entity)

16: {

17:     if (entity is AccountEntity)

18:     {

19:         // специфические действия для AccountEntity

20:     }

21:     if (entity is RoleEntity)

22:     {

23:         // специфические действия для RoleEntity

24:     }

25: }

26: }

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

Решение

Конкретизируя классы методом is или typeof мы должны сразу понять, что наш код начал «попахивать». Чтобы решить данную проблему, необходимо логику сохранения конкретных классов из иерархии AbstractEntity вынести в конкретные классы Repository. Для этого мы должны выделить интерфейс IRepository и создать хранилища AccountRepository и RoleRepository:

1: public abstract class AbstractEntity

2: {

3: }

4:  

5: public class AccountEntity : AbstractEntity

6: {

7: }

8:  

9: public class RoleEntity : AbstractEntity

10: {

11: }

12:  

13: public interface IRepository<T> where T : AbstractEntity

14: {

15: void Save(T entity);

16: }

17:  

18: public class AccountRepository : IRepository<AccountEntity>

19: {

20: public void Save(AccountEntity entity)

21: {

22:     // специфические действия для AccountEntity

23: }

24: }

25:  

26: public class RoleRepository : IRepository<RoleEntity>

27: {

28: public void Save(RoleEntity abstractEntity)

29: {

30:     // специфические действия для RoleEntity

31: }

32: }

Теперь наши изменения будут локализованы в конкретных объектах.

 










Последнее изменение этой страницы: 2018-05-27; просмотров: 195.

stydopedya.ru не претендует на авторское право материалов, которые вылажены, но предоставляет бесплатный доступ к ним. В случае нарушения авторского права или персональных данных напишите сюда...