c# - 扩展方法(类)或访问者模式

标签 c# design-patterns

在进行良好的设计时,您会选择扩展方法还是访问者模式?

哪个更容易设计,什么时候应该在访问者模式上使用扩展方法,反之亦然?

除了语法糖以帮助提高程序可读性之外,是否有任何正当理由在访问者类上使用扩展方法?

您将如何设计包含扩展方法的系统,您会在 UML 图中对它们进行分类吗?

namespace ExtensionMethods
{
    public static class MyExtensions
    {
        public static int WordCount(this String str)
        {
            return str.Split(new char[] { ' ', '.', '?' }, 
                             StringSplitOptions.RemoveEmptyEntries).Length;
        }
    }   
}

我可能有错误的模式,它看起来像上面代码中的访问者模式。所以我认为我的比较成立。

一些代码,我会说扩展方法看起来像访问者模式。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    #region Interfaces

    public interface IFred
    {
        string Data
        {
            get;
            set;
        }        

        string doSomething();
    }


    public interface IBob
    {
        string Data
        {
            get;
            set;
        }
    }

    #endregion

    #region fred stuff

    public partial class Fred : IFred
    {

        public string doSomething()
        {
            return this.Data + " is really cool";
        }

        public string Value()
        {
            throw new NotImplementedException();
        }

    }

    public partial class Fred
    {
        public string Data
        {
            get;
            set;
        }
    }

    #endregion


    #region bob stuff

    public class BobData : IBob
    {
        public string Data
        {
            get;
            set;
        }
    }

    public class BobData2 : IBob
    {
        private string pData;
        public string Data
        {

            get
            {
                return pData + " and then some!";
            }
            set
            {
                pData = value;
            }
        }
    }

    public class BobVisitor
    {
        public string dosomething(IBob bobData)
        {
            Console.WriteLine(bobData.Data);
            return "ok";
        }

        public string dosomethingOnlyToBob(BobData bobData)
        {
            Console.WriteLine("hello bob version 1");
            return "ok";
        }


        public string dosomethingOnlyToBob2(BobData2 bobData)
        {
            Console.WriteLine("hello bob version 2");
            return "ok";
        }

    }

    #endregion


    public static class Visitor
    {
        public static string visit(this IBob bobObj)
        {
            Console.WriteLine(bobObj.Data);
            return "ok";

        }

        public static string visit(this IFred fredObj)
        {
            Console.WriteLine(fredObj.Data);
            return "ok";
        }
    }


    class Program
    {
        static void Main(string[] args)
        {

            //Another way of abstracting methods from data, using Partial Classes.
            var fredObj = new Fred();
            fredObj.Data = "fred data";
            fredObj.doSomething();


            //Create the bob classes version 1 and 2
            var bobObj = new BobData();
            bobObj.Data = "bob data";

            var bob2Obj = new BobData2();
            bob2Obj.Data = "bob 2 data";


            //using the bobVisitor Class
            var bobVisitor = new BobVisitor();

            bobVisitor.dosomething(bobObj);
            bobVisitor.dosomething(bob2Obj);

            bobVisitor.dosomethingOnlyToBob(bobObj);
            bobVisitor.dosomethingOnlyToBob2(bob2Obj);


            //using the extension methods in the extension class
            bobObj.visit();
            fredObj.visit();

            Console.Read();
        }
    }
}

最佳答案

您可能应该将访问者模式与模板方法模式进行比较,因为您可以比较和对比这两件事。

将访问者模式与扩展方法进行比较就像将汽车与自行车链轮进行比较。

在任何情况下,扩展方法在任何非虚方法有用的地方都很有用,额外的好处是您不需要拥有类型来定义扩展方法。

模板方法和访问者模式都是旨在对对象树进行操作的设计模式。两者的“经典”定义都需要对象树中每个“节点类型”中的虚拟方法。但是,如有必要,可以使用非虚拟方法来实现两者。有一些限制,例如访问私有(private)成员和 protected 成员,但忽略这一点,任何一种模式都可以通过扩展方法实现。

模板方法模式的工作原理是为对象树中的每个类型添加一个用于操作的虚拟方法,“聚合节点”调用其包含的节点上的方法。

一个示例可能是表达式树的“打印”方法。

public class Node
{
   abstract void print();
}

public class AddExpression : Node {
    Node Left;
    Node Right;

    virtual void print() {
        Left.Print();
        Console.WriteLine("+");
        Right.Print();
    }
}

这有一个主要好处,因为添加新的节点类型只需要增加工作量。只有新类型需要更改。但是,它有一个缺点,即添加新操作需要编辑每个类型。

访问者模式将模板方法概括为一个名为 accept 的方法,该方法将访问者对象作为参数。它看起来像:

interface Visitor {
    void VisitAdd(AddExpression e);
    void VisitSubtract(SubtractExpression e);
}
abstract class Node {
    abstract void Accept(Visitor v);
}
class AddExpression : Node {
    //...
    virtual void Accept(Visitor v) {
        Left.Accept(v);
        v.VisitAdd(this);
        Right.Accept(v);
    }
}

这有相反的权衡。添加新操作只需要编写一个新类,但添加新类型需要编辑每个操作。

经典的建议是在操作(相对固定)但可以频繁添加新对象类型时使用模板方法。同样,当对象类型固定时,应该使用访问者,但可以经常添加新的操作,

如果两者变化相同,那么您的决定应该基于平衡:

  1. 清晰(模板方法更容易理解,避免双重分派(dispatch)的开销)。
  2. 重用(访问者将常见的遍历代码分解到一个地方)。

关于c# - 扩展方法(类)或访问者模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6935626/

相关文章:

c# - 当 POCO 文件位于单独的项目中时将 EF 指向数据库

c# - C# 中的变量作用域 - 使用 session 还是类?

c# - svcutil NOT 为 minOccurs=0 生成指定字段

c# - SQLiteDataReader,最后一次Read()需10秒

php - 这些 MVC 层正确吗?

c# - 请推荐一个将asp.net网站迁移到三层架构的方案

c# - 如何使用 WMI 确保进程在远程计算机上结束?

java - 扩展 Observable 的类的接口(interface)

integration - mule中如何处理3+消息格式?

php - 需要面向对象设计的建议 : a collection of items