【问题标题】:How to avoid empty visit functions in visitor pattern?如何避免访问者模式中的空访问函数?
【发布时间】:2022-10-04 17:29:28
【问题描述】:

我有以下用例。我有一个限制接口,需要从依赖项中填充其成员,进行验证。这些方法适用于所有实现,因此到目前为止都很好。某些限制需要稍后进行其他验证。在主函数中,我想遍历每个限制并以一般方式调用方法,而不是使用 instanceOf 然后调用。我认为这可能是here 提到的访问者模式的一个用例。现在我有以下课程。

interface Restriction() {
    void fillFields();
    void firstRoundValidation();
    void accept(SecondRoundValidationVisitor secondRoundValidationVisitor);
}
class RestrictionBasic implements Restriction {
    Field field;

    // Inject dependencies

    @Override
    void fillFields() {
        // Get field from dependencies
    }

    void firstRoundValidation() {
        // Implement
    }

    @void accept(SecondRoundValidationVisitor secondRoundValidationVisitor) {
        secondRoundValidationVisitor.visitRestrictionBasic(this);
    }
}
class RestrictionAdvanced implements Restriction {

    // Same as above except below function.

    @void accept(SecondRoundValidationVisitor secondRoundValidationVisitor) {
        secondRoundValidationVisitor.visitRestrictionAdvanced(this);
    }
}
interface ValidationVisitor {
    void visitRestriction(RestrictionBasic restrictionBasic);
    void visitRestriction(RestrictionAdvanced restrictionAdvanced);
}
class SecondRoundValidationVisitor implements ValidationVisitor {
    @Override
    void visitRestriction(RestrictionBasic restrictionBasic) {
        // Empty function
    }

    @Override
    void visitRestriction(RestrictionAdvanced restrictionAdvanced) {
        // Perform second level of validation
    }
}
class Main() {
    List<Restriction> restrictionList = new ArrayList();
    ValidationVisitor validationVisitor = new SecondRoundValidationVisitor();
    for (restriction : restrictionList) {
        restriction.accept(validationVisitor)
    }
}

你能告诉我这种方法是否有任何问题吗?还有另一种方法,可以将 getSecondValidationNeeded() 添加到接口中,并在此基础上调用带有空正文默认值的 secondValidation。但这不遵循接口隔离原则。我的疑问是访问者模式如何解决这个问题?即使在访问者模式中,也只有一个界面,并且即使只有部分访问者具有非空访问功能,也会在基本界面中添加接受。

【问题讨论】:

    标签: java oop inheritance design-patterns visitor-pattern


    【解决方案1】:

    访问者模式使用方法重载来选择合适的实现。在wiki example 中可以看到:

    interface CarElementVisitor {
        void visit(Body body);
        void visit(Car car);
        void visit(Engine engine);
        void visit(Wheel wheel);
    }
    

    所以我会编辑界面ValidationVisitor

    interface ValidationVisitor {
        void visitRestrictionBasic(RestrictionBasic restrictionBasic);
        void visitRestrictionAdvanced(RestrictionAdvanced restrictionAdvanced);
    }
    

    对此:

    public interface ValidationVisitor
    {
        void VisitRestriction(RestrictionBasic restrictionBasic);
        
        void VisitRestriction(RestrictionAdvanced restrictionAdvanced);
    }
    

    所以我们创建了具有不同重载的VisitRestriction()

    为什么?如果您不知道对象的类型?您可能需要找出Restriction 的真实类型,然后调用VisitRestrictionBasicVisitRestrictionAdvanced。我强烈建议您阅读这个非常好的答案What's the point of the accept method?

    【讨论】:

    • 对不起,我不明白。我已经在我的问题中只给出了这个或以下内容,对吗? ``` interface ValidationVisitor { void visitRestrictionBasic(RestrictionBasic restrictBasic);无效访问限制高级(限制高级限制高级); }```
    • @GopikrishnaKS 请看我更新的答案
    • 哦,对不起,我错过了使用相同的重载函数,而是使用了单独的函数。感谢您的关注,您能否告诉我们整体方法中是否存在其他问题以及其他方法更好?
    • 需要空函数在界面中添加accept(SecondRoundValidationVisitor secondRoundValidationVisitor)对吗?这是在不使用 instanceOf 的情况下迭代和调用某些类中不存在的特定函数所必需的。
    • @GopikrishnaKS 所以我只有一项改进。请看我更新的答案。谢谢!
    【解决方案2】:

    模式方面,我认为没有问题。 visitRestrictionBasic 只是空的,因为显然您没有基本限制的第二轮验证。这是业务规则,而不是设计缺陷。如果您后来决定确实需要对基本限制进行第二轮验证,您就知道可以在哪里添加它。

    除此之外,整个设置可能有点矫枉过正。从简单开始通常是好的。但是我不知道您的完整域和用例,因此无法判断是否是这种情况。

    编辑:为了评估您的方法,我们应该获得更多背景信息并退后一步了解问题。到目前为止,我所了解的是有几种限制类型具有以下特征:

    • 每个限制都有一组固定的依赖项
    • 限制将这些依赖项中的值提取到其字段中
    • 每个限制对这些字段执行两轮验证
    • 在每个限制类型中实施第一轮验证
    • 第二轮验证也是针对每个限制类型的,但以单独访问者的形式实现

    我不清楚第一轮和第二轮验证之间的根本区别。如果我理解正确,它们都有针对每种限制类型的特定验证代码。如果不是,并且基本验证器只在第一轮使用,高级验证器只在第二轮使用,那么模型可能会被简化。在这种情况下,第一轮 = 基础,第二轮 = 高级......

    【讨论】:

    • 感谢您的回答,能否请您说明为什么您认为它是矫枉过正的,您的意思是如果 if 条件的数量很少,则可以使用 instanceOf 并且只有在 if 条件很多时才需要此访问者模式?
    • 不,我认为您不应该使用 instanceOf 来解决这个问题 - 这是一种糟糕的代码气味。但我不知道你到底想解决哪个问题。如果域很简单并且案例数量很少,则可以使用更少的代码结构来实现您想要的。要知道这一点,我需要更多的背景信息。
    • 谢谢,你能告诉你究竟需要什么更多信息吗?我以为我在问题中添加了所需的信息,即遍历接口对象列表并为每个对象调用一个函数,但这个函数仅适用于某些实现,而不是全部。
    • 您的问题非常广泛:“这种方法有什么问题吗?”。要知道这种方法是否好,我们需要知道我们正在解决哪个问题。您没有向我们提出问题,而只是提出了您提出的解决方案。那么有多少种限制类型呢?他们在做什么?他们有什么不同?为什么第一轮验证是完全通用的,而第二轮是针对每个限制类型的?第一轮/第二轮的流程还有待商榷,还是已经确定了?是否可以更好地提取注入/调用依赖项和验证的问题?
    猜你喜欢
    • 1970-01-01
    • 2013-03-02
    • 1970-01-01
    • 1970-01-01
    • 2022-12-04
    • 1970-01-01
    • 1970-01-01
    • 2023-04-05
    • 2021-09-08
    相关资源
    最近更新 更多