Есть ли какая-то четкая разница, почему использовать абстрактные для расширений, если мы можем сделать то же самое с обычными исключениями классов, для которых, например, не предусмотрен контракт.
abstract class Survivalneeds {
abstract public function eat(); // everyone eats but different foods which would probably work as contract
public function breathe() {
// everyone inhale o2 exhale co2 only for animals
}
}
Сейчас
class human extends Survivalneeds {
protected function eat() {
//sometimes eat goat
// contract
}
breathe()// already extending having same functionality inhale o2 and exhale co2}
class goat extends Survivalneeds{
protected function eat() {
//wee eat greens
// contract
}
breathe()// already extending having same functionality inhale o2 and exhale co2
}
Теперь та же функциональность может быть предоставлена обычным классом путем расширения, за исключением метода контракта, и для контракта мы также можем использовать интерфейс.
То, что вы говорите, его правильное наследование работает в обоих случаях, но идея абстрактного класса заключается в том, что он является некоторой общей логикой, разделяемой классами x, которые расширяют эту функциональность, но сами по себе не могут быть созданы, поскольку это не имеет смысла (возможно, вы хотите, чтобы в вашей системе были только типы автомобилей, а не универсальный автомобиль без марки)
Также, если вы будете использовать обычный класс и интерфейс, вы будете вынуждены создать заглушку в классе, чтобы следовать контракту. Таким образом, вы сможете создать экземпляр класса. И только представьте, что вы будете использовать эту общую функцию в вашем высшем классе.
interface Crashable{
function crash();
}
class Car implements Crashable{
function crash(){}
function getCrashParams(){
return $this->crash();
}
}
class Volvo extends Car{
function crash(){
parent::crash(); // will be OK that it's not right
//.. specific params
return $params;
}
}
class Saab{
function crash(){
//.. specific params
return $params;
}
}
$car = new Car(); // will be ok, that it's not right
//getCrashParams() function in a Car will use the local version of the crash() and not the function of it's child that will kill the data flow
Вы должны использовать интерфейс всякий раз, когда у вас есть потребность в контракте. Вы должны использовать абстрактный класс в случае, если для некоторых схожих классов есть общая функциональность, и вы не хотите повторять код (DRY :). Конечно, всегда лучше использовать композицию, но сейчас не время для обсуждения 🙂
Проблема с вашим кодом (с классом Survivalneeds) заключается в том, что класс с одной стороны отвечает за контракт (методы дыхания и приема пищи), а с другой — за обеспечение общей функциональности. Вы можете изменить свой код следующим образом:
interface Survivor {
public function eat();
public function breathe();
}
abstract class Survivalneeds implements Survivor {
public function breathe() {
// method's body
}
}
При такой реализации обязанности распределяются. Также ясно, что все классы, которые будут расширять Survivalneeds, должны будут также выполнять контракт Survivor.