Заменить случай переключения: интерфейс против абстрактного класса

#java #design-patterns #polymorphism

#java #шаблоны проектирования #полиморфизм

Вопрос:

У меня есть код, в котором есть оператор switch, и я хочу улучшить дизайн.

Есть интерфейс и способ абстрактного класса.

Я хочу знать, какой способ лучше и почему?

У меня есть следующий класс :

 enum MovieChargeType {regular, new_release }

class Movie {
    public MovieChargeType type;
    public double get_price(){
          switch (type){
                  case regular: //return regular price
                  case new_release : // return new_release price }
    } 

}
  

Итак, я решил улучшить дизайн, используя 2 способа:

1 способ — интерфейсы

 Interface MovieType{
    public double get_price();
}

class RegularMovie implements MovieType{
    public double get_price(){
        // return regular price
    }
}

class NewMovie implements MovieType{
    public double get_price(){
        // return new price
    }
}

class Movie{
        public MovieType type;
        public double get_price(){
              type.get_price();
        } 
    
    }
  

2 способа — абстрактные классы:

 abstract class Movie {
        public MovieChargeType type;
        public abstract double get_price();
    
    }

class RegularMovie extends Movie{
        public double get_price(){
            // return regular price
        }
    }
    
    class NewMovie extends Movie{
        public double get_price(){
            // return new price
        }
    }
  

Я хочу знать, какой из них лучше в таком случае?
Я заметил, что люди склонны использовать интерфейсы, но кто-нибудь может объяснить, почему?

Комментарии:

1. Примечание сбоку: обзоры рабочего кода… может лучше перейти к codereview.stackexchange.com (но внимательно прочитайте их справочный центр, прежде чем публиковать там). И примечание: узнайте о соглашениях об именовании Java. Имена методов меняются getPrice() … _ только для SOME_CONSTANT.

2. Соглашения об именовании Java используют camelCase, а не snake_case для всего, кроме констант.

Ответ №1:

Я хочу знать, какой из них лучше в таком случае? Я заметил, что люди склонны использовать интерфейсы, но кто-нибудь может объяснить, почему?

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

Вот почему большинство разработчиков стремятся использовать интерфейсы и максимально упростить подпись интерфейса. Я имею в виду, что вы не должны использовать какие-либо библиотечные классы в подписи интерфейса. Используйте POJOs только для того, чтобы ваши интерфейсы зависели только от чистой Java и не зависели от других библиотек.

Абстрактные классы могут быть очень полезны, когда вы хотите реализовать шаблон шаблонного метода. Метод шаблона определяет абстрактный алгоритм, который может быть расширен за счет подклассов. Они просто переопределяют или реализуют абстрактные методы из родительского класса. Абстрактные классы могут реализовывать поведение. Итак, если существует общее поведение для всех ваших подклассов, абстрактный класс может быть хорошим выбором. Но имейте в виду, что абстракции должны быть стабильными. Если у вас есть абстракции, которые часто меняются, это влияет на все ваши подклассы. Эта проблема может резко возрастать с каждым уровнем иерархии и затруднять обслуживание вашего кода. Хорошее правило заключается в том, что чем выше класс в вашей иерархии, тем более стабильным он должен быть.

Ответ №2:

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