oop - 删除 if-is 语句的设计选择

标签 oop polymorphism typechecking

假设我有一个域对象的类层次结构,其中有一个基类和几个子类,一层。

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

我有什么选择?

  1. If-is 语句。就个人而言,这个甚至不应该被视为替代方案,但我还是写了它。

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

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

  4. 访客模式。会起作用,但应用于只有一层深度的结构似乎有些矫枉过正。

有人有解决此类问题的其他提示或技巧吗?

最佳答案

好问题。问题是有很多解决方案,而且大多数都可以。

我经常使用 MVC,类似的情况经常发生。特别是在 View 中,当类似的渲染需要在某些 View 之间发生时......但并不真正属于 View 。

假设我们有扩展 BaseList 的子类 ChildList

在子类中添加属性uiHandler。重载呈现函数,比方说 toString(),然后将 uiHandler 与您的特定 UI/设计事物一起使用。

我写了一些东西,它是用 PHP 编写的...但是您应该能够理解。它使您可以自由选择对象的显示方式,并可以灵活地为特定对象使用特定 UI。

看下面的代码,好像很多,但 int 还不错。

  • BaseList - 你的基类
  • BaseListUIExtended - 使用 UI 的基类,将可选的 UI 类作为构造函数参数。在 C# 4 中,您可以使用可选的,否则使用 2 个构造函数。
  • UIBase - UI 类的接口(interface)...
  • UIChildSpecific - 用户界面类
  • ChildList - 可以使用或不使用 UI 的子类,因为 BaseListUIExtended 可选的构造函数参数。

定义接口(interface)

/**
 * 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>

关于oop - 删除 if-is 语句的设计选择,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3813829/

相关文章:

javascript - 在 Typescript 中实例化对象,遵循松散耦合

language-agnostic - DoSomethingToThing(Thing n) vs Thing.DoSomething()

php - 域对象工厂是什么样的?

haskell - GHC 扩展的语义允许对方法进行限制 (-XConstrainedClassMethods)

c++ - 在 C++ 中重写基的重载函数

Scala:有没有一种方法可以将类型别名视为不同于它们别名的类型?

javascript - 如何在运行时在 JavaScript 中生成类型检查?

swift - Swift 中的类型检查失败并带有可选值

database - 图数据库中的面向对象编程

linq - Entity Framework 4.3 - 带有预加载的多态查询