PHP OOP: создание класса-оболочки API

у меня есть приложение, которое по существу является оболочкой для стороннего API. Приложение не использует базу данных и хранит только один файл cookie, который является идентификатором сеанса, который требуется API.

API-это торговая система, которая позволяет пользователям

- вход / регистрация/Редактирование профиля / выход

-купить товар

-сделать пожертвование

-стать

API имеет 50 или около того методов, к которым мое приложение должно подключиться. Образец Вызовы API: addItemToBasket(), addDonation (), GetUserDetails () и т.д.

Я пытаюсь выяснить, какие классы должны быть в моем приложении. Вот что у меня есть до сих пор:

классы

1) Класс APIManager () Содержит методы, которые соответствуют один к одному с методами, представленными в стороннем API и предоставляет механизм для подключения к удаленному серверу API. Таким образом, пользователь будет войти в систему через

APIManager->loginUser($sessionKey, $uid, $pwd);

и удаленный API установит пользователя как вошедшего в систему. Если необходимо, мое приложение может проверить состояние входа в любой ключ сеанса, вызвав API:

 APIManager->isLoggedIn($sessionKey);

2) Пользователь() Класс Это содержит методы, которые содержат бизнес-логику, необходимую для обработки вызовов API, таких как Register или Login. Пример метода:

function login($_POST) {
    //perform sanity checks, apply business rules etc.
    //if certain conditions are met, we may pass in a promo code, $pc

    APIManager->loginUser($sessionkey, $_POST['uid'], $_POST['pwd'], $pc);
}

Я понимаю, что я мог бы просто позвонить в APIManager со страницы входа в систему, а не иметь Класс пользователя как таковой, но я чувствовал, что, поскольку некоторая бизнес-логика должна выполняться, прежде чем мы фактически вызовем метод loginUser() API, было бы правильно, чтобы это обрабатывалось в классе пользователя. Мне бы хотелось знать, что люди думают об этом.

3) Корзина() Класс

корзина управляется в стороннем API, поэтому роль моего приложения состоит в том, чтобы сделать соответствующие вызовы API для добавления новых элементов в корзину, удаления элементов, просмотра корзины и т. д. Мое приложение ничего не знает о корзина до тех пор, пока данные не будут извлечены из API, и не может вносить какие-либо изменения в корзину без перехода через API. Опять же, было уместно сгруппировать эту связанную логику в класс корзины. Передняя веб-страница может вызвать что-то вроде:

Basket->addItem(23);

и этот метод addItem() в классе корзина будет выглядеть примерно так:

addItem($itemID) {
   //perform checks, apply business rules e.g. if user is eligible for discount
        APIManager->addToCart($itemID, $discount);
}

где addToCart () - это сторонний метод API, который мы вызываем для обработки элемента.

4) пожертвования() Класс!--23-->

Это позволяет пользователям делать пожертвования. Пожертвование появляется в корзине и может быть удалено из корзины. Я подумал о том, чтобы просто добавить метод addDonate() в класс корзины и не беспокоиться о том, что объект пожертвования вообще, но... (см. ниже)

5) Членство() Класс

... членство на самом деле является видом пожертвования! API будет лечить пожертвование идти в определенный счет как 1 год членства, а не простое пожертвование. Мы не можем изменить логику / поведение стороннего API.

Итак, если я пожертвую $50 на счет "1", то это просто обычное пожертвование, но если я пожертвую $100 на счет "8", то я стану участником со всеми преимуществами участника (сниженные цены, без почтовой оплаты и т. д.).

вот где я не уверен в лучшем способе проектирования этого.

должен ли я создать класс пожертвования, а затем расширить его членство, так как все методы пожертвования будут требуется членством. Но для членства потребуются дополнительные методы, такие как renew() или getExpiry() и т. д.

кроме того, должен ли я смотреть на расширение пользователя, чтобы стать членом? Опять же, у члена есть все основные методы, которые есть у пользователя, но также есть дополнительные, такие как getSpecialOffers() или getDiscountCode (), которые будут доступны только членам.

