Этот вопрос уже имеет ответ здесь:
То, когда я узнал об абстрактных классах, сказал что WT (H*)!!!
ВОПРОСЫ:
** если Вы знаете то, что я имею в виду*
interface
, некоторые нет) - он не знает реализация (которая должна быть предоставлена подклассами / реализующими классами) Например:
public abstract class Stream { /* lots of code, some abstract methods */ }
Что, черт возьми, за поток сам по себе ? Какой вид потока? поток в файл? сеть? буфер памяти? У каждого могут быть разные и не связанные друг с другом способы чтения / записи, но они предоставляют общий API. бессмысленно создавать только поток
, но с помощью абстрактного класса
вы можете код для Stream
API, не зная подробностей:
Stream s = CreateStream(...); // I don't *care* what kind of stream
s.Write(new byte[] {1,2,3,4,5});
s.Close();
Просто скрыть реальную реализацию от клиента, который ее использует.
Теперь вы спросите, зачем нам это нужно, поскольку интерфейсы обслуживают один и тот же механизм ... Рассмотрим простой пример регистратора
interface ILogger
{
string PrepareLog(System.Exception ex);
void InitializeLogger(string Type);
int WriteLog(string msg);
}
Любой клиент регистрации, реализующий этот интерфейс, должен реализовывать все эти функции
class EventLogger : ILogger
{
public override void InitializeLogger(string Type)
{
//Event Logger Initialize
}
public override int WriteLog(string msg)
{
//Write to event log
return 1;
}
public override string PrepareLog(System.Exception ex)
{
return ex.StackTrace ;
}
}
class FileLogger : ILogger
{
public override void InitializeLogger(string Type)
{
}
public override int WriteLog(string msg)
{
//Write to File
return 1;
}
public override string PrepareLog(System.Exception ex)
{
return ex.StackTrace ;
}
}
class MailLogger : ILogger
{
public override void InitializeLogger(string Type)
{
}
public override int WriteLog(string msg)
{
//Write to mail
return 1;
}
public override string PrepareLog(System.Exception ex)
{
//prepare HTML Formatted msg
return ex.StackTrace ;
}
}
А классы EventLogger, FileLogger и maillogger реализуют iLogger и предоставляют контекстно-зависимую реализацию . Теперь мы хотели скрыть реальную реализацию PrepareLog, и это выполнило бы обычную операцию по подготовке сообщения журнала из объекта исключения.
В нашей текущей реализации у нас нет возможности сделать один метод конкретным, а другие - просто контрактами.
так что давайте немного изменим реализацию с абстрактными классами.
abstract class AbstractLogger:ILogger
{
#region ILogger Members
public virtual string PrepareLog(System.Exception ex)
{
return ex.StackTrace;
}
public abstract void InitializeLogger(string Type);
public abstract int WriteLog(string msg);
#endregion
}
class EventLogger : AbstractLogger
{
public override void InitializeLogger(string Type)
{
//Event Logger Initialize
}
public override int WriteLog(string msg)
{
//Write to event log
return 1;
}
}
class FileLogger : AbstractLogger
{
public override void InitializeLogger(string Type)
{
}
public override int WriteLog(string msg)
{
//Write to File
return 1;
}
}
class DBLogger : AbstractLogger
{
public override void InitializeLogger(string Type)
{
}
public override int WriteLog(string msg)
{
//Write to DB
return 1;
}
}
class MailLogger : AbstractLogger
{
public override void InitializeLogger(string Type)
{
}
public override int WriteLog(string msg)
{
//Write to mail
return 1;
}
public override string PrepareLog(System.Exception ex)
{
//prepare HTML Formatted msg
return ex.StackTrace ;
}
}
Теперь я создал AbstractLogger класс, который наследуется от iLogger и реализует только метод PrepareLog, оставшиеся методы остались абстрактными. поэтому потребители будут писать контекстно-зависимый код в своей реализации.
Итак, теперь метод PrepareLog, полностью скрытый (то есть подготовка журнала) от пользователей, инициализировал любой из регистраторов.
Абстрактные классы служат полезной цели, поскольку могут создавать класс, который воплощает концепцию, а не что-то конкретное. Например, Животное может быть классом, но никто не может быть просто животным, это либо Птица , Собака , либо Кошка и т. Д. которые представляют собой разные виды животных. Надеюсь это поможет. Это также связано с такими понятиями, как наследование и полиморфизм.
И интерфейсы, и абстрактные классы способствуют слабой связи в вашей кодовой базе. Абстрактные классы - это компромисс между интерфейсами и конкретными классами, потому что абстрактные классы могут иметь реальные методы с реализованным поведением.
В общем, предпочитаю интерфейсы. Абстрактные классы полезны, когда у вас есть дерево наследования, в котором есть общие операции, которые будут использоваться дочерними классами. Но даже здесь вы можете объявить, что ваш абстрактный класс реализует интерфейс. Джош Блох называет это шаблоном «абстрактный интерфейс». Это позволяет вам развертывать различные реализации даже абстрактного класса, который, как это ни парадоксально, на самом деле не является полностью абстрактным - только интерфейсы.
Абстрактные (базовые) классы предоставляют вам полубетонные классы для реализации вашей иерархии классов. Они позволяют вам делать несколько вещей:
Список продолжается.
Смысл в том, чтобы указать методы, которые производные классы должны реализовывать, так же, как интерфейс , но также предоставляют некоторые реализации (которые интерфейс не могу сделать). Абстрактные классы, строго говоря, никогда не «необходимы», но они полезны. Просто найдите их в стандартной библиотеке .NET или Java и убедитесь сами, как они используются. Вы найдете множество примеров.
1) В чем смысл создания класса, который нельзя создать?
Просто потому, что что-то не так. создание экземпляра напрямую не означает, что он полезен. Абстрактный класс играет важную роль в наследовании и может быть очень полезен при проектировании унаследованных от него классов.
Например, я использовал абстрактные классы раньше, чтобы определить базовую структуру того, чему класс должен соответствовать. Затем я определил унаследованные классы на основе этого абстрактного класса, и если я пропустил требуемый метод или что-то еще, его может подобрать компилятор.
Абстрактные классы полезны, только если вы используете наследование . Вы создаете подклассы, которые должны реализовывать тот же интерфейс , что и ваш абстрактный класс, и унаследовать некоторую базовую реализацию, которую вы, возможно, определили в своем абстрактном классе.
Абстрактный класс - это класс, экземпляр которого невозможно создать. Например, квадрат, круг, или Rectangle - это тип формы, который может быть производным от класса Shape.
Shape будет содержать код, общий для Square, Circle или Rectangle, такой как вычисление площади формы. Но создание экземпляра формы было бы бесполезным, поскольку это абстрактная концепция, а квадраты, круги и прямоугольники являются реальными объектами.
Абстрактный класс - это абстракция . Это гарантирует существование поведения, но не определяет его реализацию. Это дает вам возможность изменить способ реализации поведения, если вы позже решите, что хотите.
Абстракция подобна клавиатуре, монитору или сотовому телефону. На всех клавиатурах есть возможность ввода данных; на всех мониторах есть возможность отображать пиксели; и все сотовые телефоны имеют возможность совершать звонки. Но производители этих товаров используют разные способы реализации этого поведения.
Поэтому, когда вы хотите позвонить, Вы можете сделать это практически с любого мобильного телефона, потому что все производители сотовых телефонов создают телефоны, которые придерживаются общей абстрактной идеи о том, что такое сотовый телефон. Вам не нужно заново учиться звонить на Samsung, если вы уже научились делать это на BlackBerry или LG.
Сотовый телефон - это сотовый телефон , и его подклассы абстрактный класс - это все абстрактный класс .
Смысл абстрактного класса в том, чтобы определить (ограничить) ваш интерфейс без описания реализации.
Абстрактный класс может быть создан путем создания совместимого объекта с типом производного класса.
Реализовать чистый интерфейс, скрывающий уродливый специфичный для платформы код. Также, чтобы скрыть рядовых от любого вида воздействия. (Так что вы действительно вынуждены использовать класс с абстрактным интерфейсом.)
Это необходимо, когда у вас есть две разные реализации одного и того же класса, которые радикально отличаются. Подумайте о файле, сокете и блоке памяти. Все они могут сделать доступные для чтения данные доступными - используя абстрактный класс, вы можете реализовать эти три тремя разными способами, даже если код использования (call-сайты) написан одним способом для поддержки всех трех.
Хорошо, теперь вы создали интерфейс со всеми методами, которые меняются в каждой реализации. В процессе программирования вы замечаете, что некоторые блоки кода используются ВСЕМИ реализациями интерфейса.
Эти блоки кода должны находиться в абстрактном классе, а не повторяться в каждой реализации. Таким образом, когда что-то меняется, вы исправляете код только в абстрактном классе, а не в каждой реализации.
Это только абстрактный, потому что вы хотите, чтобы все наследующие классы имели свои собственные реализации.