Путаница с интерфейсами и абстрактными классами в Java с примерами

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

Создание фигур кажется популярной отправной точкой. Допустим, нам нужен способ моделирования 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;
    }
}

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

Важным компонентом интерфейсов является то, что он может определять поведение, которое может быть общим для несвязанных классов. Таким образом, интерфейс Flyable будет присутствовать как в классах Airplane, так и в Bird. Так что в данном случае понятно, что интерфейсный подход предпочтительнее.

Кроме того, чтобы создать запутанный интерфейс, расширяющий другой интерфейс :Когда следует игнорировать отношение «IS -A» при принятии решения о том, каким должен быть интерфейс? Возьмем этот пример:ССЫЛКА .

Почему «VeryBadVampire» должен быть классом, а «Vampire» — интерфейсом? «VeryBadVampire» ЯВЛЯЕТСЯ -«Вампиром», поэтому я понимаю, что «Вампир» должен быть суперклассом (, возможно, абстрактным классом ). Класс «Вампир» может реализовать «Смертельный исход», чтобы сохранить свое летальное поведение. Кроме того, «Вампир» ЯВЛЯЕТСЯ -«Монстром», поэтому «Монстр» также должен быть классом. Класс «Вампир» также может реализовать интерфейс под названием «Опасный», чтобы сохранить свое опасное поведение. Если мы хотим создать нового монстра под названием «Большая крыса», опасного, но не смертельного, то мы можем создать класс «Большая крыса», который расширяет «Монстр» и реализует «Опасный».

Разве приведенное выше не приведет к тому же результату, что и использование «Вампира» в качестве интерфейса (, описанного в ссылке )? Единственная разница, которую я вижу, заключается в том, что использование наследования классов и сохранение отношения «IS -A» устраняет большую путаницу. Однако это не соблюдается. В чем преимущество этого?

Даже если вы хотите, чтобы монстр разделял поведение вампира, всегда можно переопределить способ представления объектов. Если бы мы хотели новый тип монстра-вампира под названием «VeryMildVampire» и хотели бы создать похожего на вампира -монстра под названием «Чупакабра»,мы можем сделать это:

Класс «Вампир» расширяет класс «Монстр», реализует «Опасный», «Смертельный», «Кровососущий»
Класс «VeryMildVampire» расширяет класс «Vampire»
Класс «Чупакабра» расширяет класс «Монстр», реализует «Кровососущий»

Но мы можем сделать и это:

«VeryMildVampire» расширяет «Monster», реализует Dangerous, Lethal, Vampiric
«Чупакабра» расширяет «Монстр», реализует Опасный, Вампирический

Второй способ здесь создает «вампирический» интерфейс, чтобы нам было легче определить связанного монстра, а не создавать кучу интерфейсов, которые определяют поведение вампира (, как в первом примере ). Но это нарушает отношения ЕСТЬ -А. Вот я и запутался...

19
задан Carts 9 July 2012 в 05:19
поделиться