使用访问者模式时是匿名还是真实的类定义?
当您使用访问者模式并且需要在访问者方法中获取变量时,如何继续?
我看到两种方法。第一个使用匿名类:
// need a wrapper to get the result (which is just a String)
final StringBuild result = new StringBuilder();
final String concat = "Hello ";
myObject.accept(new MyVisitor() {
@Override
public void visit(ClassA o)
{
// this concatenation is expected here because I've simplified the example
// normally, the concat var is a complex object (like hashtable)
// used to create the result variable
// (I know that concatenation using StringBuilder is ugly, but this is an example !)
result.append(concat + "A");
}
@Override
public void visit(ClassB o)
{
result.append(concat + "B");
}
});
System.out.println(result.toString());
优点和缺点缺点:
- 优点:您不需要为这个小行为创建一个类文件
- 缺点:我不喜欢这种情况下的“final”关键字:匿名类的可读性较差,因为它调用外部变量,并且您需要使用包装器来获取请求的值(因为使用关键字final,您无法重新分配变量)
另一种方法是创建一个外部访问者类
public class MyVisitor
{
private String result;
private String concat;
public MyVisitor(String concat)
{
this.concat = concat;
}
@Override
public void visit(ClassA o)
{
result = concat + "A";
}
@Override
public void visit(ClassB o)
{
result = concat + "B";
}
public String getResult()
{
return result;
}
}
MyVisitor visitor = new MyVisitor("Hello ");
myObject.accept(visitor);
System.out.println(visitor.getResult());
:缺点:
- 优点:所有变量都在干净的范围内定义,不需要包装器来封装请求的变量
- 缺点:需要外部文件,必须在接受方法之后调用 getResult() 方法,这非常难看,因为您需要知道函数调用顺序才能正确使用访问者
您,在这种情况下您的方法是什么?首选方法?另一个想法?
When you use the Visitor pattern and you need to get a variable inside visitor method, how to you proceed ?
I see two approaches. The first one uses anonymous class :
// need a wrapper to get the result (which is just a String)
final StringBuild result = new StringBuilder();
final String concat = "Hello ";
myObject.accept(new MyVisitor() {
@Override
public void visit(ClassA o)
{
// this concatenation is expected here because I've simplified the example
// normally, the concat var is a complex object (like hashtable)
// used to create the result variable
// (I know that concatenation using StringBuilder is ugly, but this is an example !)
result.append(concat + "A");
}
@Override
public void visit(ClassB o)
{
result.append(concat + "B");
}
});
System.out.println(result.toString());
Pros & Cons :
- Pros : you do not need to create a class file for this little behavior
- Cons : I don't like the "final" keyword in this case : the anonymous class is less readable because it calls external variables and you need to use a wrapper to get the requested value (because with the keyword final, you can't reassign the variable)
Another way to do it is to do an external visitor class :
public class MyVisitor
{
private String result;
private String concat;
public MyVisitor(String concat)
{
this.concat = concat;
}
@Override
public void visit(ClassA o)
{
result = concat + "A";
}
@Override
public void visit(ClassB o)
{
result = concat + "B";
}
public String getResult()
{
return result;
}
}
MyVisitor visitor = new MyVisitor("Hello ");
myObject.accept(visitor);
System.out.println(visitor.getResult());
Pros & Cons :
- Pros : all variables are defined in a clean scope, you don't need a wrapper to encapsulate the requested variable
- Cons : need an external file, the getResult() method must be call after the accept method, this is quite ugly because you need to know the function call order to correctly use the visitor
You, what's your approach in this case ? Preferred method ? another idea ?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
好吧,这两种方法都是有效的,而且在我看来,这实际上取决于您是否愿意重用代码。顺便说一句,您的最后一个“缺点”点并不完全有效,因为您不需要“外部文件”来声明类。它很可能是一个内部类...
也就是说,我使用访问者的方式是这样的:
这样,被访问的对象不知道访问者想要用它们做什么,但它们确实返回访问者想要的任何内容返回。您的访问者甚至可以通过抛出异常来“失败”。
几年前我写了这个的第一个版本,到目前为止,它在任何情况下都对我有用。
免责声明:我只是将其拼凑在一起,质量(甚至编译)无法保证。但你明白了...:)
Well, both approaches are valid and imo, it really depends on whether you would like to reuse the code or not. By the way, your last 'Con' point is not totally valid since you do not need an 'external file' to declare a class. It might very well be an inner class...
That said, the way I use Visitors is like this:
That way, the visited objects are ignorant of what the visitor wants to do with them, yet they do return whatever the visitor wants to return. Your visitor can even 'fail' by throwing an exception.
I wrote the first version of this a few years ago and so far, it has worked for me in every case.
Disclaimer: I just hacked this together, quality (or even compilation) not guaranteed. But you get the idea... :)
我没有看到您的第二个示例中实现了
接口
,但我相信它是存在的。我将添加到您的界面(或创建一个子界面),其中有一个getResult()
方法。这对示例 1 和 2 都有帮助。您不需要在示例 1 中使用包装器,因为您可以定义
getResult()
方法来返回所需的结果。在示例 2 中,由于getResult()
是界面的一部分,因此没有“需要了解”的函数。我的偏好是创建一个新类,除非该类的每个变体仅使用一次。在这种情况下,我会匿名内联它。
I do not see an
interface
being implemented in your second example, but I believe it is there. I would add to your interface (or make a sub interface) that has agetResult()
method on it.That would help both example 1 and 2. You would not need a wrapper in 1, because you can define the
getResult()
method to return the result you want. In example 2, becausegetResult()
is a part of your interface, there is no function that you 'need to know'.My preference would be to create a new class, unless each variation of the class is only going to be used once. In which case I would inline it anonymously.
从更干净设计的角度来看,出于与您已经陈述的相同的确切原因,第二种方法是更可取的。
在正常的 TDD 周期中,我会从一个匿名类开始,稍后再对其进行重构。但是,如果仅在该一个地方需要访问者,并且其复杂性与您在示例中提供的内容相匹配(即不复杂),那么我会将其悬置并重构为稍后如果需要(例如出现另一个用例,访问者/周围类的复杂性增加)。
From the perspective of a cleaner design, the second approach is preferrable for the same exact reasons you've already stated.
In a normal TDD cycle I would start off with an anonymous class and refactored it out a bit later. However, if the visitor would only be needed in that one place and its complexity would match that of what you've provided in the example (i.e. not complex), I would have left it hanging and refactor to a separate class later if needed (e.g. another use case appeared, complexity of the visitor/surrounding class increased).
我建议使用第二种方法。让访问者处于成熟的类中也可以达到文档和干净代码的目的。我不同意您提到的该方法的缺点。假设你有一个数组列表,并且你没有向其中添加任何元素并执行 get 操作,那么你肯定会得到一个 null,但这并不意味着它一定是错误的。
I would recommend using the second approach. Having the visitor in its full fledged class also serves the purpose of documentation and clean code. I do not agree with the cons that you have mentioned with the approach. Say you have an arraylist, and you don't add any element to it and do a get, surely you will get a null but that doesn't mean that it is necessarily wrong.
访问者模式的要点之一是允许多种访问者类型。如果你创建一个匿名类,你就打破了这个模式。
您应该将
accept
方法更改为由于您将
this
传递给访问者,this
是被访问的对象,访问者可以访问根据标准访问规则访问对象的属性。One of the points of the visitor pattern is to allow for multiple visitor types. If you create an anonymous class, you are kind of breaking the pattern.
You should change your
accept
method to beSince you pass
this
into the visitor,this
being the object that is visited, the visitor can access the object's property according to the standard access rules.