2011-01-08 12 views
9

Ich habe eine App, die im Wesentlichen ein Wrapper für eine API von Drittanbietern ist. Die App verwendet keine Datenbank und speichert nur ein einzelnes Cookie, das die von der API benötigte Sitzungs-ID ist.PHP OOP :: Erstellen einer API-Wrapper-Klasse

Die API ist ein Einkaufssystem, das Benutzern

-login ermöglicht/Registrieren/Profil bearbeiten/Logout

-Buy Waren

-Stellen einer Spende

-become Mitglied

Die API verfügt über 50 Methoden, zu denen meine App eine Verbindung herstellen muss. Beispiel API Aufrufe sind addItemToBasket(), addDonation(), GetUserDetails() etc.

Ich versuche herauszufinden, was Klassen in meiner Anwendung sein sollte. Hier ist, was ich bisher:

Klassen

1) APIManager() der Klasse Enthält die Methoden, die in der 3rd-Party-API und bietet ausgesetzt eins-zu-eins mit den Methoden entsprechen den Mechanismus, um eine Verbindung zum Remote-API-Server herzustellen. So würde ein Benutzer über

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

angemeldet werden und die Remote-API, um den Benutzer festgelegt würde, wie angemeldet Wenn es sein muss, meine Anwendung der in Status jeder Sitzungsschlüssel durch den Aufruf der API protokolliert überprüfen.

APIManager->isLoggedIn($sessionKey); 

2) Benutzer() der Klasse Dies gilt Methoden, die vor der Verarbeitung API-Aufrufe wie Register oder Anmeldung erforderlich Geschäftslogik enthalten. Ein beispielhaftes Verfahren ist:

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

Ich weiß, dass ich wahrscheinlich einen Anruf zu APIManager von der Anmeldeseite machen nur, anstatt per se eine Benutzerklasse zu haben, aber ich fühlte, dass da einige Geschäftslogik ausgeführt werden muss, bevor Wir rufen tatsächlich die loginUser() - Methode der API auf, es fühlte sich richtig an, dass dies in einer Benutzerklasse behandelt wurde. Ich würde gerne wissen, was die Leute darüber denken.

3) Basket() der Klasse

Der Korb in der 3rd Party-API verwaltet wird, so Rolle meine App ist auf die entsprechende API-Anrufe neue Objekte in den Warenkorb hinzufügen, Elemente entfernen, sehen die Warenkorb usw. Meine App weiß nichts über den Warenkorb, bis die Daten aus der API abgerufen werden, noch kann er Änderungen am Warenkorb vornehmen, ohne über die API zu gehen. Wiederum schien es angemessen, diese verwandte Logik in eine Basket-Klasse zu gruppieren. Die Front-End-Web-Seite so etwas wie nennen könnte:

Basket->addItem(23); 

und diese addItem() -Methode in der Basket-Klasse würde in etwa so aussieht:

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

wo addToCart() ist die dritte Partei API Methode, die wir Rufen Sie an, um den Artikel zu bearbeiten.

4) Donation() der Klasse

Dies ermöglicht es Benutzern, eine Spende zu machen. Die Spende erscheint im Warenkorb und kann aus dem Warenkorb entfernt werden. Ich dachte, nur ein addDonate() -Methode zum Korb Klasse hinzufügen und sich keine Sorgen über überhaupt eine Spende zum Gegenstand haben, aber ... (siehe unten)

5) Mitgliedschaft() der Klasse

. .. Mitgliedschaften sind eigentlich eine Art von Spende! Die API behandelt die Spende, die auf ein bestimmtes Konto geht, als eine einjährige Mitgliedschaft und keine einfache Spende. Wir können die Logik/das Verhalten der API der dritten Partei nicht ändern.

Also, wenn ich $ 50 auf Konto '1' spende, dann ist es nur eine normale Spende, aber wenn ich $ 100 auf Konto '8' spende, werde ich Mitglied mit allen Mitgliedsvorteilen (reduzierte Preise, keine Portogebühr usw).

Hier bin ich mir nicht sicher über den besten Weg, dies zu entwerfen.

Sollte ich eine Spendenklasse erstellen und diese dann mit der Mitgliedschaft erweitern, da alle Spendenmethoden von der Mitgliedschaft benötigt werden. Aber die Mitgliedschaft wird zusätzliche Methoden wie renew() oder getExpiry() usw. benötigen.

Auch, sollte ich auf die Erweiterung des Benutzers Mitglied werden? Wiederum hat ein Mitglied alle Kernmethoden, die der Benutzer hat, hat aber auch zusätzliche wie getSpecialOffers() oder getDiscountCode(), auf die nur Mitglieder zugreifen würden.

Jede Anleitung, wie man sich dem Design am besten annähert, würde sehr geschätzt werden.

Danke, James

+3

nicht die Antwort, die Sie suchen, aber was mir hilft, wenn sie in einem objektorientierten Design etwas entwerfen, ist ein Diagramm zu ziehen von den Objekten, herauszufinden, wo es Gemeinsamkeiten gibt. Wenn du dies tust, bevor du einsteigst und Code schreibst, wirst du feststellen, dass es dir viel leichter fällt. Und Sie werden mit etwas mehr auf lange Sicht pflegen. – diagonalbatman

Antwort

18

Persönlich würde ich dies in 3 Schichten bauen.

Layer 1: API-Schnittstelle

