2011-01-08 5 views
9

Mam aplikację, która jest zasadniczo opakowaniem dla interfejsu API innej firmy. Aplikacja nie używa bazy danych i przechowuje tylko jeden plik cookie, który jest identyfikatorem sesji wymaganym przez interfejs API.PHP OOP :: Budowanie klasy wrapper API

API jest to system, który pozwala użytkownikom na zakupy do

-login/Rejestracja/edytuj profil/wylogowania

-buy towar

-make darowizny

-become członkiem

Interfejs API ma około 50 metod, z którymi moja aplikacja musi się połączyć. Przykładowe wywołania API to addItemToBasket(), addDonation(), GetUserDetails() itd.

Próbuję ustalić, co powinno być zajęciami w mojej aplikacji. Oto co mam do tej pory:

Klasy

1) APIManager() klasy zawiera metody, które pasują jeden do jednego z metodami narażonych na 3rd API i zapewnia mechanizm nawiązywania połączenia ze zdalnym serwerem API. Tak więc użytkownik będzie zalogowany poprzez

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

a pilot API by ustawić użytkownika jako zalogowany razie potrzeby być, moja aplikacja może sprawdzić zalogowany stanu dowolnego klucza sesji przez wywołanie funkcji API.

APIManager->isLoggedIn($sessionKey); 

2) użytkownika() Klasa Dotyczy to metody, które zawierają logikę biznesową wymaganą przed rozpoczęciem przetwarzania wywołań API, takich jak lub zarejestruj. Metoda przykładem jest:

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); 
} 

Zdaję sobie sprawę, że mogę chyba tylko zatelefonować do APIManager ze strony logowania, zamiast klasę użytkownika per se, ale czułem, że ponieważ niektóre logiki biznesowej musi uruchomić przed tak naprawdę nazywamy metodę loginUser() API, to było właściwe, aby było obsługiwane w klasie użytkownika. Chciałbym wiedzieć, co ludzie o tym myślą.

3) Basket() klasy

Kosz jest zarządzany w API 3rd party, więc moja aplikacja rola jest, aby odpowiednie wywołań API do dodawania nowych elementów do kosza, usunąć elementy, wyświetlić koszyk itp. Moja aplikacja nic nie wie o koszyku, dopóki dane nie zostaną pobrane z interfejsu API, ani nie może wprowadzać żadnych zmian w koszyku bez przechodzenia przez interfejs API. Ponownie, należałoby pogrupować tę powiązaną logikę w klasę koszyka. Koniec strona przednia może wywołać coś takiego:

Basket->addItem(23); 

i tym sposobem addItem() w klasie Basket będzie wyglądał:

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

gdzie addToCart() to trzecia metoda partia API my zadzwoń, aby przetworzyć przedmiot.

4) Darowizna() klasy

To pozwala użytkownikom na dokonanie wpłaty. Darowizna pojawia się w koszyku i może zostać usunięta z koszyka. Myślałem, że po prostu dodanie metody addDonate() do klasy kosza i nie martwić się o obiekt Darowizna w ogóle, ale ... (patrz niżej)

5) członków() Klasa

. .. członkostwa są w rzeczywistości typem darowizny! API będzie traktować darowiznę na określone konto jako roczne członkostwo, a nie zwykłą darowiznę. Nie możemy zmienić logiki/zachowania API strony trzeciej.

Więc, jeśli oddam 50 dolarów na konto "1", to zwykła darowizna, ale jeśli przekażę 100 dolarów na konto "8", zostanę członkiem z wszystkimi korzyściami dla członków (obniżone ceny, brak opłaty pocztowej itp.).

Oto, gdzie nie jestem pewien najlepszego sposobu zaprojektowania tego.

Czy powinienem utworzyć klasę darowizny, a następnie rozszerzyć ją o Członkostwo, ponieważ wszystkie metody darowizn będą wymagane przez Członkostwo. Ale członkostwo będzie wymagać dodatkowych metod, takich jak odnowienie() lub getExpiry() itp.

Ponadto, czy powinienem patrzeć na rozszerzenie użytkownika, aby stać się członkiem? Znowu członek ma wszystkie podstawowe metody, które posiada użytkownik, ale ma też dodatkowe, takie jak getSpecialOffers() lub getDiscountCode(), do których dostęp mieliby tylko członkowie.

Wszelkie wskazówki dotyczące najlepszego podejścia do projektu będą bardzo mile widziane.

Dzięki, James

+3

nie odpowiedź, której szukasz, ale co mi pomaga przy projektowaniu coś w zorientowane obiektowo projektu jest zwrócenie się schemat obiektów, wypracowanie, gdzie jest wspólność itd. Jeśli zrobisz to przed zanurkowaniem i kodowaniem, okaże się, że masz o wiele łatwiejszy czas. A skończysz z czymś dłuższym maintabke na dłuższą metę. – diagonalbatman

Odpowiedz

18

Osobiście byłoby zbudować to w 3 warstwach.

Warstwa 1: Interfejs API

