用连贯接口实现 DSL 风格编程
上一篇:如何运用连贯接口2
转自:http://www.fleaphp.org/index.php?q=node/68
DSL 是一个比较热门的概念,是领域描述语言(Domain Specific language)的英文缩写。不过这里我不想和大家解释概念问题,这个任务还是留给其他人吧 :)
从实践来说,DSL 风格就是通过一组前后关联的方法调用来完成一个领域操作。所谓领域操作可以看作常说的业务操作,虽然本质上两个定义是不同的。
下面就是一段创建订单的代码:
// 通过 SESSION 获得当前用户对象,然后创建一个新的 Order 对象
$order = $session->getCurrentUser()->newOrder()
// 添加产品和数量信息到订单中
$order->addItem(Product::find('id=?', intval($_POST['product_id'])))
->withQuantity(intval($_POST['quantity']);
// 保存订单对象
$order->save();看看内部实现的主要代码:
/**
* Member 封装一个用户
*/
class Member extends QActiveRecord_Abstract
{
function newOrder()
{
// 构造订单对象要求传入用户ID
$order = new Order($this);
// 根据用户的属性,设置订单的折扣率、收货人等信息
$order->withDiscount($this->purchaseDiscount)
->withConsignee($this->realname)
->withAddress($this->address)
}
}
/**
* Order 封装一个订单
*/
class Order extends QActiveRecord_Abstract
{
/**
* 下订单的用户
*
* @var Member
*/
protected $_user;
/**
* 保存订单的多个项目
*
* @var array
*/
protected $_items;
/**
* 收货地址
*
* @var Address
*/
protected $_address;
function __construct(Member $user)
{
$this->_user = $user;
}
/**
* 设置订单的折扣率
*
* @param float $discount
*
* @return Order
*/
function withDiscount($discount)
{
// 设置订单的折扣率
$this->discount = $discount;
return $this;
}
/**
* 设置订单的收货地址
*
* @param Address $address
*
* @return Order
*/
function withAddress(Address $address)
{
$this->address = $address;
return $this;
}
/**
* 添加一个购买项目
*
* @param Product $product
* @param int $quantity
*
* @return OrderItem
*/
function addItem(Product $product, $quantity = 1)
{
// 添加一个订单项目
$item = new OrderItem($product);
$item->withQuantity($quantity);
$this->items[] = $item;
return $item;
}
/**
* sum 属性的 getter 方法
*
* @return float
*/
function getSum()
{
$sum = 0;
foreach ($this->_items as $item) {
/* @var $item OrderItem */
$sum += $item->sum;
}
return $sum;
}
}
/**
* OrderItem 封装一个订单项目
*/
class OrderItem extends QActiveRecord_Abstract
{
/**
* 订单项目对应的商品
*
* @var Product
*/
protected $_product;
/**
* 订单项目金额合计
*
* @var float
*/
protected $_sum;
/**
* 购买数量
*
* @var int
*/
protected $_quantity;
function __construct(Product $product)
{
$this->_product = $product;
}
/**
* 指定购买数量
*
* @param int $quantity
*
* @return OrderItem
*/
function withQuantity($quantity)
{
$this->_quantity = $quantity;
$this->_sum = $this->_product->price * $this->_quantity;
return $this;
}
/**
* sum 属性的 getter 方法
*
* @return float
*/
function getSum()
{
return $this->_sum;
}
}
在这个示例中,我们用到多个对象。这些对象,每一个都只承担了属于自己的那部分职责。这种设计,使得整个对象体系清晰可见,对于功能的扩展和修改都提供了帮助。
当然,这几个类都从 QActiveRecord_Abstract 继承。正是 QActiveRecord_Abstract 封装了对只读属性、属性方法以及对象的 CRUD 等操作,才使得我们可以将精力集中于编写业务代码。
结束语
连贯接口是一种非常有价值的语法工具,在特定场合可以简化编码和提高代码的可读性。但是正如所有技术和概念,连贯接口也有自己适合的领域。超出这个领域,再强行使用连贯接口就事倍功半了 ;)
限于个人水平,本文难免存在不足和错误。希望能够听到广大 PHPer 的反馈意见,这样对于所有人来说都是一个学习提高的过程。

