【问题标题】:How to handle dependent class and avoid type checking when using common base class as function parameter?使用公共基类作为函数参数时如何处理依赖类并避免类型检查?
【发布时间】:2017-06-09 14:21:10
【问题描述】:

我们有一个从公共树节点派生的不同类型的对象树,我们想编写一个方法来渲染完整的树。我们正在使用基于所选 UI 的不同渲染器,例如文本渲染器、2d 渲染器、3d 渲染器。我们的树是我们的模型,我们不想在其中放入渲染代码。因此,我们决定创建单独的渲染代码。 我们的渲染器有一个方法 RenderNode(TreeNode node) 引用基类 TreeNode。由于我们需要处理各种不同类型的节点,因此我们对每个树节点类型进行类型检查并进行渲染。

void RenderNode(TreeNode node) { 
    if(node is NoEditNode){
        // call Derive class RenderEditNode
        RenderEditNode(node);
    }
    else if (node is PersonNode){
        // call Derive class RenderPersonNode
        RenderPersonNode(node);
    }
    else if (node is AssetNode){
        // call Derive class RenderAssetNode
        RenderAssetNode(node);
    }
    .
    .
    .
}

尽管这是在基类中完成的,派生的 Renderer 只会实现特定的渲染代码,但这并不是一个好的 OO 设计。如果我添加一个新的树节点,我需要更改所有渲染器,为了避免这种情况,我们需要将渲染代码添加到树节点本身,当引入新的渲染器时,这将再次需要同步。 所以我们有多种树节点和多种渲染器。我们如何设计它来避免类型检查并避免在一个新的类中更改一个类。

【问题讨论】:

    标签: c# oop


    【解决方案1】:

    如果你愿意创建多个同名但类型参数不同的渲染方法,你可以解决这个问题:

    public void RenderNode(TreeNode node) { 
        RenderNodeInternal(node as dynamic);
    }
    
    private void RenderNodeInternal(AssetNode node) {
        .
        .
        .
    }
    
    private void RenderNodeInternal(NoEditNode node) {
        .
        .
        .
    }
    
    private void RenderNodeInternal(PersonNode node) {
        .
        .
        .
    }
    

    这利用dynamic 机制根据RenderNodenode 的运行时类型选择正确的重载。

    (总的来说,这与原始代码非常相似 - 除了您不必自己编写所有类型检查代码)

    【讨论】:

    • 了解了动态关键字,但是,抱歉我正在寻找一些模式。
    【解决方案2】:

    看看visitor pattern。访问者设计模式是一种将算法与其操作的对象结构分离的方法。

    基本上,您为渲染算法的每个实现创建一个单独的类,并将该类注入到 TreeNode 派生类中。

    这是一个实现示例:

    public interface IRenderer
    {
        void Render(TreeNode node);
    }
    
    class NoEditNode : TreeNode
    {
        IRenderer renderer = new RenderEditNode();
        public void Render()
        {
            renderer.Render(this);
        }
    }
    
    class RenderEditNode :  IRenderer
    {
        public void Render(TreeNode node)
        {
        /*  
        ...
        */
        }
    }
    
    
    class PersonNode : TreeNode
    {
        IRenderer renderer;
    
        public PersonNode(IRenderer renderer)
        {
            this.renderer = renderer;
        }
    
        public void Render()
        {
            renderer.Render(this);
        }
    }
    
    class RenderPersonNode :  IRenderer
    {
        public void Render(TreeNode node)
        {
            /*  
            ...
            */
        }
    }
    

    【讨论】:

    • 我试图将渲染器创建为仅作为访问者。您的示例看起来更像策略模式。顺便说一句,当我的节点只能存储一个渲染器引用时,如何处理多个渲染器?
    • 这就像我有多个窗口,我想在不同的视图中表示我的模型。
    • 策略和访问者模式非常相似,见link。当模型必须遍历多个窗口时,您必须添加一个视图类型的感知。响应链模式可能会有所帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-05
    • 2020-08-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多