Warstwa ta jest gdzie rzeczywista linia szczebla wzywa do zdalnego API odbyć. Ta warstwa dotyczy protokołu. W tej warstwie nie powinno być nic, co jest specyficzne dla API. Wszystko powinno być w 100% ogólne, ale powinno być możliwe użycie środkowej warstwy do interakcji z interfejsem API. Zauważ, że ta warstwa może pochodzić z biblioteki lub z innego źródła, takiego jak framework. Lub możesz napisać niestandardowo. Wszystko zależy od tego, gdzie jesteś i Twoich konkretnych potrzeb.

niektórych klas, które mogą należeć tutaj:

  • XMLRPC_Client
  • SOAP_Client
  • REST_Client

Layer 2: API Adapter

Warstwa ta rzeczywiście ma informacji API zakodowane na stałe. Jest to w zasadzie Adapter Pattern. Zasadniczo zadaniem tej warstwy jest konwersja zdalnego interfejsu API na lokalny interfejs API przy użyciu warstwy interfejsu. Tak więc, w zależności od potrzeb, możesz odzwierciedlić zdalny interfejs API w dworku 1: 1 lub możesz go nieco bardziej dostosować do swoich potrzeb. Należy jednak pamiętać, że ta klasa nie polega na zapewnieniu funkcjonalności twojego programu.Celem jest odłączenie zdalnego interfejsu API od lokalnego kodu. Zamieniając tę ​​klasę, Twój kod powinien być w stanie szybko dostosować się do używania różnych wersji zdalnego interfejsu API i ewentualnie różnych zdalnych interfejsów API razem.

Należy pamiętać, że ta warstwa adaptera ma obejmować interfejs API. Zatem zakres każdej indywidualnej klasy jest całkowitą implementacją API. Powinno więc istnieć mapowanie 1: 1 między adapterami i zdalnymi interfejsami API.

niektórych klas, które mogą być tutaj:

  • RemoteAPI_v1_5
  • RemoteAPI2_v1

Layer 3: Przedmioty wewnętrzne

Warstwa ta powinna być twoja wewnętrzna reprezentacja różnych obiektów (W twoim konkretnym przypadku: Użytkownik, Kosz, Koszyk, Darowizna, Członkostwo, itp.). Nie powinni bezpośrednio wywoływać interfejsu API, ale używać funkcji Composition (Dependency Injection), aby uzyskać interfejs API. Zachowując oddzielenie, powinieneś być w stanie zmieniać API całkowicie niezależnie od klas wewnętrznych (i vice versa).

Tak, jeden z twoich zajęciach może wyglądać następująco:

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

W ten sposób, nie ma rzeczywistej potrzeby dla API kierownika tak mówić. Po prostu utwórz nową instancję API na początku programu i przekaż ją do reszty kodu. Ale ma to dużą zaletę, że jest dość elastyczny w tym sensie, że powinieneś być w stanie zmieniać interfejsy API (zarówno wersję, jak i samo wywołanie), po prostu zamieniając warstwę adaptera w swoim kodzie (podczas tworzenia instancji adaptera). Wszystko inne powinno po prostu działać, i powinieneś być całkowicie odizolowany od zmian w kodzie lub zdalnym interfejsie API (nie wspominając, że powinien on być całkiem testowalny, jeśli jest zbudowany w ten sposób) ...

To jest 0,02 USD. To może być przesada, ale tak naprawdę w zależności od dokładnego potrzebie ...

+0

Dobre sugestie i objaśnienia. Dependency Injection jest kluczem w Composition over Heritance. – crush

+0

@ircmaxwell dobry post. Mam zamiar zbudować wrapper API zgodnie z twoją radą. Czy możesz utworzyć link do przykładowego wrappera API zbudowanego na tym modelu, który mógłbym obejrzeć? – bernie2436

+1

Nieakceptowanie tej odpowiedzi jest przestępstwem .. to było tylko 3 i pół roku .. – FloatingRock

0

powiedziałbym:

  1. utworzyć klasę datek ze wszystkim, że musi
  2. utworzyć zmienną składową członkostwa (który powinien być typu Darowizna).
  3. można mieć metodę w klasie członkostwa takiego:

    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 
    } 
    

ten sposób masz piękny oddzielenia elementów. Również darowizna powinna implementować iterface tak, że jeśli zachowanie zostanie później zmienione, powinno nadal zawierać metody wymagane przez klasę memeber.

W ten sposób jest bardziej elastyczny niż dziedziczenie. Poprosiłem podobne pytanie jakiś czas temu i dostałem dobrą odpowiedź:

Elegant alternatives to the weird multiple inheritance

+1

Aby było jeszcze lepiej, można użyć zastrzyku zależności zamiast tworzyć instancję klasy w metodzie. Więc 'makeDonation (darowizna $ darowizny) {$ this-> darowizna = $ darowizna; ... '. Jest jeszcze lepiej. Ale zgadzam się, zawsze faworyzuję kompozycję ponad dziedziczenie ... – ircmaxell