【问题标题】:visitor pattern against conditionals?访问者模式反对条件?
【发布时间】:2009-08-09 11:03:27
【问题描述】:

我似乎没有在访问者模式的使用场景中找到这一点(或者我可能不明白)。它也不是分层的。

让我们使用一个身份验证示例。 UserAuthenticator 对用户提供的凭据进行身份验证。它返回一个结果对象。结果对象包含身份验证的结果:身份验证成功,未成功,因为未找到用户名,未成功,因为使用了非法字符等。客户端代码可能会求助于条件来处理此问题。 在伪代码中:

AuthResult = Userauthenticator.authenticate(Username, Password)
if AuthResult.isAuthenticated: do something
else if AuthResult.AuthFailedBecauseUsernameNotFound: do something else
else if etc...

访客模式适合这里吗? :

Authresult.acceptVisitor(AuthVisitor)

Authresult 然后根据结果调用 AuthVisitor 上的方法:

AuthVisitor.handleNotAuthenticatedBecauseUsernameNotFound

【问题讨论】:

    标签: oop design-patterns visitor-pattern conditional


    【解决方案1】:

    我不建议将模式用于它们不适合的意图。

    the visitor patterns 的意图是:

    • 表示要对对象结构的元素执行的操作。 Visitor 可让您定义新操作,而无需更改其操作的元素的类。
    • 恢复丢失类型信息的经典技术。
    • 根据两个对象的类型做正确的事。
    • 双重调度

    如果您计划执行各种身份验证方法,此解决方案会很有用,但如果您打算只执行一种,则无论如何您都必须使用条件。

    【讨论】:

    • 我不同意不使用它们不适合的东西。有些东西可能会解决一个问题,但它并不打算这样做。如果访问者模式很好地解决了我的问题,我为什么不使用它呢?那么问题就变成了:这个解决方案是一个好的解决方案吗?没有人以这种方式使用它并不意味着它是一个糟糕的解决方案,尽管它可能暗示着这个方向。更重要的是为什么它是一个好的或坏的解决方案。如果 McGyver 采纳了你的建议,他就会失业。
    • 另一件事是:处理身份验证的方式与示例无关。我不明白为什么我的示例将 UserAuthenticator 限制为仅一种身份验证方式(例如,仅 LDAPUserAuthentication、OpenIdUserAuthentication 等)
    【解决方案2】:

    当您的数据不会随着您的行为而快速变化时,访问者是一种有价值的设计。一个典型的例子是解析树:

    • 您的类层次结构(您的数据)已冻结
    • 你的行为变化太大,你不想破坏你的类添加另一个虚拟方法

    我不认为访问者在这里是一个有价值的解决方案,因为每次添加 AuthResult 的子类都会破坏访问者。

    Visitor 是关于双调度的交易封装

    您可以尝试类似的方法:

    interface Handler {
    
        void onUsernameNotFound();
    
        void onWrongPassword();
    
        void authOk();
    }
    
    interface Authenticator {
        void authenticate(String username, String password, Handler handler);  
    }   
    
    class SimpleAuthenticator implements Authetnciator {
    
        void authenticate(String username, String password, Handler handler) {
            if (username.equals("dfa")) {
                if (password.equals("I'm1337")) {
                    handler.authOk();
                } else {
                    handler.onWrongPassword();
                }
            } else {
                handler.onUsernameNotFound();
            }
        }
    }
    

    一些 Handler 策略:

    class FatalHandler implements Handler {
    
        void onUsernameNotFound() {
            throw new AuthError("auth failed");
        }
    
        void onWrongPassword() {
            throw new AuthError("auth failed");
        }
    
        void authOk() {
            /* do something */
        }   
    }
    

    和:

    class DebugHandler implements Handler {
    
        void onUsernameNotFound() {
            System.out.println("wrong username");
        }
    
        void onWrongPassword() {
            System.out.println("wrong password");
        }
    
        void authOk() {
            System.out.println("ok");
        }   
    }
    

    现在您可以在 Handlers 中封装错误处理和操作,这比 Visitor 的代码少得多,因为您实际上不需要在这里进行双重调度。

    【讨论】:

    • 我会使用这种设计。但是,有一个观察结果:您评论说访问者不合适,因为 AuthResult 的每个新子类都需要更改访问者。这里 Handler 上的方法对应不同的结果,实际上是 AuthResult 的不同子类。相同的耦合关系...新的可能结果 Handler 接口和所有实现都发生了很大变化。鉴于我们有两个维度的可变性,Auth + Handler,我们实际上距离 Double Dispatch 要求有多远?
    • 我一定错过了什么。为什么访问者的 Auth 子类会失败?可能有一个实现acceptVisitor 的抽象基类。我看到一个策略在这里是如何工作的,但在某种程度上,一个策略与访问者恕我直言,除了它不做层次结构。
    • @djna: 访问者重复了逻辑,因为您需要一个 if-elseif 链来构建正确的具体 AuthResponse 类...然后您需要另一个 if-elseif 链进行调度
    • @koen:尝试编写实际代码,让我谈谈你的实现;每次您想为您的 authresponse 添加另一个“案例类”时,您必须更改可访问的调度代码
    猜你喜欢
    • 1970-01-01
    • 2022-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-20
    • 1970-01-01
    相关资源
    最近更新 更多