Мне сложно понять, когда использовать интерфейс, а не абстрактный класс и наоборот. Кроме того, я смущен, когда расширяю интерфейс с другим интерфейсом. Извините за длинный пост, но это очень запутанно.
Создание фигур кажется популярной отправной точкой. Скажем, мы хотим, чтобы моделировать 2D-фигуры. Мы знаем, что у каждой формы будет область. Какая разница между следующими двумя реализациями:
с интерфейсами:
public interface Shape {
public double area();
}
public class Square implements Shape{
private int length = 5;
public Square(){...}
public double area()
return length * length;
}
}
с абстрактным классом:
abstract class Shape {
abstract public double area();
}
public class Square extends Shape {
private length = 5;
public Square(){...}
public double area(){
return length * length;
}
Я понимаю, что абстрактные классы позволяют вам определять переменные экземпляра и позволяют давать реализации методов, тогда как интерфейс не может этого делать. Но в этом случае, похоже, эти две реализации идентичны. Так что использовать любой из них хорошо?
Но теперь скажем, что мы хотим описать различные типы треугольников. Мы можем иметь равнобедренные, острые и прямоугольные треугольники. Для меня имеет смысл использовать наследование класса в этом случае. Использование определения "IS-A" : треугольник "Третий треугольник" IS-A. Треугольник "IS-A" . Кроме того, абстрактный класс должен определять поведение и атрибуты, которые являются общими во всех подклассах, поэтому это идеально:
с абстрактным классом
abstract Triangle extends Shape {
private final int sides = 3;
}
class RightTriangle extends Triangle {
private int base = 4;
private int height = 5;
public RightTriangle(){...}
public double area() {
return .5 * base * height
}
}
Мы можем это сделать и с интерфейсами, с интерфейсом Triangle и Shape. Однако, в отличие от наследования классов (используя отношение "IS-A" для определения того, что должно быть подклассом), я не уверен, как использовать интерфейс. Я вижу два пути:
Первый способ:
public interface Triangle {
public final int sides = 3;
}
public class RightTriangle implements Triangle, Shape {
private int base = 4;
private int height = 5;
public RightTriangle(){}
public double area(){
return .5 * height * base;
}
}
Второй способ:
public interface Triangle extends Shape {
public final int sides = 3;
}
public class RightTriangle implements Triangle {
....
public double area(){
return .5 * height * base;
}
}
Мне кажется, что оба эти способа работают. Но когда вы будете использовать один путь над другим? И есть ли какие-либо преимущества в использовании интерфейсов над абстрактными классами для представления разных треугольников? Несмотря на то, что мы усложнили описание фигуры, использование интерфейса vs abstract class по-прежнему кажется эквивалентным.
Критический компонент для интерфейсов состоит в том, что он может определять поведение, которое может использоваться совместно с несвязанными классами. Таким образом, интерфейс Flyable будет присутствовать в классах Airplane, а также в Bird. Таким образом, в этом случае ясно, что подход интерфейса является предпочтительным.
Кроме того, для создания сложного интерфейса, расширяющего другой интерфейс: Когда следует игнорировать отношения "IS-A" при принятии решения о том, каким должен быть интерфейс? Возьмите этот пример: ССЫЛКА.
Почему "VeryBadVampire" должен быть классом, а "Vampire" - интерфейсом? "VeryBadVampire" IS-A "Vampire" , поэтому я понимаю, что "вампир" должен быть суперклассом (может быть, абстрактным классом). Класс "Вампир" может реализовать "Смертельный", чтобы сохранить свое смертоносное поведение. Кроме того, "вампир" IS-A "Monster", поэтому "монстр" должен быть классом. Класс Vampire также может реализовать интерфейс под названием "Dangerous", чтобы сохранить его опасное поведение. Если мы хотим создать новый монстр под названием BigRat, который опасен, но не смертелен, тогда мы можем создать класс BigRat, который расширяет "Monster" и реализует "Dangerous".
Не будет ли вышеприведенный результат таким же, как использование "Vampire" в качестве интерфейса (описано в ссылке)? Единственное различие, которое я вижу, заключается в том, что использование наследования классов и сохранение отношения "IS-A" устраняет много путаницы. Но это не соблюдается. В чем преимущество этого?
Даже если вы хотите, чтобы монстр делил вампирское поведение, всегда можно переопределить, как объекты представлены. Если бы нам нужен новый тип вампирского монстра под названием "VeryMildVampire", и мы хотели создать вампир-монстра под названием "Чупакабра", мы можем это сделать:
"Класс вампира" расширяет "орудия монстров" "Опасный", "Смертельный", "Кровавый щит"
Класс "VeryMildVampire" расширяет класс "Вампир"
Класс "Чупакабра" расширяет "орудия монстров" BloodSuckable
Но мы также можем это сделать:
'VeryMildVampire' расширяет возможности монстров. Опасный, смертельный, вампирский
"Чупакабра" расширяет орудия "Монстр" Опасный, вампирский
Второй способ создает интерфейс "Vampiric", чтобы мы могли легче определить связанный монстр, а не создавать пучок интерфейсов, которые определяют вампирическое поведение (как в первом примере). Но это нарушает отношения IS-A. Поэтому я смущен...