PHP OOP :: 构建 API Wrapper 类
Posted
技术标签:
【中文标题】PHP OOP :: 构建 API Wrapper 类【英文标题】:PHP OOP :: Building an API Wrapper class 【发布时间】:2011-06-05 19:06:21 【问题描述】:我有一个应用程序,它本质上是 3rd 方 API 的包装器。该应用不使用数据库,仅存储一个 cookie,即 API 所需的会话 ID。
API 是一个购物系统,允许用户
-登录/注册/编辑个人资料/注销
-购买商品
-捐款
-成为会员
API 有大约 50 个方法,我的应用需要连接到这些方法。示例 API 调用为 addItemToBasket()、addDonation()、GetUserDetails() 等。
我正在尝试确定我的应用程序中应该包含哪些类。这是我目前所拥有的:
类
1) APIManager() 类 包含与第 3 方 API 中公开的方法一一匹配的方法,并提供与远程 API 服务器建立连接的机制。所以用户将通过
登录APIManager->loginUser($sessionKey, $uid, $pwd);
远程 API 会将用户设置为已登录。如果需要,我的应用可以通过调用 API 检查任何会话密钥的登录状态:
APIManager->isLoggedIn($sessionKey);
2) User() 类 这包含包含处理 API 调用(如注册或登录)之前所需的业务逻辑的方法。一个示例方法是:
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,而不是拥有 User 类本身,但我觉得因为在我们实际调用 API 的 loginUser() 方法之前需要运行一些业务逻辑,感觉在 User 类中处理它是正确的。我很想知道人们对此有何看法。
3) Basket() 类
购物篮在 3rd Party API 中进行管理,因此我的应用程序的角色是进行适当的 API 调用,以将新商品添加到购物篮、删除商品、查看购物篮等。我的应用程序在获取数据之前对购物篮一无所知从 API 中检索,也不能在不通过 API 的情况下对篮子进行任何更改。同样,将这个相关的逻辑分组到一个 Basket 类中是合适的。前端网页可能会这样调用:
Basket->addItem(23);
Basket 类中的这个 addItem() 方法看起来像:
addItem($itemID)
//perform checks, apply business rules e.g. if user is eligible for discount
APIManager->addToCart($itemID, $discount);
其中 addToCart() 是我们调用来处理商品的第三方 API 方法。
4) Donation() 类
这允许用户进行捐赠。捐赠出现在篮子中,可以从篮子中取出。我想只是在 Basket 类中添加一个 addDonate() 方法,根本不用担心有一个 Donation 对象,但是......(见下文)
5) Membership() 类
...会员资格实际上是一种捐赠! API 会将进入某个帐户的捐款视为 1 年会员资格,而不是普通捐款。我们无法更改 3rd 方 API 的逻辑/行为。
所以,如果我向账户“1”捐赠 50 美元,那么这只是一次普通捐赠,但如果我向账户“8”捐赠 100 美元,那么我将成为会员,享受所有会员福利(降价、免邮费等) .
这就是我不确定最佳设计方法的地方。
我是否应该创建一个 Donation 类,然后使用 Membership 扩展它,因为 Membership 需要所有的捐赠方法。但是 Membership 需要额外的方法,例如 renew() 或 getExpiry() 等。
另外,我应该考虑将用户扩展为会员吗?同样,成员拥有 User 拥有的所有核心方法,但也有其他方法,例如只有成员才能访问的 getSpecialOffers() 或 getDiscountCode()。
非常感谢任何关于如何最好地处理设计的指导。
谢谢,詹姆斯
【问题讨论】:
不是您正在寻找的答案,但是在面向对象设计中设计任何东西时对我有帮助的是绘制对象图,找出共性等。如果你这样做在你潜入并编写代码之前,你会发现你的时间要容易得多。从长远来看,您最终会得到一些更易于维护的东西。 【参考方案1】:就我个人而言,我会分 3 层构建它。
第 1 层: API 接口
这一层是对远程 API 进行实际行级调用的地方。这一层是关于协议的。在这一层中不应该有任何特定于 API 的内容。一切都应该是 100% 通用的,但应该能够被中间层用来与 API 交互。请注意,该层可以来自库或其他来源,如框架。或者你可以自定义编写。这一切都取决于您在哪里以及您的确切需求。
一些可能属于这里的类:
XMLRPC_Client SOAP_Client REST_Client第 2 层: API 适配器
这一层实际上将 API 信息硬编码到其中。这基本上是Adapter Pattern。基本上,这一层的工作是使用接口层将远程 API 转换为本地 API。因此,根据您的需要,您可以以 1:1 的方式镜像远程 API,或者您可以根据您的需要进一步调整它。但要记住的是,这个类不是为你的程序提供功能。目的是将远程 API 与本地代码分离。通过换出这个类,您的代码应该能够快速适应使用不同版本的远程 API,甚至可能同时使用不同的远程 API。
要记住的重要一点是,此适配器层旨在包含 API。因此,每个单独类的范围是整个 API 实现。所以适配器和远程 API 之间应该有 1:1 的映射关系。
这里可能有一些类:
RemoteAPI_v1_5 RemoteAPI2_v1第 3 层: 内部对象
这一层应该是不同对象的内部表示(在您的特定情况下:用户、购物篮、购物车、捐赠、会员资格等)。他们不应该直接调用 API,而是使用组合(依赖注入)成为 API 的基本 bridge。通过将其分开,您应该能够完全独立于内部类来改变 API(反之亦然)。
因此,您的其中一个类可能如下所示:
class User
protected $api;
public function __construct(iAPIAdapter $api)
$this->api = $api;
public function login()
$this->api->loginUser($blah);
这样一来,就不需要 APImanager 了。只需在程序开始时创建一个新的 API 实例,并将其传递给您的其余代码。但它的主要优点是非常灵活,您应该能够通过简单地更换代码中的适配器层(当您实例化适配器时)来更改 API(版本或调用本身)。其他一切都应该正常工作,并且您应该与代码或远程 API 的更改完全隔离(更不用说如果以这种方式构建它应该是相当可测试的)...
这是我的 0.02 美元。这可能有点矫枉过正,但这实际上取决于您的确切需求......
【讨论】:
好的建议和解释。依赖注入是组合优于继承的关键。 @ircmaxwell 好帖子。我将按照您的建议构建一个 API 包装器。您能否链接到我可以查看的基于此模型构建的 API 包装器示例? 同意,抱歉。我没有意识到在当天接受答案很重要,然后离开 SO 几年做其他事情。回到现在,很好的答案被适当地接受了。非常感谢@ircmaxell @bernie2436 你有没有找到一个很好的例子?我也将这样做,我很想先看一个例子。 太棒了。感谢分享。【参考方案2】:我会说:
-
创建一个包含所有需要的捐赠类
创建一个成员成员变量(应该是捐赠类型)。
您可以在成员资格类中有一个方法,例如:
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
这样你就可以很好地在项目之间解耦。此外,Donation 应该实现一个 iterface,以便以后如果行为发生变化,它仍然应该包含 memeber 类所需的方法。
这种方式比继承更灵活。 前段时间我也问过类似的问题,得到了很好的回答:
Elegant alternatives to the weird multiple inheritance
【讨论】:
为了让它变得更好,你可以使用依赖注入而不是在方法中实例化类。所以makeDonation(Donation $donation) $this->donation = $donation; ...
。它甚至更好。但我同意,总是倾向于组合而不是继承......以上是关于PHP OOP :: 构建 API Wrapper 类的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 OOP 将 $db 从另一个 .php 使用到另一个 .php 的类?
python:import com.oceanoptics.omnidriver.api.wrapper.Wrapper