【问题标题】:Extension methods (class) or Visitor Pattern扩展方法(类)或访问者模式
【发布时间】:2011-08-04 02:45:21
【问题描述】:

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

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

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

你会如何设计一个包含扩展方法的系统,你会在 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();
        }
    }
}

【问题讨论】:

  • 我认为编写扩展方法和访问者模式本质上是在解决同一个问题。但是使用访问者模式,您可以将逻辑与类完全分离。扩展方法是向类本身添加方法。
  • 访问者将数据与方法分开。我认为扩展方法类添加的语法糖混淆了它们的真正含义。我很想称它们为装饰器,但仔细研究后,它们作用于整个班级,检查语法。
  • 他们并没有真正在课堂上添加任何东西。只是有点甜美的魔法。

标签: c# design-patterns


【解决方案1】:

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

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

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

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

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

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

public class Node
{
   abstract void print();
}

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

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

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

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

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. 清晰(模板方法更容易理解,避免双重分派的开销)。
  2. 重用(访问者将常见的遍历代码放在一个地方)。

【讨论】:

  • 在一般情况下使用访问者模式时,树是正确的。但我不是在谈论有限状态机或类似的东西。首先,我认为我们需要对扩展方法进行分类,我相信它是访问者模式或装饰器。我会称它为访客,只是根据班级的设计。然而,当使用时,语法糖会隐藏它的本质。
  • 嗯?扩展方法不是设计模式。它比设计模式低级别。这只是一种方法。同样,“for 循环”不是模式。
  • for 循环是一种模式。谷歌一下。
  • 迭代器模式是一种设计模式... for 循环是一种实现它的方式的机制...
  • @Scott,一些语言选择退出 for 循环机制,但他们需要使用一个系统来迭代列表中的一系列项目。我会说这是一种设计模式。不应将 for 循环的实现与机制混淆。我使用经典 Gof 中的迭代器模式作为 OO 语言中使用的模式的示例。如您所知,您可以使用 Array.ForEach 来迭代数组。实际的实现被隐藏起来,但概念仍然存在,作为一种模式。
【解决方案2】:

扩展方法不是一种模式。它们只是一些使代码更易于阅读的语言语法。

访客模式是完全不同的野兽。我真的不知道你为什么要比较两者。

【讨论】:

  • 它们是一种设计模式,只是语法糖隐藏了它们的真实面目。
  • 我对扩展确实形成了一个模式这一事实投了反对票。它作用于传入的对象。模式不断重复,这就是模式。您可以一遍又一遍地重复使用该模式。环境给它涂上了一层漂亮的糖衣,使 Linq 这样可爱的外观和感觉都像一种 SQL 类型语言,但这并不能阻止 Extension 方法是一种模式这一事实。如果您诚实地将扩展方法视为“语法糖”,那么我请求您再次查看它们并重新评估您的意见。
  • 我也对答案简洁这一事实投了反对票,并且在没有考虑所提出的问题的情况下给出了意见。有人会问,这样做的目的是否只是为了听起来很聪明,而不是根本不回答这个问题。神秘和简洁比礼貌、诗意和有风格要容易得多。关心需要时间,不去管它需要几分钟。
  • @Phil,到目前为止,我的问题还没有得到答复,我觉得已经回答了。如果你对你只有巨魔行为,我会请你离开我。
  • @WeeNeedAnswers,扩展方法只不过是静态方法。可以使用扩展方法完成的任何“模式”都可以使用静态方法完成。此外,扩展方法是一种语言特性,而不是一种模式。设计模式是针对常见问题的通用解决方案,并且(大部分)与语言无关。扩展方法只是在一种特定语言中调用静态方法的不同方式。我的回答很简洁,很好地反映了这个问题缺乏焦点。如果我问“请比较汽车和手机”,我不会期待“它们不同”
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-28
  • 1970-01-01
  • 2018-02-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多