Есть несколько вещей, которые вы не рассматриваете. Существуют абстрактные классы и абстрактные методы. Абстрактные классы не могут быть непосредственно созданы, они, так сказать, просто шаблоны. Абстрактные методы могут быть определены только в классе abstrac, а должен быть пустым (т. Е. Без тела), но мы все это знаем. Однако абстрактные классы могут предоставлять не абстрактные методы. Прекрасным примером является Log4j's AppenderSkeleton
(см. http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/AppenderSkeleton.html).
AppenderSkeleton
- это абстрактный класс.У этого есть конкретные методы, которые вы можете переопределить, у него есть абстрактный метод (append
), который вы должны реализовать, если вы наследуете (если ваш класс также не является абстрактным классом) и два метода, которые исходят из интерфейса Appender
, который также должен быть реализовано или прошло как абстрактное для любого дочернего класса (close
и requireLayout
).
Теперь, если вы хотите, чтобы написать свой собственный Appender, скажем, чириканье некоторые вещи, вы бы начать:
public class TweetAppender extends AppenderSkeleton {
public boolean requiresLayout() {
return false;
}
public void close() {
// do nothing
}
@Override
protected void append(LoggingEvent event) {
// take the message and tweet it!
}
}
Таким образом, все тонкости относительно лесозаготовок (с помощью фильтров, настройки уровней, ошибки хендлеры). Вам просто нужно выполнить фактическое ведение журнала, и log4j сделает все остальное за вас. Конечно, ваш TweetAppender
может переопределить другие методы, если хотите. Возможно, вам нужна специальная обработка ошибок, в этом случае вам просто нужно переопределить setErrorHandler
.
Теперь представьте, что вы также хотите реализовать приложение для Facebook и другое, чтобы изменить свой статус в Skype. Предположим, что все они выставляют API через веб-службы, чтобы публиковать обновления статуса, изменения и т. Д. Довольно скоро вы поймете, что есть несколько вещей, которые бы были похожи, например, при вызове веб-служб и т. Д. Кроме того, вы заметили, что у Skype есть какой-то формат, а у Tweeter есть еще один, а что нет. Таким образом, вы мудры и сделать WebServiceAppender:
public abstract class WebServiceAppender extends AppenderSkeleton {
public boolean requiresLayout() {
return false;
}
public final void close() {
// do extra clean up of resources
}
// make this final so no one can do strange stuff
protected final void append(LoggingEvent event) {
// do a lot of stuff, like, opening up a connection
// send an xml, close the connection and stuff...
// ...
// ready to send the message!
final String messageToSend = getFormattedMessage(event);
// send the message and do lots of complicated stuff
// ...
// close and clean up
}
// let the implementations decide on the format
protected abstract String getFormattedMessage(LoggingEvent event);
}
Теперь ваш TweetAppender
будет выглядеть
public class TweetAppender extends WebServiceAppender {
@Override
protected String getFormattedMessage(LoggingEvent event) {
// use tweeter's specific format
}
public boolean requiresLayout() {
return super.requiresLayout();
}
}
Объявив getFormattedMessage
, как абстрактные, WebServiceAppender
вынуждает любую реализацию на самом деле обеспечить реализацию, которая принимает LoggingEvent
и возвращает String
. Также обратите внимание, что, объявив методы append
и close
в качестве окончательных, WebServiceAppender
запрещает любую имплементацию, чтобы переопределить эти методы. Метод requireLayout
по-прежнему открыт для переоценки.
Другим интересным признаком классов, наследуемых от абстрактных классов, является использование super
. Подумайте об этом как о родительском классе 'this
. В случае с TweetAppender реализация метода requireLayout решает в основном отложить ответственность за решение, требует ли этот appender макет или нет, просто используя родительский класс.
Так положить все это вместе:
public class YourParentClass {
public void doThis() {
if (1 < 0){
int x = 34
}
}
public class YourChildClass extends YourParentClass {
@Override
public void doThis() {
// do I want to do this, or something else?
if (iGuessIWillDoThis) {
super.doThis();
} else {
// do something else
}
}
}
Во всяком случае, мои два цента.
Btw, вы говорите о C++ или java? Насколько мне известно, в Java интерфейсы почти всегда лучше, чем абстрактные базовые классы. – Falmarri
@falmarri, я не согласен, они совершенно разные. Обратите внимание, что вы можете использовать их вместе. Интерфейсы предназначены для определения поведения, абстрактные классы предназначены для обеспечения функциональности * some *, но позволяют заполнять окончательные детали позже. – hvgotcodes