Абстрактный класс и интерфейс вместе?

У меня есть раздел моего кода, в котором некоторые классы реализуют интерфейс.

Он чувствует себя правильно, но между дочерними классами существует небольшое дублирование, а именно 3 метода.

Итак, это кричит, чтобы использовать абстрактный класс.

Мой вопрос: будут ли какие-либо недостатки в использовании как абстрактного класса, так и интерфейса в следующих ситуациях:

  • Абстрактный класс для реализации интерфейса и дочерних классов для расширения абстрактного класса
  • Детские классы для расширения абстрактного класса и реализации интерфейса

или

Если абстрактные классы и интерфейсы не будут использоваться вместе вообще?

Ответ 1

Совершенно нормально использовать эти два вместе. Рассмотрим, например, AbstractList (внедрение List) и AbstractMap (реализация Map) в JDK.

Моя реакция на коленный рефлекс заключалась бы в том, чтобы абстрактный класс реализовал интерфейс, а затем из него получили конкретные классы:

abstract class Base implements TheInterface {
    /* ...shared methods... */
}

class Concrete1 extends Base { }

class Concrete1 extends Base { }

Но ваш вопрос, поднимающий другую возможность, заставлял меня думать, и я не вижу большого аргумента против этого:

abstract class Base {
    /* ...shared methods... */
}

class Concrete1 extends Base implements TheInterface { }

class Concrete1 extends Base implements TheInterface { }

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

Также есть третий вариант: Композиция. У вас не может быть абстрактного класса вообще, но, скорее, несколько конкретных классов, реализующих интерфейс, используют общий вспомогательный класс в своей реализации:

class Helper {
    /* ...shared methods... */
}

class Concrete1 implements TheInterface {
    /* ...uses instance of Helper */
}

class Concrete1 implements TheInterface {
    /* ...uses instance of Helper */
}

Это имеет ту же гибкость в другой форме.

Ответ 2

Я не думаю, что существует такое эмпирическое правило. При проектировании старайтесь следовать принципам SOLID, чтобы выяснить, хорошо ли это или плохо. Эти принципы можно найти в здесь. В этом конкретном случае я бы подумал, что вы должны убедиться, что соблюдаете принцип "открытого закрытия".

Ответ 3

Лично я предпочитаю утверждение, что абстрактный класс является фоном для других классов, поэтому, если у трех других классов есть что-то общее, их общий предок, абстрактный класс, созданный только для этих трех других классов, должен также содержать код из интерфейса, Это сделало бы абстрактный класс "полным" (на его пути), обеспечивающим все эти свойства и методы, которые разделяют три класса.

Однако, в конечном итоге, не имеет значения, будут ли они реализовывать один и тот же интерфейс. Понятие абстрактного класса, дающее все, что является общим, на мой взгляд более ясным образом. Затем легче сравнивать классы, глядя только на то, что отличается.