我想知道以下是否是访客模式的可接受用途.从Accept()或Visit()调用返回时我感到有点不舒服 - 这是否适合使用此模式,如果没有,为什么不呢?
注意:长代码示例的道歉,似乎有必要了解我正在做的事情,因为访问者似乎总是有点参与......
interface IAnimalElement<T>
{
T Accept(IAnimalVisitor<T> visitor);
}
interface IAnimalVisitor<T>
{
T Visit(Lion lion);
T Visit(Peacock peacock);
T VisitZoo(List<Animal> animals);
}
abstract class Animal
{
public int Age { get; protected set; }
}
class Lion : Animal, IAnimalElement<int>
{
public Lion(int age)
{
Age = age;
}
public int Accept(IAnimalVisitor<int> visitor)
{
return visitor.Visit(this);
}
}
class Peacock : Animal, IAnimalElement<int>
{
public Peacock(int age)
{
Age = age;
}
public int Accept(IAnimalVisitor<int> visitor)
{ …Run Code Online (Sandbox Code Playgroud) 让我们想象一下,我有一组节点,我稍后会用于我的Renderer类.然后我有一个访问者类,可以访问节点或整个集合.这很简单,因为我的节点集合只是std :: list的包装器,只需要很少的额外方法.
问题是我希望节点(而不是简单列表)具有树状结构,因此节点可以有父节点和n个子节点.这将是方便的,因为我希望能够将一个节点传递给我的Renderer并将所有内容呈现在该节点的"下方".答案可能是复合材料.
我如何一起使用访客和复合?我已经读过它通常是一个很好的组合,但我的实现看起来很糟糕......我很想念......
嘿那里,我已经阅读了关于何时/如何使用访问者模式的一些帖子,以及它上面的一些文章/章节,如果你正在遍历AST并且它是高度结构化的,并且你想要封装逻辑分成一个单独的"访问者"对象等.但是使用Ruby,它似乎有点过分,因为你可以使用块来做几乎相同的事情.
我想使用Nokogiri的pretty_print xml.作者建议我使用访问者模式,这需要我创建一个FormatVisitor或类似的东西,所以我可以说"node.accept(FormatVisitor.new)".
问题是,如果我想开始自定义FormatVisitor中的所有内容(例如,它允许您指定节点的选项卡方式,属性如何排序,属性如何间隔等等).
我有几个选择:
而不是必须构造一个FormatVisitor,设置值,并将其传递给node.accept方法,为什么不这样做:
node.pretty_print do |format|
format.tabs = 2
format.sort_attributes_by {...}
end
Run Code Online (Sandbox Code Playgroud)
这与我觉得访客模式看起来形成鲜明对比:
visitor = Class.new(FormatVisitor) do
attr_accessor :format
def pretty_print(node)
# do something with the text
@format.tabs = 2 # two tabs per nest level
@format.sort_attributes_by {...}
end
end.new
doc.children.each do |child|
child.accept(visitor)
end
Run Code Online (Sandbox Code Playgroud)
也许我的访客模式都是错误的,但是从我在红宝石中读到的那些,看起来有点矫枉过正.你怎么看?无论哪种方式都适合我,只是想知道你们对它的看法.
非常感谢Lance
是否可以跟踪访问者遇到的JS错误?显然我们自己做测试,但访问者不时会运行某个浏览器版本,或者有一个特定的插件,因为它会导致JS错误.同样地,有时JS中的错误会被忽视.
如果我们能够以某种方式捕获这些事件,并在我们的服务器上使用错误的详细信息来调用脚本以便我们可以尝试解决问题,那将非常有用.
更新:
感谢您的反馈.通常在发布后我设法找到:
在服务器上记录客户端的JavaScript错误在
window.onerror 上有东西,还有一些关于JQuery和FireFox onerror bug的有趣的东西,多年来仍未修复...
访问者模式允许在不扩展对象类的情况下编写对象上的操作.当然.但是为什么不编写一个从外部操作我的对象集合的全局函数或静态类呢?基本上,在像java这样的语言中,accept()出于技术原因需要一种方法; 但是在一种我可以在没有accept()方法的情况下实现相同设计的语言中,访问者模式是否变得微不足道?
说明:在访问者模式中,可访问的类(实体)有一个方法,.accept()其作用是自己调用访问者的.visit()方法.我可以看到java示例的逻辑:访问者.visit(n)为n它支持的每个可访问类型定义了一个不同的方法,并且.accept()必须使用技巧在运行时选择它们.但是像python或php这样的语言有动态类型,没有方法重载.如果我是访问者,我可以在.serialize()不知道实体的类型甚至方法的完整签名的情况下调用实体方法(例如).(那是"双重调度"问题,对吧?)
我知道一种接受方法可以将受保护的数据传递给访问者,但有什么意义呢?如果数据暴露给访问者类,它实际上是类接口的一部分,因为它的详细信息在类之外很重要.无论如何,暴露私人数据从来没有让我感到是访客模式的重点.
所以似乎在python,ruby或php中我可以在访问对象中没有accept方法(并且没有反射)的情况下实现类似访问者的类,对吧?如果我可以使用一系列异构对象并在没有"访问"类的任何合作的情况下调用他们的公共方法,那么这仍然应该被称为"访问者模式"吗?是否存在一些我缺失的模式的本质,或者它只是归结为"编写一个从外部操作对象来执行操作的新类"?
PS.我已经看过很多关于SO和其他地方的讨论,但找不到任何解决这个问题的东西.指针欢迎.
我在.Net 4.0中有以下C#代码.它需要对IRetailBusiness进行IBusiness类型转换.
//Type checking
if (bus is IRetailBusiness)
{
//Type casting
investmentReturns.Add(new RetailInvestmentReturn((IRetailBusiness)bus));
}
if (bus is IIntellectualRights)
{
investmentReturns.Add(new IntellectualRightsInvestmentReturn((IIntellectualRights)bus));
}
Run Code Online (Sandbox Code Playgroud)
业务场景:
我正在为投资控股公司设计软件系统.该公司拥有零售业务和IntellectualRights业务.BookShop和AudioCDShop是零售业务的例子.EngineDesignPatent和BenzolMedicinePatent是IntellectualRights业务的例子.这两种业务类型完全不相关.
投资公司有一个概念叫InvestmentReturn (但每个企业对这个概念完全无知).InvestmentReturn是从每个业务获得的利润,并使用它进行计算ProfitElement.对于每种"业务类型"(Retail,IntellectualRights),使用的ProfitElement是不同的.
题
如何重构这个一流的设计,以避免这种情况type casting和type checking?
摘要投资
public abstract class InvestmentReturn
{
public double ProfitElement { get; set; }
public IBusiness Business{ get; set; }
public abstract double GetInvestmentProfit();
public double CalculateBaseProfit()
{
double profit = 0;
if (ProfitElement < 5)
{
profit = ProfitElement * 5 / 100; …Run Code Online (Sandbox Code Playgroud) 考虑以下访问者的简单语言解释器.
public interface Visitor{
void visit( VarStat vs);
void visit( Ident i);
void visit( IntLiteral a);
void visit( Sum s);
}
Run Code Online (Sandbox Code Playgroud)
为了完整起见,我添加了一些代码,提供了必要的实现细节(您可以跳过并直接阅读问题).
public interface Visitable{
void accept( Visitor v);
}
public class VarStat implements Visitable{
Ident i;
Exp e;
public VarStat(Ident id, Exp ex){
i = id;
e = ex;
}
public Ident getIdent() { return i; }
public Exp getExp() { return e; }
@Override
public void accept( Visitor v){
v.visit( this);
}
}
public interface Exp extends Visitable{ …Run Code Online (Sandbox Code Playgroud) 我的IComposerc#项目中有一个界面:
public interface IComposer
{
string GenerateSnippet(CodeTree tree);
}
Run Code Online (Sandbox Code Playgroud)
CodeTree是一个基类,包含一个List<CodeTree>继承自的类CodeTree.例如:
public class VariablesDecleration : CodeTree
{
//Impl
}
public class LoopsDecleration : CodeTree
{
//Impl
}
Run Code Online (Sandbox Code Playgroud)
我可以有几个实现的类,IComposer每个我都有GenerateSnippet循环List<CodeTree>,基本上做:
foreach (CodeTree code in tree.Codes)
{
if (code.GetType() == typeof(VariablesDecleration))
{
VariablesDecleration codeVariablesDecleration = (VariablesDecleration) code;
// do class related stuff that has to do with VariablesDecleration
}
else if (code.GetType() == typeof(LoopsDecleration))
{
LoopsDecleration codeLoopsDecleration = (LoopsDecleration) code;
// …Run Code Online (Sandbox Code Playgroud) 由于前一个问题的一些答案,我可以成功地替换lambda表达式中的简单参数类型,但我无法弄清楚如何将传入的lambda中的参数替换为嵌套参数.
考虑以下对象:
public class DtoColour {
public DtoColour(string name)
{
Name = name;
}
public string Name { get; set; }
public ICollection<DtoFavouriteColour> FavouriteColours { get; set; }
}
public class DtoPerson
{
public DtoPerson(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
FavouriteColours = new Collection<DtoFavouriteColour>();
}
public string FirstName { get; private set; }
public string LastName { get; private set; }
public ICollection<DtoFavouriteColour> FavouriteColours { get; set; }
}
public class DtoFavouriteColour
{ …Run Code Online (Sandbox Code Playgroud) 我只是在 std::visit 和 std::function 附近反弹到一些微妙的东西,这让我感到困惑。我并不孤单,但我能找到的唯一其他人做了“解决方法并继续前进”的舞蹈,这对我来说还不够:
这可能与 LWG 中的一个悬而未决的问题有关,但我认为这里正在发生一些更险恶的事情:
最小示例:
// workaround 1: don't include <variant>
#include <variant>
#include <functional>
struct Target
{
Target *next = nullptr;
};
struct Visitor
{
void operator()(const Target &tt) const { }
};
// workaround 2: concretely use 'const Visitor &' instead of 'std::function<...>'
void visit(const Target &target, const std::function<void(const Target &)> &visitor)
{
visitor(target);
if(target.next)
visit(*target.next,visitor); // workaround 3: explicitly invoke ::visit(...)
//^^^ problem: compiler is trying to resolve this as …Run Code Online (Sandbox Code Playgroud)