Java 访问者设计模式意图:误导还是我遗漏了什么?
在四人帮的参考书《可重用面向对象软件的设计模式元素》中,访问者模式的意图解释如下: 表示要在对象结构的元素上执行的操作。Visitor允许您定义新操作,而无需更改其操作的元素的类 我读到的关于访问者模式的另一个优点是: 在没有类的源代码的情况下添加新操作 我在谷歌上做了一次深度搜索,但没有找到任何例子来说明如何做到这一点。 让我们举一个简单的例子:Java 访问者设计模式意图:误导还是我遗漏了什么?,java,design-patterns,extensibility,visitor,Java,Design Patterns,Extensibility,Visitor,在四人帮的参考书《可重用面向对象软件的设计模式元素》中,访问者模式的意图解释如下: 表示要在对象结构的元素上执行的操作。Visitor允许您定义新操作,而无需更改其操作的元素的类 我读到的关于访问者模式的另一个优点是: 在没有类的源代码的情况下添加新操作 我在谷歌上做了一次深度搜索,但没有找到任何例子来说明如何做到这一点。 让我们举一个简单的例子: public interface MyInterface { public void myMethod(); } public class
public interface MyInterface {
public void myMethod();
}
public class MyClassA implements MyInterface {
/* (non-Javadoc)
* @see com.mycomp.tutorials.designpattern.behavorials.MyInterface#myMethodA()
*/
public void myMethod() {
System.out.println("myMethodA implemented in MyClassA");
}
}
public class MyClassB implements MyInterface {
/* (non-Javadoc)
* @see com.mycomp.tutorials.designpattern.behavorials.MyInterface#myMethodA()
*/
public void myMethod() {
System.out.println("myMethod implemented in MyClassB");
}
}
那么,我如何使用访问者模式向这个类层次结构添加一个新方法
myNewMethod()。该模式的动机不是向外部类添加功能,而是将访问者中的功能本地化,否则这些功能将分散在几个类中,例如用于保存元素(参见示例)。假设您有一个消息类和两个子类Email和Sms
您可以对这两个类执行许多操作,如sendToOnePerson()
,sendToSeveralPeople()
。但是您可能不希望将这些方法直接放在Email和Sms类中,因为它将它们紧密地耦合到SMTP/电话系统。您还希望能够在将来添加其他操作,如forward()或delete()或其他任何操作。因此,您可以使用的第一个实现是
public void delete(Message message) {
if (message instanceof Email) {
deleteEmail(Email) message);
}
else if (message instanceof Sms) {
deleteSms((Sms) message);
}
}
但这很难看:它不是面向对象的,如果出现新的VoiceMessage子类,它将失败
另一种方法是使用访问者模式
public interface MessageVisitor {
void visitEmail(Email email);
void visitSms(Sms sms);
}
public abstract class Message {
public void accept(MessageVisitor visitor);
}
public class Email extends Message {
@Override
public void accept(MessageVisitor visitor) {
visitor.visitEmail(this);
}
}
public class Sms extends Message {
@Override
public void accept(MessageVisitor visitor) {
visitor.visitSms(this);
}
}
这样,要实现send(),您只需要一个MessageVisitor实现,它可以发送电子邮件和短信:
SendMessageVisitor visitor = new SendMessageVisitor();
message.accept(visitor);
如果引入一个新的delete()操作,则根本不必接触消息类。您只需要一个DeleteMessageVisitor:
DeleteMessageVisitor visitor = new DeleteMessageVisitor();
message.accept(visitor);
因此,基本上,这有点像通过不实际修改消息类而向消息类添加多态方法。您的示例不是访问者模式。这只是继承
访问者模式首先需要访问者界面
interface ThingVisitor {
void visit(ThingA a);
void visit(ThingB b);
}
现在您需要一个接口:
interface Thing {
void accept(ThingVisitor visitor);
}
例如,ThingA
的实现是
class ThingA implements Thing {
public void accept(final ThingVisitor visitor) {
visitor.visit(this);
}
}
现在,您可以看到处理事物类型的逻辑包含在ThingVisitor
访问者模式的快速描述的实现中。
需要修改的类都必须实现“accept”方法。客户机调用此accept方法对该类族执行一些新操作,从而扩展其功能。客户端可以使用这个one-accept方法,通过为每个特定操作传入不同的visitor类来执行各种新操作。visitor类包含多个重写的visit方法,这些方法定义了如何为族中的每个类实现相同的特定操作。这些访问方法传递了一个要在其上工作的实例我认为您的接口应该有一个完全接受访问者的方法。您的类应该知道,可能会有访问者改变行为。访客设计模式并不是一个可以从外部改变行为的魔弹,你不能。您需要更改这些类以拥有accept方法。您可以在没有源代码的类中添加新操作。。。假设这些类已经用accept方法编译过。你的例子非常清楚。谢谢让我们以它为基础。但是假设您不是元素类(Message、Email、Sms)的设计者,并且您无权访问元素类的源代码。您仍然能够使用visitor设计模式“向消息类库添加方法”吗?你会怎么做?不,你不会的。这必须是一个设计决策