一般来说,我遵守得墨忒耳法则,因为它有助于将更改保持在缩小的范围内,这样新需求或错误修复就不会遍布您的系统。还有其他设计指南可以帮助实现这个方向,例如本文中列出的指南。话虽如此,我认为得墨忒耳法则(以及设计模式和其他类似的东西)是有用的设计指南,它们有权衡取舍,如果你认为这样做是可以的,你可以打破它们。例如我一般不测试私有方法,主要是因为它会创建脆弱的测试. 但是,在一些非常特殊的情况下,我确实测试了一个对象私有方法,因为我认为它在我的应用程序中非常重要,因为我知道如果对象的实现发生更改,该特定测试将受到更改。当然,在这些情况下,您必须格外小心,并为其他开发人员留下更多文档来解释您这样做的原因。但是,最后,您必须使用您的良好判断力:)。
现在,回到最初的问题。据我了解,您的问题是为一个对象编写(网络?)GUI,该对象是可以通过消息链访问的对象图的根。对于这种情况,我会以与您创建模型类似的方式模块化 GUI,为模型的每个对象分配一个视图组件。因此,您将拥有诸如OrderView
、AddressView
等知道如何为其各自模型创建 HTML 的类。然后,您可以组合这些视图来创建您的最终布局,或者通过将责任委托给它们(例如OrderView
创建AddressView
)或通过使用调解器负责组合它们并将它们链接到您的模型。作为第一种方法的一个例子,你可以有这样的东西(我将使用 PHP 作为例子,我不知道你使用的是哪种语言):
class ShoppingBasket
{
protected $orders;
protected $id;
public function getOrders(){...}
public function getId(){...}
}
class Order
{
protected $user;
public function getUser(){...}
}
class User
{
protected $address;
public function getAddress(){...}
}
然后是意见:
class ShoppingBasketView
{
protected $basket;
protected $orderViews;
public function __construct($basket)
{
$this->basket = $basket;
$this->orederViews = array();
foreach ($basket->getOrders() as $order)
{
$this->orederViews[] = new OrderView($order);
}
}
public function render()
{
$contents = $this->renderBasketDetails();
$contents .= $this->renderOrders();
return $contents;
}
protected function renderBasketDetails()
{
//Return the HTML representing the basket details
return '<H1>Shopping basket (id=' . $this->basket->getId() .')</H1>';
}
protected function renderOrders()
{
$contents = '<div id="orders">';
foreach ($this->orderViews as $orderView)
{
$contents .= orderViews->render();
}
$contents .= '</div>';
return $contents;
}
}
class OrderView
{
//The same basic pattern; store your domain model object
//and create the related sub-views
public function render()
{
$contents = $this->renderOrderDetails();
$contents .= $this->renderSubViews();
return $contents;
}
protected function renderOrderDetails()
{
//Return the HTML representing the order details
}
protected function renderOrders()
{
//Return the HTML representing the subviews by
//forwarding the render() message
}
}
在您的 view.php 中,您会执行以下操作:
$basket = //Get the basket based on the session credentials
$view = new ShoppingBasketView($basket);
echo $view->render();
这种方法基于组件模型,其中视图被视为可组合组件。在此模式中,您尊重对象的边界,并且每个视图都有一个单一的职责。
编辑(根据 OP 评论添加)
我假设没有办法在子视图中组织视图,并且您需要在一行中呈现购物篮 ID、订单日期和用户名。正如我在评论中所说,对于那种情况,我会确保在一个单独的、有据可查的地方执行“坏”访问,让视图不知道这一点。
class MixedView
{
protected $basketId;
protected $orderDate;
protected $userName;
public function __construct($basketId, $orderDate, $userName)
{
//Set internal state
}
public function render()
{
return '<H2>' . $this->userName . "'s basket (" . $this->basketId . ")<H2> " .
'<p>Last order placed on: ' . $this->orderDate. '</p>';
}
}
class ViewBuilder
{
protected $basket;
public function __construct($basket)
{
$this->basket = $basket;
}
public function getView()
{
$basketId = $this->basket->getID();
$orderDate = $this->basket->getLastOrder()->getDate();
$userName = $this->basket->getUser()->getName();
return new MixedView($basketId, $orderDate, $userName);
}
}
如果稍后您重新排列域模型并且您的ShoppingBasket
类无法再实现该getUser()
消息,那么您将不得不更改应用程序中的一个点,避免该更改遍布您的系统。
高温高压