Проблема:
Требуется, чтобы несколько взаимодействующих между собой объектов могли управлять логикой определенных методов.
Например: каждый из объектов имеет метод Data(), который возвращает данные, необходимые для отображения объекта на странице (данные для шаблонизатора). Например объект $news класса News должен возвратить 5 последних новостей в приемлемом для шаблонизатора виде, например в виде массива. В свою очередь News::Data() обращается поочередно к объектам $newsPost->Data() (к вложенным объектам) для получения данных, касающихся отдельной новости (заголовок, дата и т.д.). Однако в некоторых случаях требуется, чтобы NewsPosе::Data() возвращал не полные данные, а лишь заголовок (например на главной странице сайта), а в другом случае требуется, чтобы вернулись все данные, включая ссылки на «новости по теме».
Применение нескольких методов Data() ( DataShort(), DataFull() ) решило бы проблему, но хотелось бы более элегантного решения.
Управляющий скрипт (допустим, конструктор страницы) «знает» в каком виде NewsPost::Data() должен вернуть данные, однако он не может напрямую взаимодействовать с объектами класса NewsPost, потому как это «забота» класса News. Следовательно, все, что может скрипт, это «попросить» $news «попросить» $newsPost вернуть «сокращенные» данные.
тогда внутри News::Data() будет что-то:
возможно, NewsPost::Data() обращается еще к каким-то объектам, допустим NewsPostBody::Data() для получения собственно статьи. Тогда, получив запрос «short data» на NewsPost::Data(), нужно сделать запрос с параметром «body short» к методу NewsPostBody::Data(). Не обязательно «управляющему скрипту» знать об этом, однако нельзя блокировать возможность напрямую «дать указание» объекту класса NewsPostBody вернуть «короткий вариант» статьи. То есть что-то вроде:
Однако, возможна ситуация, когда нужно обращаться к методу NewsPostBody::Data() с разными параметрами, причем за один запрос. Например когда News содержит «новости сайта», где NewsPostBody::Data() должен всегда возвращать «необрезанную» версию. То есть что-то вроде:
или:
но тогда News::Data() должен сделать примерно следующее:
или еще сложнее, когда для SiteNewsBody::Data() передаются другие параметры, например 'return SEO friendly body'.
Вырисовываются некоторые технические требования:
Context::Class::Command
Class, принципе, тоже в нектором свмсле «контекст», так что можно использовать «вложение контекстов»:
ContextInContextInContex::Command
Также следует подумать о расширенном использовании управляющей составляющей Command и предусмотреть возможность отделения аргумента от собственно «команды», напрмер «body_short=250», что может означать «ограничить статью до 250 символов». Имеем:
ContextInContext::Command[=Argument(s)];
Попробуем набросать интерфейс «в живую»:
Почти все готово для начала разработки класса. Подведем итог и выделим основные моменты в будущем классе:
Можно приступать к реализации.
PS. топик был когда-то опубликован мною. Использую это паттэрн с тех пор, не задумываясь, новый ли это «велосипед» или нет. Возможно кому-то будет полезен. Буду рад обсудить кончно.
Требуется, чтобы несколько взаимодействующих между собой объектов могли управлять логикой определенных методов.
Например: каждый из объектов имеет метод Data(), который возвращает данные, необходимые для отображения объекта на странице (данные для шаблонизатора). Например объект $news класса News должен возвратить 5 последних новостей в приемлемом для шаблонизатора виде, например в виде массива. В свою очередь News::Data() обращается поочередно к объектам $newsPost->Data() (к вложенным объектам) для получения данных, касающихся отдельной новости (заголовок, дата и т.д.). Однако в некоторых случаях требуется, чтобы NewsPosе::Data() возвращал не полные данные, а лишь заголовок (например на главной странице сайта), а в другом случае требуется, чтобы вернулись все данные, включая ссылки на «новости по теме».
Применение нескольких методов Data() ( DataShort(), DataFull() ) решило бы проблему, но хотелось бы более элегантного решения.
Управляющий скрипт (допустим, конструктор страницы) «знает» в каком виде NewsPost::Data() должен вернуть данные, однако он не может напрямую взаимодействовать с объектами класса NewsPost, потому как это «забота» класса News. Следовательно, все, что может скрипт, это «попросить» $news «попросить» $newsPost вернуть «сокращенные» данные.
Copy Source | Copy HTML
- $data = $news->Data('ask newsPost: return short data');
тогда внутри News::Data() будет что-то:
Copy Source | Copy HTML
- public function Data($askWhat)
- {
- ...
- $dataNews = $this->newsPost->Data('return short data');
- ...
- }
возможно, NewsPost::Data() обращается еще к каким-то объектам, допустим NewsPostBody::Data() для получения собственно статьи. Тогда, получив запрос «short data» на NewsPost::Data(), нужно сделать запрос с параметром «body short» к методу NewsPostBody::Data(). Не обязательно «управляющему скрипту» знать об этом, однако нельзя блокировать возможность напрямую «дать указание» объекту класса NewsPostBody вернуть «короткий вариант» статьи. То есть что-то вроде:
Copy Source | Copy HTML
- $data = $news->Data('ask NewsPostBody: return short article');
Однако, возможна ситуация, когда нужно обращаться к методу NewsPostBody::Data() с разными параметрами, причем за один запрос. Например когда News содержит «новости сайта», где NewsPostBody::Data() должен всегда возвращать «необрезанную» версию. То есть что-то вроде:
Copy Source | Copy HTML
- $data = $news->Data('ask NewsPostBody(main news only): return short article');
или:
Copy Source | Copy HTML
- $data = $news->Data('return main news short body');
но тогда News::Data() должен сделать примерно следующее:
или еще сложнее, когда для SiteNewsBody::Data() передаются другие параметры, например 'return SEO friendly body'.
Вырисовываются некоторые технические требования:
- управляющая составляющая (команда)
- адресат (кому предназначается управляющая команда)
- указание лишь класса объекта недопустимо, дополнительно требуется указать «адресата» на уровне «бизнес логики», или «контекст», так как возможно несколько значений одного и того же параметра для одного и того же класса (возможно даже объекта) в различных контекстах.
Context::Class::Command
Class, принципе, тоже в нектором свмсле «контекст», так что можно использовать «вложение контекстов»:
ContextInContextInContex::Command
Также следует подумать о расширенном использовании управляющей составляющей Command и предусмотреть возможность отделения аргумента от собственно «команды», напрмер «body_short=250», что может означать «ограничить статью до 250 символов». Имеем:
ContextInContext::Command[=Argument(s)];
Copy Source | Copy HTML
- $data = $news->Data(new Parameters('NewsBodyInMainNews:short=250;NewsBodyInSiteNews:seo_frendly;short=500;NewsBody:keepHtml'));
Попробуем набросать интерфейс «в живую»:
Copy Source | Copy HTML
- function News::Data($params)
- {
- $newsData = new NewsData();
-
- // добавляем необходимые данные с параметрами в контексте 'SiteNews'
- // должен использоваться параметр short=500 и seo_freindly и keepHtml
-
- $params->useContext('SiteNews');
- $newsData->add( $this->getSiteNewsData($params) );
- $params->dontUseContext('SiteNews');
-
- // добавляем необходимые данные с параметрами в контексте 'MainNews'
- // должны использоваться только параметры short=250 и keepHtml
-
- $params->useContext('MainNews');
-
- $newsData->add( $this->getMainNewsData($params) );
-
- $params->dontUseContext('MainNews');
-
- return $newsData;
-
- }
-
- ...
-
- function NewsBody::Data($params)
- {
- $newsBodyData = new NewsBodyData();
-
- $params->useContext('NewsBody');
-
- $bodyLimit = $params->get('short');
-
- $newsBodyData->add('body', $this->getBody($bodyLimit));
-
- $params->dontUseContext('NewsBody');
-
- ...
-
- return $newsBodyData;
- }
-
-
Почти все готово для начала разработки класса. Подведем итог и выделим основные моменты в будущем классе:
- Значение параметра можно задать в определенном «контексте», причем контекст может быть «вложенным» (NewsBodyInMainNewsInPage не то же самое что NewsBodyInSiteNewsInPage);
- метод get() должен возвращать значение параметра в текущем контексте, заданным в useContext();
- возможность «объединения» параметров (двух объектов класса Parameters с возможностью управления перегрузкой значений для одинаковых параметров)
- возможность задавать параметры строкой, вида 'ContextInContext:param=value;param2' или вызывая метод set($parameter, $value, $context)
Можно приступать к реализации.
PS. топик был когда-то опубликован мною. Использую это паттэрн с тех пор, не задумываясь, новый ли это «велосипед» или нет. Возможно кому-то будет полезен. Буду рад обсудить кончно.