当使用公共基类作为函数参数时,如何处理依赖类并避免类型检查?

时间:2017-06-09 05:55:04

标签: c# oop

我们有一个不同类型的对象树,它来自一个公共树节点,我们想要编写一个方法来渲染完整的树。我们使用基于UI选择的不同渲染器例如..文本渲染器,2d渲染器,3d渲染器。我们的树是我们的模型,我们不想在其中放入渲染代码。所以,我们决定创建单独的渲染代码。 我们的渲染器有一个方法RenderNode(TreeNode节点),它接受基类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设计。如果我添加一个新的Tree节点,我需要更改所有渲染器并避免这种情况,我们需要将渲染代码添加到树节点本身,当引入新的渲染器时,再次需要同步。 所以我们有多种树节点和多种渲染器。我们如何设计它以避免类型检查,并避免更改一个类,如果是另一类新类。

2 个答案:

答案 0 :(得分:0)

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

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机制根据nodeRenderNode的运行时类型选择正确的重载。

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

答案 1 :(得分:0)

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

Bacisly你为render alogritm的每个实现创建一个单独的类,并将该类注入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)
    {
        /*  
        ...
        */
    }
}