2010-09-28 23 views
3

假设我有一个具有一个基类和几个子类(一个级别)的域对象的类层次结构。删除if-is语句的设计选择

假设我有这些对象的列表(基类列表),我想将一些逻辑应用于我觉得不属于类的类(例如,设计/ UI特定的代码) 。

我的替代品是什么?

  1. If-is语句。就个人而言,这不应该被视为一种选择,但我仍然写它。

  2. 多态性。这其中的一个在某些情况下实际上是一种替代方案,但是在上面的示例中,我不希望我的类包含任何UI细节。

  3. 根据对象类型通过反射/ IoC容器解析一些逻辑方法。例如C#。 Type type = typeof(ILogic <>)。MakeGenericType(domainObject.GetType());
    我真的很喜欢这个,我没有得到任何编译时间检查,虽然如果一个子类的实现丢失,或者可能以某种方式?

  4. 访客模式。将工作,但看起来有点矫枉过正,适用于只有一层深的结构。

任何人有任何其他的技巧或窍门来解决这类问题?

回答

2

伟大的问题。问题是有很多解决方案,其中大部分都可以工作。

我和MVC一起工作了很多,类似的情况经常发生。特别是在视图中,当某些视图需要类似的渲染时......但并不真正属于视图。

比方说,我们有子类ChildList,延伸BaseList

在子类中添加属性uiHandler。重载渲染函数,让我们说toString(),并使用uiHandler与您的特定UI /设计的东西。

我写了一点东西,它是在PHP中......但你应该能够得到一个想法。它使您可以自由选择对象的显示方式,并可灵活地为特定对象使用特定的UI。

看看下面的代码,它似乎很多,但诠释并没有那么糟糕。

  • BaseList - 你的基类
  • BaseListUIExtended - 使用用户界面,采用可选的UI类的构造函数的参数基类。在C#4中,您可以使用可选的,否则使用2个构造函数。
  • UIBase - 界面UI类...
  • UIChildSpecific - UI类
  • ChildList - 子类可以使用UI还是不行,因为BaseListUIExtended可选的构造参数。

定义界面

/** 
* Base UI interface 
*/ 
interface IUIBase { 

    /** 
    * Renders the Base Class 
    * 
    * @param UIBase $obj 
    * @return string 
    */ 
    public function render($obj); 

} 

定义的基类,儿童类

//************************************************************** 
// Define Base Classes 
//************************************************************** 
/** 
* Base Class 
*/ 
class BaseList { 

    /** 
    * List of items 
    * @var array 
    */ 
    protected $_items = array(); 

    /** 
    * Gets collection of items 
    * 
    * @return array 
    */ 
    public function getItems() { 
     return $this->_items; 
    } 

    /** 
    * Adds new item to the list 
    * @param object $item 
    */ 
    public function add($item) { 
     $this->_items[] = $item; 
    } 

    /** 
    * Displays object 
    */ 
    public function display() { 
     echo $this->toString(); 
    } 

    /** 
    * To String 
    */ 
    public function __toString() { 
     // Will output list of elements separated by space 
     echo implode(' ', $this->_items); 
    } 

} 

/** 
* Extended BaseList, has UI handler 
* This way your base class stays the same. And you 
* can chose how you create your childer, with UI or without 
*/ 
class BaseListUIExtended extends BaseList { 

    /** 
    * UI Handler 
    * @var UIBase 
    */ 
    protected $_uiHandler; 

    /** 
    * Default Constructor 
    * 
    * @param UIBase Optional UI parameter 
    */ 
    public function __construct($ui = null) { 

     // Set the UI Handler 
     $this->_uiHandler = $ui; 
    } 

    /** 
    * Display object 
    */ 
    public function display() { 
     if ($this->_uiHandler) { 
      // Render with UI Render 
      $this->_uiHandler->render($this); 
     } else { 
      // Executes default BaseList display() method 
      // in C# you'll have base:display() 
      parent::display(); 
     } 
    } 

} 

//************************************************************** 
// Define UI Classe 
//************************************************************** 

/** 
* Child Specific UI 
*/ 
class UIChildSpecific implements UIBase { 

    /** 
    * Overload Render method 
    * 
    * Outputs the following 
    *  <strong>Elem 1</strong><br/> 
    *  <strong>Elem 2</strong><br/> 
    *  <strong>Elem 3</strong><br/> 
    * 
    * @param ChildList $obj 
    * @return string 
    */ 
    public function render($obj) { 
     // Output array for data 
     $renderedOutput = array(); 

     // Scan through all items in the list 
     foreach ($obj->getItems() as $text) { 
      // render item 
      $text = "<strong>" . strtoupper(trim($text)) . "</strong>"; 
      // Add it to output array 
      $renderedOutput[] = $text; 
     } 

     // Convert array to string. With elements separated by <br /> 
     return implode('<br />', $renderedOutput); 
    } 

} 

//************************************************************** 
// Defining Children classes 
//************************************************************** 

/** 
* Child Class 
*/ 
class ChildList extends BaseListUIExtended { 
    // Implement's logic  
} 

测试...

//************************************************************** 
// TESTING 
//************************************************************** 

// Test # 1 
$plainChild = new ChildList(); 
$plainChild->add("hairy"); 
$plainChild->add("girl"); 
// Display the object, will use BaseList::display() method 
$plainChild->display(); 
// Output: hairy girl 

// Test # 2 
$uiChild = new ChildList(new UIChildSpecific()); 
$uiChild->add("hairy"); 
$uiChild->add("girl"); 
// Display the object, will use BaseListUIExtended::display() method 
$uiChild->display(); 
// Output: <strong>hairy</strong><br /><strong>girl</strong> 
+0

+1毛女孩 – demoncodemonkey 2010-09-30 16:16:39

+0

嗨,有现在多看看你的例子,这是一个很好的解决方案,但是......它不适用于更复杂的解决方案。当所有项目都是字符串或其他值类型时,你的解决方案是优雅的,但是如果列表中的对象是复杂的对象,需要更复杂的渲染方法,这不会太好,因为需要某种“if-is “在IUIBase实现中的渲染方法中,这是我想要解决的问题。 – Marcus 2010-10-02 07:40:48

+0

@马库斯,从我的理解你的问题,你想渲染的对象,但不想保持UI /设计分开。这正是我的示例代码所做的。每个类都有它自己的UI处理程序,这会将UI与逻辑分开(类似于MVC中的“视图”和“控制器”)。 字符串被用于简化。您可以创建Container,并使用容器定位所有对象(按钮,标签,文本框)。无论哪种方式,你将不得不在你的类(杂乱的代码+难以维护)或某种UI处理程序(更干净的代码+更容易维护)中编写UI /设计逻辑。 – Alex 2010-10-04 12:46:55