Циклическая зависимость: IVisitor и Datas - PullRequest
0 голосов
/ 22 октября 2018

Я недавно спрашивал о циклической зависимости.Ответ состоял в том, чтобы посвятить проект интерфейсам (MyProject.Abstractions).Теперь этот проект является причиной другой циклической зависимости с шаблоном Visitor.

namespace MyProject.Abstractions
{
    public interface ICharacter { }

    public interface ICharacterVisitor
    {
        // References MyProject.Characters
        void Visit(Warrior warrior);
        void Visit(Wizard wizard);
    }
}

namespace MyProject.Characters
{
    // References MyProject.Abstractions
    public abstract class CharacterBase : ICharacter { }

    public class Warrior : CharacterBase { }
    public class Wizard : CharacterBase { }
}

Означает ли это, что мой ICharacterVisitor должен быть в моем MyProject.Characters проекте?Я посвящаю все это решение моим тренировкам SOLID.

Ответы [ 2 ]

0 голосов
/ 27 октября 2018

По своей природе шаблон посетителя имеет тенденцию вводить циклическую зависимость - интерфейс посетителя должен знать обо всех различных классах, с которыми он работает, и каждый из них должен каким-то образом знать, как вызвать правильный метод для посетителя, что означаетзная класс посетителя.

Вы можете разорвать эту циклическую зависимость, выполнив что-то вроде:

// Abstractions project
interface ICharacter
{
    string Name { get; } 
}
interface IWarrior : ICharacter
{
    void Attack();
}
interface IWizard : ICharacter
{
    void CastSpell();
}
interface IVisitor
{
    void Visit(IWarrior w);
    void Visit(IWizard w);
}
// implementations project
abstract class CharacterBase : ICharacter
{
    public string Name { get; }
    public abstract void Accept(IVisitor v);
}
class Warrior : CharacterBase, IWarrior
{
    public void Attack()
    {
        // do warrior things
    }
    public override void Accept(IVisitor v)
    {
        v.Visit(this);
    }
}
class Wizard : CharacterBase, IWizard
{
    public void CastSpell()
    {
        // do wizardly things
    }
    public override void Accept(IVisitor visitor)
    {
        visitor.Visit(this);
    }
}

Предполагая, что вам действительно нужен класс "CharacterBase" для хранения какого-то родаобщей функциональности / свойств между персонажами.

Стоит ли делать вызов, который вам придется сделать, так как характер шаблона Visitor является реальной проблемой - насколько вероятно, что вы добавите больше типов символов вбудущее?Какова вероятность того, что вы добавите больше посетителей?Есть ли у вас способ, которым каждый тип персонажа сам решает, какие действия ему необходимо предпринять в определенных сценариях?

0 голосов
/ 22 октября 2018

Посетитель - это инструмент, который применяется к существующей структуре, но не является ее частью.Поэтому я бы отделил посетителя от данных, которые вы посещаете.

namespace MyProject.Abstractions.Characters
{
    public interface ICharacter { }
}
using MyProject.Abstractions.Characters;

namespace MyProject.Characters
{
    public abstract class CharacterBase : ICharacter { }

    public class Warrior : CharacterBase { }
    public class Wizard : CharacterBase { }
}
using MyProject.Abstractions.Characters;
using MyProject.Characters;

namespace MyProject.Abstractions.Visitors
{
    public interface ICharacterVisitor
    {
        // References MyProject.Characters
        void Visit(Warrior warrior);
        void Visit(Wizard wizard);
    }
}
using MyProject.Abstractions.Characters;
using MyProject.Abstractions.Visitors
using MyProject.Characters;

namespace MyProject.Visitors
{
    // Concrete visitors here
}

Вам не обязательно иметь отдельный проект для каждого пространства имен.Материал для посетителей может быть в том же проекте, что и MyProject.Characters.SOLID - это логическая организация кода, а не физическая.В этом ответе на преимущества нескольких проектов и одного решения перечислены веские причины для нескольких проектов.

...