Разработка поддерживаемой системы RPC
Я работаю над веб-приложением, которое будет широко использовать методы AJAX для взаимодействия клиент/сервер... JSON-RPC. Zend Framework используется на стороне сервера, и он предлагает хороший JSON-RPC-сервер, который я бы хотел использовать.
Моя цель - архивировать поддерживаемую систему, которая предоставляет большой набор функциональных возможностей на стороне сервера для клиентской стороны (javascript) без ненужного дублирования кода. Я видел многочисленные сообщения в блогах и руководства по использованию сервера ZF JSON-RPC (здесь здесь и здесь), но все они были направлены на то, чтобы разоблачить небольшой, общедоступный API. Дублирование кода является обычным явлением, например, одно сообщение в блоге раскрывает следующий метод:
public static function setTitle($bookId, $title) {
$book = new Nickel_Model_Book($bookId);
$book->setTitle($title);
$book->update();
return true;
}
Мне не нравится тот факт, что есть два метода setTitle
. Если сигнатура метода для одного изменяется, другая должна храниться в синхронизации... кажется кошмаром на ноу-хау, если ваш API обширен. Мне кажется, что должен быть один класс Book
с одним методом setTitle
.
Моя первоначальная мысль добавляет аннотацию docblock @export
к методам/классам, которые я хочу опубликовать. Когда я решаю разоблачить метод setTitle
, я просто добавляю аннотацию, а не новый метод.
Одна потенциальная проблема, которую я вижу, связана с сохранением объекта. На стороне сервера, для setTitle
имеет смысл установить свойство title объекта... но не сохранять его в базе данных до тех пор, пока не будет вызван update()
. Клиентская сторона, вызывающая setTitle
, должна немедленно влиять на базу данных. Одним из возможных решений является изменение всех аксессуаров, так что они берут необязательный второй параметр, означающий модификацию, немедленно обновлять базу данных:
function setTitle($title, $persist = false) {
$this->title = $title;
if ($persist) $this->update();
}
Какой-то прокси-класс может гарантировать, что флаг $persist
установлен для всех клиентских RPC-вызовов.
Другой проблемой является сериализация объектов PHP. На стороне сервера имеет смысл делать вызов $book->setTitle("foo")
OO-стиля, но клиентская сторона book.setTitle(1234, "foo")
имеет смысл (где 1234 - идентификатор книги) из-за отсутствия состояния. Моим решением для этого было бы, чтобы вышеупомянутый класс-прокси отвечал за то, что он превратил book.setTitle(1234, "foo")
в:
$book = new Book();
$book->load(1234);
return $book->setTitle($title);
Я чувствую, что эта проблема, должно быть, обсуждалась или обсуждалась раньше... но я не нахожу много ресурсов в Интернете. Это похоже на разумное решение?
Ответы
Ответ 1
То, что вы ищете, называется Уровень сервиса.
Их объекты должны быть чисто контейнерами данных (если вы не используете Active Record), вы должны показывать только свой сервисный уровень, а это, в свою очередь, обращается к их объектам и их соответствующим методам.
!["Your Book class is a Domain Model, you should now create your Service Layer"]()
Ваш класс обслуживания будет примерно таким:
class BookService extends Service {
//...
public function changeBookTitle( $title, Book $book )
{
//verify if the $title is correct and valid
$book->setTitle( $title );
$this->methodToGetThePersistenceManager()->save( $book );
//fire events, create a result object, etc...
}
}
Ответ 2
Я обдумывал ваш вопрос в течение нескольких минут. Если вы хотите попробовать, и это в PHP, вы можете сделать некоторые магические методы формы
множество {Свойство} из {Объект}.
Вы можете сделать это с помощью магического метода __call.
Таким образом, вам не нужно явно определять методы, но свойства все равно будут установлены.
Магический метод определит, какой тип объекта он должен создать, а какое свойство установить на нем, а затем сохранить.
Вам нужно будет найти способ дифференцирования между удаленными вызовами и локальными вызовами, хотя вы не будете упорствовать по ошибке, если выполняете вызовы локальной библиотеки и что вы сохраняете удаленные вызовы.
Я чувствую, что каким-то образом я неправильно понял ваш вопрос, но я попробовал.
Ответ 3
Ну,
То, что вы хотите делать на самом деле хорошо, на бумаге, и вы хотите, чтобы код поддерживался, и, в зависимости от вашей реальной системы, то, что вы просите, может даже быть несколько безопасным, однако оно приближается к тому, чтобы быть опасным и подверженным ошибкам.
Во всяком случае, вам, вероятно, придется самому сделать большую часть этого, или просто вставьте какой-то перехватчик во все вызовы методов, вот быстрый пример:
class Book {
public $title = '';
public $id = 0;
public function setTitle($string) {
$this->title = $string;
echo "title called with $string\n";
}
public function twoArgs($arg1,$arg2) {
echo "Multi-call: $arg1,$arg2\n";
}
}
class Api {
public function parse($json) {
$jsonobj = json_decode($json,true);
foreach ($jsonobj as $key=>$value) {
$class_name = $key;
$obj = new $class_name();
foreach ($value as $vkey=>$vvalue) {
if (method_exists($obj,$vkey)) {
call_user_func_array(array($obj,$vkey),$vvalue);
} else if (isset($obj->$vkey)) {
echo "Setting $vkey\n";
$obj->$vkey = $vvalue;
}
}
}
}
}
$json = json_encode(array('Book' => array('id' => 1234, 'setTitle' => array('The new title'))));
$api = new Api();
$api->parse($json);
$json = json_encode(array('Book' => array('id' => 1234, 'twoArgs' => array('arg1 :) ', 'arg2 :]'))));
$api->parse($json);
Очевидно, что вы хотите добавить логику, чтобы обрабатывать флаг persist, а также загружать или разрешать им передавать в конструкторе: [args] и обрабатывать это. и т.д. Когда речь заходит об экспонировании функций, вы раскрываете их в документации, они все доступны до тех пор, пока они являются общедоступными функциями с этой точки и т.д.