Автор оригинала: triumvirat
и какой в этом смысл? объект подразумевает под собой единство методов и их свойств. А Вы предлагаете объект на две и более половинки разрубить.
Угу, это в старых умных книжках так написано, только это единство можно понимать очень по-разному.
Задача
Создать объект Lorry (Грузовик), обладающий свойствами классов Car и Trailer.
Дано
1. PHP
2. Объекты Car (Автомобиль), Trailer (Прицеп).
Решение 1. Классическое, с наследованием аггрегацией и делегированием.
Кому-то нужно создать класс с множественным наследованием. Из принципа, шоб було. Это классическое решение, оно некузявое, но оно работает и все равно другого способа в PHP нет.
PHP:
interface ICar
{
public function drive();
}
interface ITrailer
{
public function load();
}
public class Car implements ICar
{
public function drive() { }
}
public class Trailer implements ITrailer
{
public function load() { }
}
class Lorry extends Car implements ITrialer
{
private var $trialer = new Trailer();
public function load()
{
return $this->trialer->load();
}
}
Решение 2. Наследование и агрегация без делегирования.
Но надо ли грузовикуу знать о том, что находиться в прицепе? Зачем ему содержать в себе методы прицепа? А если мы захотм новый прицеп побольше, покрасивше? Тогда менять грузовик целиком? Методы класса должны быть однородны, узкоспециализированы, как говорят, обладать высокой зацепленностью (High cohesion). Поэтому просто делаем прицеп свойством класса Грузовик
PHP:
class Lorry extends Car
{
public var $trialer = new Trailer();
}
Решение 3. Когда наследование не нужно.
Может быть так, что классу Lorry не нужно публиковать методы одного или обоих классов предков, а он использует их внутри себя. Тогда, может быть, лучше спрятать их, чем множественное наследование ибо... да, да священная Инкапсуляция.
PHP:
class Lorry
{
private var $car = new Car();
private var $trialer = new Trailer();
public function doSomething()
{
$this->tralaer->load();
$this->car->drive();
}
}
Решение 4. Аггрегация без наследования и делегирования.
Очень часто (по крайней мере, к этому надо сремиться) свойства Автомобиля и Прицепа не используются в программе одновременно. Например грузчику, который загружает прицеп, совершенно наплевать на то, какому автомобилю он принадлежит, а водителю, в свою очередь, по барабану, что лежит в прицепе. В этом случае наследование вообще не нужно, а Грузовик авляется комбинацией Авотмобиля и Прицепа. Это значает что классы Водитель и Грузчик никак не заисят от неинтересующих их частей грузовика это приводит к низкой сязанности классов (Low coupling).
PHP:
class Lorry
{
public var $car = new Car();
public var $trailer = new Trailer();
}
class Driver
{
function drive(ICar $car)
{
$car->drive();
}
}
class Loader
{
function load(ITrailer $trailer)
{
$trailer->load();
}
}
$lorry = new Lorry();
$driver->drive($lorry->car);
$loader->load($lorry->trailer);
Решение 5. Классы-помощники.
Предположим, есть водитель, активно пользующийся грузовиком, его интересует скорость автомобиля, вес и высота. Значит, класс грузовика должен выглядеть так.
PHP:
class Lorry extends Car
{
private var $trialer = new Trailer();
public function getSpeed(){ }
public function getWeight(){ }
public function getHeight(){ }
}
Но тут на сцене появляется Бухгалтер и его интересует стоимость автомобиля, мы добавляем в него метод getPrice(), потом повляется Блондинка, и ее интересует количество шашечек, добавляем метод getChequeredPattern(), потом пояляется Физик... Ой ё! В результае, в классе оказывается стописят методов. Это говорит о том, что в зависимости от контекста, объект может быть рассмотрен с разных точек зрения.
На это есть два подхода: минималистический и гуманистический. Гуманисты говорят: нам очень часто нужно получать последнее значение массива, поэтому довайте добавим туда метод last(), а заодно first(), а потом еще немного и еще немного. Минималисты говорят: нефиг засирать интерфейс класса, у вас есть все необходимое, а последний элемент можно получить прямым вычислением вот так.
PHP:
$last = $array[count($array) - 1]
Но есть еще способ: классы-помощники, содержащие в себе статическикие методы, с их использованием вышеприведенный пример выглядит так.
PHP:
ArrayHelper::last($array).
Не очень здорово, правда? Однако это лучше, чем запихивать все-все-все в один класс. Кроме того, например, в С# имеются классы-расширения, статические методы которых можно вызывать как методы экземпляра, поэтому пример
PHP:
ArrayHelper::last($array)
на C# можно переписать так
PHP:
static class ArrayHelper
{
public static Last(this int[] array)
{
return array[array.Length - 1];
} // Обратите внимание на this.
}
array.Last(); // Здесь вызывается метод класса ArrayHelper на самом деле.
Tо, что надо.
Методы расширения - это первый шаг к DSL (Domain Specific Language) - языков конкретных задач. Для Физика это будет один язык, для Блондинки другой. Однако в PHP этого нет. Поэтому наиболее близким к DSL решением, не смешивать все контксты в одном классе, являются классы-помщники.
PHP:
class PhysicLorryHelper
{
public static getMass($lorry) { }
}
class BlondLorryHelper
{
public static getChequeredPattern($lorry) { }
}
Решение 6. Функциональное.
Однако есть еще психи-вероотступники попирающие святую троицу Полиморфизм, Инкапсуляция, Наследование и заявляющие, что, дескать, объекты вообще не должны содеражть методов - этo просто данные, а функции должны существовать сами по себе, хуже того, сами функции тоже должны быть объектами. Да, а еще объекты должны быть неизменяемыми. Этих еретиков зовут функциональщиками. Собственно, мы все немного функциональщики, ведь SQL это функциональный язык.
Допустим, вы решили сохранить объект Lorry в базу данных. Вы продвинуый чувак и используете модную ORM. Да вот беда - класc Lorry содержит в себе огромное текстовое поле, содержащее в себе всю историю его поездок, а пользвателю надо всего лишь показать два поля - грузопдъемность и номер. Проблема в том, что ему не нужен весь объект целиком, а нужна только его часть, иначе это приведет к большим потерям производительности. И тут начинаюся танцы с бубном: в ORM вводятся механизмы кеширования, ленивой загрузки, упреждающего чтения. И все равно это не будет работать оптимально. В конченом итоге, пишется хак в виде прямых SELECTов для ондих случаев и для других. Ради чего все это? чтобы не изменять святым пинципам ООП? Вот пример запроса на С# (частично функциональный язык), выбирающий только два поля из базы.
PHP:
var context = new DatabseContext();
var query = from lorry in context.Lorry where lorry.Сapacity > 2000 select new { lorry.Capacity, lorry.Numer };
foreach (var lorry in query)
{
Console.WriteLine(lorry);
}
В сайтах типа Amazon.com 90% функционала - это тупое отображение данных из базы на страницу. Что угодно может быть показано как угодно. Там ООП с его косными классами не то что полезен, а даже вреден, ибо на каждый случай класс не придумешь. В новой версии PHP пытаются ввести основы функционального программирования, так что будем посмотреть.
Решение 7. Кодогенерация.
Справедливости ради надо сказать, что иногда множественное наследоваение все же нужно или просто красиво. Но если его нет, можно изпользовать кодогенерацию. Да это не то, что хотелось бы, но... в конце концов делать это раком тоже надо уметь.