PHP-интерфейс и абстрактная классная практика для наследования?

При определении структуры и наследования интерфейса и/или абстрактного класса, какой из них является лучшей практикой? И почему? Вот два примера:

Вот пример для [Interface] → [Abstract Class] → [Class]

Interface DataInterface
{
    public function __construct($connection);
    public function connected();
    public function get();
}

Abstract class BaseData implements DataInterface
{
    protected $connection;

    public function __construct($connection)
    {
        $this->connection = $connection;
    }
}


class UserData extends BaseData
{
    public function exists()
    {
        return is_connected($this->connection);
    }

    public function get()
    {
        return get_data($this->connection);
    }
}

$oUserData = new UserData(new Connection());

И вот образец для [Абстрактного класса] → [Класс] без интерфейса

Abstract class BaseData
{
    protected $connection;

    public function __construct($connection)
    {
        $this->connection = $connection;
    }

    abstract public function connected();
    abstract public function get();
}

class UserData extends BaseData
{
    public function exists()
    {
        return is_connected($this->connection);
    }

    public function get()
    {
        return get_data($this->connection);
    }
}

$oUserData = new UserData(new Connection());

В настоящее время я создаю небольшое приложение (может увеличиться) и путаюсь, как правильно реализовать в начале.

Кстати, это объявление для __construct() с параметром имеет смысл в интерфейсе?

public function __construct($connection);

Ответ 1

Абстрактные классы определяют интерфейс, который должен быть реализован для наследников абстрактного класса. Interface-Construct определяет интерфейс, который должен реализовываться классом, реализующим конструкцию интерфейса, реализация интерфейса не ограничивается одним интерфейсом, тогда как наследование класса связано с одним (абстрактным) классом.

Интерфейсы в PHP преднамеренно используются для обозначения типов ограниченного подмножества всего интерфейса класса. Нет причин для интерфейса на абстрактных классах, поскольку их получатель экземпляров их наследников не использовал их (с типом или логической идентификацией над instanceof/is_a). Более ценным преимуществом интерфейсных конструкций является возможность замены общей реализации интерфейсов с альтернативной реализацией.

В случае вашего примера BaseData я рекомендую отказаться от абстрактной идеи и использовать вместо нее характер и отдельные интерфейсы.

trait connectionBrokerConstructor {
    protected $connection;

    public function isConnected()
    {
        return $this->connection instanceof Connection;
    }

    public function setConnection(Connection $connection)
    {
        $this->connection = $connection;
    }
}

interface connectable
{
    public function setConnection(Connection $connection);
    public function isConnected();
}

interface userDataRepositoryInterface
{
    public function get();
}

class UserData implements connectable, userDataRepositoryInterface
{
    use connectionBrokerConstructor;

    public function __construct(Connection $connect = null)
    {
        $this->setConnection($connection);
    }

    public function get()
    {
        return array('something');
    }
}

Ответ 2

Действительно абстрактные классы и интерфейсы различны.

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

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

------- Редактировать с помощью примера

Я не эксперт в таких вещах, но я всегда делал interfaceclass.

Например, вот простой интерфейс:

interface AnimalInterface {

  public function all();

  public function findBySlug($slug);

}

И вот класс, который реализует этот интерфейс (упрощенный):

class AnimalEloquentRepository implements AnimalInterface {

  public function all()
  {
    return Animal::all();
  }

  public function findBySlug($slug)
  {
    return Animal::whereSlug($slug)->first();
  }

}

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

Я уверен, что есть лучшие способы сделать что-то, но в прошлом это работало очень хорошо.

Ответ 3

Моя репутация не позволяет комментировать, но этот оператор в tr0y-ответе вводит в заблуждение:

"Абстрактные классы определяют интерфейс, который должен быть реализован наследникам абстрактного класса.

Одно из основных различий между интерфейсом и абстрактным классом заключается в том, что вам не требуется реализовать какой-либо из методов абстрактного класса в классе, который его расширяет. Для абстрактного класса довольно распространено использование методов с поведением по умолчанию и только предоставление этих методов у наследников, если поведение по умолчанию не является тем, что вы хотите.

С другой стороны, вам необходимо создать все методы, указанные в интерфейсе в любом классе, который реализует этот интерфейс.

Другое отличие состоит в том, что методы в интерфейсе не могут иметь тело, тогда как методы в абстрактном классе должны иметь тело.