2015-08-21 11 views
4

Я вижу код, где абстрактный класс реализует интерфейс? Почему ты бы так поступил? Вместо того, чтобы вводить абстрактные элементы в интерфейс, мы можем прямо поместить их в интерфейс правильно?Почему абстрактный интерфейс для реализации класса?

Зачем писать такой код? Какова цель или необходимость?

ответ

0

Предположим, вы разрабатываете популярную библиотеку с интерфейсом IStream, которая используется в различных API-интерфейсах в вашей библиотеке. IStream имеет следующие методы:

int Read(byte[] buffer, int offset, int count); 
void Write(byte[] buffer, int offset, int count); 

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

public abstract int Read(byte[] buffer, int offset, int count); 
public abstract void Write(byte[] buffer, int offset, int count); 

Многие люди следуют вашим рекомендациям, но не все читают документацию, поэтому некоторые реализуют IStream напрямую.

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

Task<int> ReadAsync(byte[] buffer, int offset, int count); 
Task WriteAsync(byte[] buffer, int offset, int count); 

Теперь вы идете, чтобы обновить свой абстрактный класс, чтобы сделать его компиляции.Вы могли бы сделать новые методы абстрактно, но, как выясняется, есть не-совсем-безумная альтернатива (обработка ошибки опущены):

public virtual Task<int> ReadAsync(byte[] buffer, int offset, int count) 
{ 
    return Task.Run(() => this.Read(buffer, offset, count)); 
} 

public virtual Task WriteAsync(byte[] buffer, int offset, int count) 
{ 
    return Task.Run(() => this.Write(buffer, offset, count); 
} 

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

Теперь давайте посмотрим на две группы людей. Люди, которые наследуют от абстрактного класса, автоматически получают реализацию IStream.ReadAsync и IStream.WriteAsync без необходимости писать больше кода самостоятельно. Их потоки также могут использоваться как есть в ваших новых асинхронных API-интерфейсах без необходимости выполнять какую-либо работу, и есть вероятность, что он просто не сосать.

С другой стороны, людям, которые внедрили интерфейс, теперь приходится иметь дело с написанием собственных реализаций этих методов IStream, даже если они не заинтересованы в использовании асинхронных API. Возможно, они выбрасывают NotSupportedException, чтобы ошибки исчезли. Теперь им нужно убедиться, что они не звонят во что-либо, что может вызвать звонок в IStream.ReadAsync или IStream.WriteAsync. Они недовольны. Они сделали это сами по себе, не выполнив ваши рекомендации, но все же трудно не сочувствовать.

Это большое преимущество, заключающееся в том, что абстрактный класс реализует интерфейс. На самом деле, некоторые могут утверждать, что IStream не должны существовать вообще именно по этой причине, а абстрактный класс Stream должен отображаться только во всех API-интерфейсах, где вместо этого должно было быть IStream. Слепая догадка: может быть, это точно почему есть System.IO.Stream, но нет System.IO.IStream. Хотя я лично предпочел бы иметь IStream.

+0

Удивительный .. очень хороший пример. Спасибо. – user2683098

6

Интерфейс - это просто контракт на другой код. С другой стороны, абстрактный класс может быть намного больше. Они могут:

  • есть члены данных (поля)
  • Наличие фактической реализации кода
  • проистекают из другого базового класса
  • защитили члены

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

public interface IMyCommand 
{ 
    void Execute(); 
} 

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

public abstract class MyTemplateClass : IMyCommand 
{ 
    public void Execute() 
    { 
     MyProcessFirst(); 
     MyProcessSecond(); 
    } 

    protected abstract void MyProcessFirst(); 
    protected abstract void MyProcessSecond(); 
} 

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

+0

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

+0

@ Брэдли ... благодарю так много. Я очень ценю. Очень хороший пример и очень полезный пример .. для проектов в реальном времени .. – user2683098

0

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

2

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

Не с реализацией мы не можем.

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

Мы могли бы хотя спросить обратное; почему бы просто не иметь абстрактного класса и никакого интерфейса.

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

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

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

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

Смежные вопросы