En general, me atengo a la Ley de Demeter, ya que ayuda a mantener los cambios en un ámbito reducido, de modo que un nuevo requisito o una corrección de errores no se extienda por todo el sistema. Existen otras pautas de diseño que ayudan en esta dirección, p. los enumerados en this article. Habiendo dicho eso, considero la Ley de Demeter (así como los Patrones de Diseño y otras cosas similares) como pautas de diseño útiles que tienen sus compensaciones y que puede romperlas si juzga que está bien hacerlo. Por ejemplo, generalmente no test private methods, principalmente porque crea fragile tests. Sin embargo, en algunos casos muy particulares, probé un método privado de objeto porque lo consideré muy importante en mi aplicación, sabiendo que esa prueba en particular estará sujeta a cambios si la implementación del objeto cambia. Por supuesto, en esos casos debe tener mucho cuidado y dejar más documentación para que otros desarrolladores expliquen por qué lo hace. Pero, al final, tienes que usar tu buen juicio :).
Ahora, volviendo a la pregunta original. Por lo que entiendo, su problema aquí es escribir la GUI (¿web?) Para un objeto que es la raíz de un gráfico de objetos a los que se puede acceder a través de cadenas de mensajes. Para ese caso, modularizaría la GUI de forma similar a la que creó su modelo, asignando un componente de vista para cada objeto de su modelo. Como resultado, tendría clases como OrderView
, AddressView
, etc. que saben cómo crear el HTML para sus respectivos modelos. A continuación, puede componer esas vistas para crear su diseño final, ya sea delegando la responsabilidad a ellas (por ejemplo, OrderView
crea el AddressView
) o teniendo un Mediator que se encarga de componerlas y vincularlas a su modelo.Como ejemplo del primer enfoque que podría tener algo como esto (Voy a usar PHP para el ejemplo, no sé qué idioma que está utilizando):
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(){...}
}
y luego los puntos de vista:
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
}
}
y en su view.php que haría algo como:
$basket = //Get the basket based on the session credentials
$view = new ShoppingBasketView($basket);
echo $view->render();
Este enfoque se basa en un modelo de componentes, donde las vistas son tratados como componentes componibles. En este esquema, usted respeta los límites del objeto y cada vista tiene una responsabilidad única.
Editar (añadido basado en la observación OP)
Vamos a suponer que no hay manera de organizar las vistas en subvistas y que usted necesita para hacer la cesta de identificación, fecha de la orden y el nombre de usuario en una sola linea Como dije en el comentario, para ese caso me aseguraría de que el acceso "malo" se realice en un único lugar bien documentado, dejando la vista inconsciente de esto.
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);
}
}
Si más adelante que reorganizar su modelo de dominio y su clase de ShoppingBasket
no puede poner en práctica el mensaje getUser()
más, entonces tendrá que cambiar un solo punto en su aplicación, evitar tener que el cambio extendido por todo el sistema.
HTH
¿Cuál es tu pregunta? – hakre
Sé que no hay una pregunta específica aquí ... Coloqué la recompensa porque * podría haber * y el tema vale la pena discutirlo. Tal vez el OP podría aclarar la pregunta un poco? – rdlowrey
@Tom su problema es que no le gusta inyectar un usuario de clase, información de clase y una matriz de clases de productos en el pedido de clase? son bastante independientes aunque –