Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 访问者设计模式意图:误导还是我遗漏了什么?_Java_Design Patterns_Extensibility_Visitor - Fatal编程技术网

Java 访问者设计模式意图:误导还是我遗漏了什么?

Java 访问者设计模式意图:误导还是我遗漏了什么?,java,design-patterns,extensibility,visitor,Java,Design Patterns,Extensibility,Visitor,在四人帮的参考书《可重用面向对象软件的设计模式元素》中,访问者模式的意图解释如下: 表示要在对象结构的元素上执行的操作。Visitor允许您定义新操作,而无需更改其操作的元素的类 我读到的关于访问者模式的另一个优点是: 在没有类的源代码的情况下添加新操作 我在谷歌上做了一次深度搜索,但没有找到任何例子来说明如何做到这一点。 让我们举一个简单的例子: public interface MyInterface { public void myMethod(); } public class

在四人帮的参考书《可重用面向对象软件的设计模式元素》中,访问者模式的意图解释如下:

表示要在对象结构的元素上执行的操作。Visitor允许您定义新操作,而无需更改其操作的元素的类

我读到的关于访问者模式的另一个优点是:

在没有类的源代码的情况下添加新操作

我在谷歌上做了一次深度搜索,但没有找到任何例子来说明如何做到这一点。 让我们举一个简单的例子:

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设计模式“向消息类库添加方法”吗?你会怎么做?不,你不会的。这必须是一个设计决策