любое руководство о том, как лучше всего подойти к дизайну, было бы очень оценено.

Спасибо, Джеймс

2 ответов


лично я бы построил это в 3 слоях.

слой 1: интерфейс API

этот слой-это место, где происходят фактические вызовы уровня линии к удаленному API. Этот уровень - все о протоколе. В этом слое не должно быть ничего специфичного для API. Все должно быть на 100% общим, но должно быть в состоянии использоваться средним слоем для взаимодействия с API. Обратите внимание, что этот слой может поступать из библиотеки или другого источника, например из фреймворка. Или вы можете написать его на заказ. Все зависит от того, где вы находитесь и ваши точные потребности.

некоторые классы, которые могут принадлежать здесь:

  • XMLRPC_Client
  • SOAP_Client
  • REST_Client

слой 2: API-интерфейс адаптер

этот слой фактически имеет информацию API, жестко закодированную в него. Это в основном Шаблон Адаптер. В основном задача этого слоя заключается в преобразовании удаленный API в API с использованием уровня интерфейса. Таким образом, в зависимости от ваших потребностей, вы можете отразить удаленный API в поместье 1:1, или вы можете согнуть это для ваших нужд немного больше. Но следует иметь в виду, что этот класс не предназначен для предоставления функциональности вашей программе. Цель-отвязать удаленный API из вашего местного кодекса. Заменяя этот класс, ваш код должен иметь возможность быстро адаптироваться к использованию разных версий удаленного API и, возможно, даже разных удаленные API все вместе.

важно помнить, что этот слой адаптера предназначен для охвата API. Таким образом, область каждого отдельного класса-это вся реализация API. Таким образом, должно быть сопоставление 1:1 между адаптерами и удаленными API.

некоторые классы, которые могут быть здесь:

  • RemoteAPI_v1_5
  • RemoteAPI2_v1

слой 3: Внутренние Объекты

этот слой должен быть вашим внутренним представлением различных объектов (в вашем конкретном случае: пользователь, корзина, корзина, пожертвование, членство и т. д.). Они не должны напрямую вызывать API, но использовать композицию (инъекцию зависимостей), чтобы стать тем, что в основном мост к API. Сохраняя его разделенным, вы должны иметь возможность изменять API полностью независимо от внутренних классов (и наоборот).

Итак, один из ваших классов может выглядеть это:

class User {
    protected $api;
    public function __construct(iAPIAdapter $api) {
        $this->api = $api;
    }
    public function login() {
        $this->api->loginUser($blah);
    }
}

таким образом, нет реальной необходимости в API менеджер так сказать. Просто создайте новый экземпляр API в начале программы и передайте его остальной части кода. Но у этого есть главное преимущество-быть довольно гибким в том смысле, что вы должны иметь возможность изменять API (либо версию, либо сам вызов), просто заменяя слой адаптера в своем коде (когда вы создаете экземпляр адаптера). Все остальное должно просто работать, и вы должны быть полностью изолированы от изменений в вашем коде или удаленном API (не говоря уже о том, что он должен быть вполне тестируемым, если построен таким образом)...

вот мои $0.02. Это может быть излишним, но это действительно зависит от вашей точной потребности...


Я говорю:

  1. создайте класс пожертвований со всем, что ему нужно
  2. создайте переменную члена членства (которая должна иметь тип пожертвования).
  3. вы можете иметь метод в классе состав такой:

    public function makeDonation($data) {
        $this->donation = new Donation($data) // maybe with the data parameter;
        $this->donation->commit() // whatever it does to set things up
          ......
          whatever you need
    }
    

таким образом, у вас есть хорошая развязка между элементами. Кроме того, пожертвование должно реализовать iterface так, что если поведение позже будет изменено, оно все равно должно содержать методы, требуемые класс memeber.

способ Этот более гибкий, чем наследование. Я задал подобный вопрос некоторое время назад и получил хороший ответ:

элегантные альтернативы странному множественному наследованию