Добрый день. При разработке одной системы столкнулся с вопросом именования классов в сложных случаях. Проблема заключается в том, что в иерархии классов отдельный элемент не всегда имеет только одного «предка» (не только в смысле наследования («цветок наследуется от растения»), но и в более общем «цветок имеет листики»). Собственно, вопрос заключается в следующем — по какому правилу именовать данные элементы?
Несколько примеров классов, с именами которых возникает проблема (прошу прощения за дурацкие примеры):
— Лилия, наследуемая от цветка (B, наследуемый от A)
— Лепесток, который может быть только у лилии (C, который используется только в B)
— Лист, который может быть и у лилии, и у цветка вообще (D, который используется и в B, и в A)
— Тычинка, которая может быть и у лилии, и у мака, но необязательно она есть у цветка вообще (E, который используется и F (который, в свою очередь, наследуется от A), и в B, но в A он не используется)
И крайний случай: есть квартира, в которой есть какая-то красивая фигня. Лилию, в принципе, можно использовать в качестве красивой фигни. Как назвать адаптер «Лилия as Красивая фигня», чтобы он не потерялся среди всех классов (т.к. теоретически он принадлежит группе «квартира», но также зависит и от группы «цветы»)? Абстрактно — есть G, которая использует H; как назвать I, который ползволяет использовать B как H?
Заминка возникла из-за строго иерархической файловой системы и привычки называть классы a-la папки (т.е. Fuston_Http_Response_Cookie_Marked). Эта привычка как раз и не дает ответа на вопрос «как назвать интерфейс, который позволит Testing_Database_Cache_Cookies использовать посторонний класс Fuston_Http_Response_Cookie как свой собственный Testing_Database_Cache_Cookies_Cookie?».
Подумайте не о том как класы назвать, а о том как этим всем пользоваться.
Подумайте в первую очередь об интерфейсах. В вашем случае, на мой взгляд, нужно частично отказаться от наследования и использовать стратегию. Для пораждения объекта использовать паттерн фабрика.
Наследование здесь только самое что ни на есть простейшее, например, Response отражает абстрактный отклик, StaticResponse — отклик, тело которого задается в виде строки, и DynamicResponse — отклик, тело которого генерируется во время отправки самого отклика. Все остальное не наследуется, а всего лишь используется (например, класс Response использует класс Cookie для объектного отображения печенек).
Интересует именно именование, т.к. с использованием проблем нет. Могу дать конкретный пример: есть модуль тестирования EduPortal_Addon_Testing с кучей классов внутри, и есть модуль рейтинга EduPortal_Application_Rating, тоже, как ни странно, с кучей классов внутри. Я хочу добавить функцию использования в качестве рейтинга данных из тестирования. У меня в рейтинге есть абстрактный получатель данных EduPortal_Application_Rating_Getter_Abstract и получатель данных непосредственно от клиента EduPortal_Application_Rating_Getter_Wui. Обычно делают так: создают получатель для модуля тестирования, в который скармливают объект из пакета EduPortal_Addon_Testing (неважно, самый ли главный EduPortal_Addon_Testing_Core, у которого будет API для получения данных, или же специальный отдельный класс-API для этой же функции). Как назвать этот получатель (язык PHP)? EduPortal_Application_Rating_Getter_EduPortal_Addon_Testing? EduPortal_Application_Rating_Getter_EduPortalAddonTesting? EduPortal_Application_Rating_Getter_Testing? Последний кажется идеальным, но что, если появится MathSite_Testing? От какого Testing зависит последний получатель?
, EduPortal_Application_Rating_Getter_Abstract — странное имя. Зачем дописывать Abstract в конце, если у класса в сигнатуре уже абстрактность? Не знаю, как у вас в PHP принято, но я бы назвал EduPortal_Application_Rating_BaseGetter.
А получатель из Testing — он, разве, не принадлежит логически к самому модулю Testing? Если так — то почему бы его туда не поместить и не назвать: EduPortal_Addon_Testing_RatingGetter?
Странное имя вызвано автозагрузкой классов и желанием распихать классы одной «области» в одну директорию. В вашем случае и в стандартах Zend абстрактный класс лучше будет обозвать либо _Getter, либо _Getter_Abstract, т.к. файл BaseGetter.php и папка Getter на быстрый взгляд плохо вяжутся друг с другом.
Извините, задумался и забыл ответить. Идея хорошая, но косяк тот же — кроме EduPortal_Application_Rating появился MathSite_Rating — чей геттер EduPortal_Addon_Testing_RatingGetter?
Вот-вот:
1. Имя класса не будет влезать на строчку — критично для распечатанного исходного кода, да и читаемость убивает
2. Вполне ожидаемый вопрос «ШОЗАНАХ?», ибо эта длинная строка ни о чем не говорит, где кончается описание конкретного класса (геттер для рейтинга) и начинаются параметры описания (откуда тырит данные)
3. Автозагрузка классов хромает — обычный принцип «подчеркивание на слеш» не применишь в силу его абсурдности в данном случае, а умничать с ней пока не хочется
Ну в моем варианте как раз из-за пункта 2 в одном месте стоят два подчеркивания — там, где кончается полное имя Getter'а и начинается имя связанного класса.
А вообще, мне все равно кажется, что нормальным именем было бы:
EduPortal_Application_Rating_AddonTestingGetter, а еще лучше юзать неймспейсы.
Если у вас технические проблемы и так сделать не получается — прийдется вам мучиться с непонятными длинными строками… что ж, повод преодолеть технические проблемы ;)
1. Ни в коем случае не именуйте классы Родитель_дите_ итд
2. Используйте неймспейсы, это избавит от уродских названий
3. Посмотрите на именование классов в зенде (пире), логика тамошних названий говорит сама за себя
Если будете держаться этих трех пунктов, то никогда не потеряетесь =) и названия будут красивые и структурная логика будет всегда понятна.
1. Это и стало причиной такого вопроса (^_^) Есть Response, есть DynamicResponse, наследуемый от Response, и есть ResponseCookie, который используется Response. Response_Dynamic и Response_Cookie — ИМХО, глупость, т.к. отличить потомка от класса-помощника невозможно без кода, но других символов-разделителей, кроме подчеркивания, нет (-_-).
2. Нет неймспейсов, хоть и хочется (-_-) (см. предыдущий ответ).
3. Смотрел, сейчас пересмотрю еще внимательней.
В Zend нет адаптеров между компонентами из разных систем, вроде модуля фреймворка, модуля чужого сайта и модуля собственной разработки, а мне они пока что нужны (пока я не придумал решения лучше) — см. первый ответ, в нем я дал конкретный пример.
/library
/Responce/
/AbstractResponse.php
/Dinamic.php
/Cookie.php
Классы называться будут примерно:
Responce_Dinamyc и Responce_Cookie
Я бы сделал так.
А насчет цветков и лепестков Вы были правы
/Library/
/Flower
/Petal.php
/Flower.php
И классы соответственно:
Flower и Flower_Petal (тут уже логика не родитель — наследник, а соотношение обьект к подобьекту)
Вот она-то и проблема: Response_Dynamic наследуется от Response, а Response_Cookie нет, но при беглом взгляде и без кода это понять нельзя. Меня это и натолкнуло на подобный вопрос.
Дык они только респонзовые (О_О) Или вы предлагаете в случае появления чего-либо, что будет использовать класс, принадлежащий только одному модулю (и слава богу), этот класс выносить на самый верхний уровень иерархии?
Это структура не наследования, а структуры, лепестки тоже не наследуются от цветка, но они относятся к нему, поэтому находятся в папке «цветок», то же самое и с куки
Опять же, куда писать какой-нибудь Foobar, который используется ResponseDynamic и ResponseStatic (которые оба наследуются от Response), но не используются самим Response? Выносить как Response_Foobar? Он не используется Response'ом. Вообще наверх, в Foobar? Сдался мелкий класс реализации на самом высоком уровне приложения.
Хм. Если куки будут называться Response_Cookie, т.к. относятся к Response, то как обозвать тогда динамический отклик (DynamicResponse)? От же не относится к Response в прямом виде (Response его не использует).
class Leaf {} // лист
class Thorn {} // шип (т.к. тычинка вроде как у всех цветковых есть)
class Flower // цветок
{
Leaf[] leaves;
}
interface Thorny // с шипами
{
Thorn[] thorns;
}
class LilyPetal {} // лепесток лилии
interface CuteThing {} // красивая фигня
// лилия — шипастый цветок, да и к тому же, красивая фигня
class Lily : Flower, Thorny, CuteThing
{
LilyPetal[] petals;
}
class Apartment // квартира
{
CuteThing[] things;
}
По поводу именования файлов в стиле:
«как назвать интерфейс, который позволит Testing_Database_Cache_Cookies использовать посторонний класс Fuston_Http_Response_Cookie как свой собственный Testing_Database_Cache_Cookies_Cookie?»
… то непонятно, зачем это вообще нужно. Особенно учитывая, что зачастую это невозможно.
Название класса должно кратко описывать сущность, а не говорить об особенностях внутреннего устройства.
Прекрасно понимаю ваши слова про именование по смыслу. Сам так хочу, но пока стеной стоит вопрос автозагрузки классов и вообще организации классовой структуры по модулям в условиях отсутствия namespace'ов (т.е. единственным идентификатором класса является его имя, которое, естественно, должно быть уникальным).
Собственно, вы совершенно правы в структуре классов для столь абстрактной задачи с цветами, но в конкретной системе на PHP присутствует мешанина из префиксов классов вроде MyApplication_MyModule_MyService_SubComponent_ClassName (-_-)
1. У меня создалось впечатление, что тут наследование перепутано с делегированием. Если у разных объектов, которые не участвуют в наследственности(т.е. по-хорошему комната и листок не должны иметь общих предков), должны быть общие методы — то тут есть несколько путей развития: примеси, делегирование специального объекта с общим поведением в объекты. А, если реализация простая, то для каждого супер-класса можно написать свою реализацию.
2. Лучше классы называть понятно. Может стоит извратиться, написать небольшой скрипт, который сканирует файлы классов и создаёт массив: array('class_name'=>'class_file', ...)? Можно такое сделать только для этой части проекта и зарегистрировать для него отдельный автозагрузчик.
1. Не до конца понял вас. В моем примере наследование используется только в 3 случаях: «Лилия» extends «Цветок», «Мак» extends «Цветок», «Лилия as Красивая фигня» extends «Красивая фигня» («Красивая фигня» и «Цветок» вполне могут быть как абстрактными классами, так и интерфейсами). Все остальное — использование: внутри метода «старшего» класса просто создается и используется объект используемого класса.
class B
{
function someMethod()
{
$someVar = new C;
}
}
2. Проблема даже не в автозагрузке, а в пересечении названий классов из-за отсутствия namespace'ов.