Методы расширения (класс) или Шаблон посетителя - PullRequest
7 голосов
/ 04 августа 2011

При выборе хорошего дизайна, что бы вы выбрали, методы расширения или шаблон посетителя?

Что легче спроектировать, когда следует использовать метод расширения для шаблона посетителя и наоборот?

Есть ли веская веская причина использовать метод расширения для класса посетителя, кроме синтаксического сахара, чтобы улучшить читаемость программы?

Как бы вы разработали систему, которая включает методы расширения, вы бы классифицировали их в диаграмме 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();
        }
    }
}

Ответы [ 2 ]

8 голосов
/ 04 августа 2011

Вы, вероятно, должны сравнивать шаблон посетителя с шаблоном метода шаблона, поскольку это две вещи, которые вы можете сравнивать и сравнивать.

Сравнение шаблона посетителя с методами расширения похоже на сравнение автомобиля с велосипедной звездочкой.

В любом случае методы расширения полезны везде, где полезен не виртуальный метод, с дополнительным бонусом, которыйвам не нужно владеть типом для определения метода расширения.

И шаблонный метод, и шаблон посетителя являются шаблонами проектирования, предназначенными для работы над деревьями объектов.«Классическое» определение обоих требует наличия виртуального метода в каждом «типе узла» в дереве объектов.Тем не менее, при необходимости можно реализовать оба с использованием не виртуальных методов.Существуют некоторые ограничения, такие как доступ к закрытым и защищенным элементам, но игнорируя это, любой шаблон может быть реализован с помощью методов расширения.

Шаблон метода шаблона работает путем добавления виртуального метода для операции к каждому типу вдерево объектов с "агрегатными узлами", вызывающими метод на их содержащихся узлах.

Примером может быть метод "print" для дерева выражений.

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. Ясность (методы шаблона проще для понимания и позволяют избежать затрат на двойную диспетчеризацию).
  2. Повторное использование (посетители учитывают общий код обхода в одном месте).
0 голосов
/ 04 августа 2011

Методы расширения не являются шаблонами. Это всего лишь некоторый синтаксис языка, который облегчает чтение кода.

Шаблон посетителя - это совершенно другой зверь. Я действительно не знаю, почему вы сравниваете их.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...