Diese Schicht ist, wo der eigentliche Line-Pegel an die Remote-API statt aufruft. Auf dieser Ebene dreht sich alles um das Protokoll. In diesem Layer sollte nichts enthalten sein, das API-spezifisch ist. Alles sollte zu 100% generisch sein, sollte aber von der mittleren Ebene zur Interaktion mit der API verwendet werden können. Beachten Sie, dass diese Ebene aus einer Bibliothek oder einer anderen Quelle wie einem Framework stammen kann. Oder Sie könnten es benutzerdefiniert schreiben. Alles hängt davon ab, wo Sie sind und Ihre genauen Bedürfnisse.

Einige Klassen, die hier gehören könnten:

  • XMLRPC_Client
  • SOAP_Client
  • REST_Client

Layer 2: API Adapter

Diese Schicht hat tatsächlich die API Informationen fest in es codiert. Dies ist im Grunde die Adapter Pattern. Die Aufgabe dieser Ebene besteht im Wesentlichen darin, die Remote-API mithilfe der Interface-Ebene in eine lokale API zu konvertieren. Je nach Bedarf können Sie die Remote-API in einem 1: 1-System spiegeln, oder Sie können dies Ihren Bedürfnissen ein wenig anpassen. Beachten Sie jedoch, dass es bei dieser Klasse nicht darum geht, Ihrem Programm Funktionalität zur Verfügung zu stellen.Der Zweck besteht darin, die Remote-API von Ihrem lokalen Code zu entkoppeln. Durch den Austausch dieser Klasse sollte Ihr Code in der Lage sein, sich schnell anzupassen, um verschiedene Versionen der Remote-API und möglicherweise sogar verschiedene Remote-APIs zu verwenden.

Eine wichtige Sache zu erinnern ist, dass diese Adapterschicht die API umfassen soll. Daher ist der Umfang jeder einzelnen Klasse die Gesamtheit einer API-Implementierung. Daher sollte eine 1: 1-Zuordnung zwischen Adaptern und Remote-APIs bestehen.

Einige Klassen, die hier sein könnten:

  • RemoteAPI_v1_5
  • RemoteAPI2_v1

Schicht 3: Diese Schicht sollte Interne Objekte

Ihre interne Darstellung der verschiedenen Objekte sein (In Ihrem speziellen Fall: Benutzer, Warenkorb, Warenkorb, Spende, Mitgliedschaft, usw.). Sie sollten nicht direkt die API aufrufen, sondern Composition (Dependency Injection) verwenden, um im Grunde ein bridge für die API zu werden. Indem Sie es getrennt halten, sollten Sie in der Lage sein, die API völlig unabhängig von den internen Klassen zu variieren (und umgekehrt).

also eine Ihrer Klassen könnte wie folgt aussehen:

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

Auf diese Weise gibt es keine wirkliche Notwendigkeit für eine API Manager so zu sprechen. Erstellen Sie einfach eine neue Instanz der API am Anfang des Programms und geben Sie sie an den Rest Ihres Codes weiter. Aber es hat den großen Vorteil, dass es ziemlich flexibel ist in dem Sinne, dass Sie in der Lage sein sollten, APIs (entweder die Version oder den Aufruf selbst) durch einfaches Austauschen der Adapterschicht in Ihrem Code zu ändern (wenn Sie den Adapter instanziieren). Alles andere sollte einfach funktionieren, und Sie sollten vollständig von Änderungen an Ihrem Code oder der Remote-API isoliert sein (ganz zu schweigen davon, dass es auf diese Weise ziemlich überprüfbar sein sollte) ...

Das sind meine $ 0,02. Es könnte zu viel des Guten, aber das ist wirklich abhängig von Ihrem genauen Bedarf ...

+0

Gute Vorschläge und Erklärungen. Dependency Injection ist der Schlüssel in der Zusammensetzung gegenüber der Vererbung. – crush

+0

@ircmaxwell guter Beitrag. Ich bin dabei, einen API-Wrapper zu erstellen, der Ihrem Rat folgt. Können Sie auf ein Beispiel eines API-Wrappers verweisen, der auf diesem Modell aufbaut? – bernie2436

+1

Diese Antwort nicht zu akzeptieren ist ein Verbrechen .. es ist erst 3 und ein halbes Jahr .. – FloatingRock

0

würde ich sagen:

  1. eine Spende Klasse mit allen schaffen, die es braucht
  2. eine Membervariable der Mitgliedschaft erstellen (welches sollte vom Typ Donation sein).
  3. können Sie eine Methode in der Klasse Mitgliedschaft haben so:

    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 
    } 
    

diese Weise können Sie schöne Entkopplung zwischen den Einzelteilen haben. Auch die Donation sollte ein Iterface implementieren, so dass, wenn das Verhalten später geändert wird, es immer noch die Methoden enthalten sollte, die von der Member Klasse benötigt werden.

So ist es flexibler als Vererbung. Ich hatte vor eine ähnliche Frage einiger Zeit gefragt und bekam eine gute Antwort:

Elegant alternatives to the weird multiple inheritance

+1

Um es noch besser zu machen, könnten Sie die Abhängigkeitsinjektion verwenden, anstatt die Klasse innerhalb der Methode zu instanziieren. Also 'makeDonation (Donation $ donation) {$ this-> spende = $ spende; ... '. Es ist noch besser. Aber ich stimme zu, immer die Komposition über die Vererbung zu bevorzugen ... – ircmaxell