【问题标题】:Factory Design Pattern - defining methods in subclasses工厂设计模式 - 在子类中定义方法
【发布时间】:2018-06-14 12:42:08
【问题描述】:

我正在实现一个工厂,这是一个负责跨应用程序管理令牌的类。我将在这个简化示例之后解释我面临的问题:

假设我们有我们的工厂类:

TokenManagerFactory.java:

public class TokenManagerFactory {

    public static TokenManager create(String tokenType)
    {
        if ("JWT".equals(tokenType))
            return new JwtTokenManagerImpl();

        return null;
    }


}

然后是我们的abstract interface

public abstract interface TokenManager {
    public  String       generateToken();
    public  boolean      verifyToken();
}

最后是实现JwtTokenManagerImpl

public class JwtTokenManagerImpl implements TokenManager {
    //..Implementation of methods defined in interface (generateToken() and 
    //  verifyToken())

    public String aMethodNotDefinedInInterface() {
        return "A very cool String";
    }
}

现在我们要在我们的main 中创建一个 JwtTokenManager 实例:

main {

    TokenManager tm = TokenManagerFactory.create("JWT");
    tm.aMethodNotDefinedInInterface(); // <-- Compilation error.

}

方法 aMethodNotDefinedInInterface() 未为该类型定义 令牌管理器

如何调整此设计模式,以免出现此错误?在进行此类调用时向下转换似乎是一个苛刻的解决方案,我可以进行更高级别的调整来适应这种情况吗?

谢谢。


我标记了我最终使用的解决方案。

【问题讨论】:

  • aMethodNotDefinedInInterface 是做什么的?额外的初始化?
  • 为什么main 需要知道具体的类型? JwtTokenManagerImpl 是否打破单一职责? aMethodNotDefinedInInterface()的目的是什么?
  • 为什么要在那里调用实例特定的方法?它以某种方式使工厂变得无用,因为它存在于从具体实现中抽象出来。

标签: java oop design-patterns factory-pattern factory-method


【解决方案1】:

如何调整此设计模式,以免出现此错误?

您必须做出选择:要么使用基本公共类型以统一的方式从基本公共类型的 API 操作所有子类,要么将其转换为特定类型以便能够调用子类的特定方法.

解决您问题的一些想法:

  • 在界面中添加方法。
    如果某些实现需要该方法但并非所有实现都需要该方法,则可以使用默认实现将其添加到接口中(例如,抛出UnsupportedOperationException)。您可以在想要支持它的子类中覆盖它。
    它会起作用,但也会让你的代码更脆弱,因为异常只会在运行时抛出。

  • 提供一个额外的工厂方法,在其声明中返回具体的子类。

  • 或者作为替代丰富实际方法以返回由调用程序返回中指定的目标类型推断的泛型类型。它不是类型安全的,但可以避免显式转换。

它会给出类似的东西:

@SuppressWarnings("unchecked")
public static <T extends TokenManager> T create(String tokenType) {
    if ("JWT".equals(tokenType)) {
        return (T) new JwtTokenManagerImpl();
    }
    return null;
}

你调用的:

JwtTokenManagerImpl token = create("JWT");
  • 如果符合您的需要,请使用装饰器模式来丰富某些对象的行为。您应该依赖装饰器将丰富的TokenManager 中的常用方法。
    你可以这样写:

TokenManager tm = new TokenFooDecorator(TokenManagerFactory.create("JWT"));

【讨论】:

    【解决方案2】:

    您可以将TokenManagerFactory 改为接受接口吗?

    public interface JwtTokenManager extends TokenManager {
        String aMethodNotDefinedInInterface();
    }
    
    public class TokenManagerFactory {
    
        public static <T extends TokenManager> T create(Class<T> managerInterface) {
            if (managerInterface == JwtTokenManager.class) {
                return (T) new JwtTokenManagerImpl();
            }
    
            return null;
        }
    }
    

    那么你使用工厂的地方可能是这样的:

    public static void main(String[] args) {
        JwtTokenManager tm = TokenManagerFactory.create(JwtTokenManager.class);
        tm.aMethodNotDefinedInInterface();
    }
    

    【讨论】:

    • 谢谢。我最终使用了这个解决方案:)
    【解决方案3】:

    如果所有 Token 管理器都需要aMethodNotDefinedInInterface(),则应将其添加到界面中。


    否则,这表明您需要为每个令牌管理器使用不同的流程,在这种情况下,您可能需要使用 Bridge 设计模式。

    在这种情况下,Implementor 层次结构将是令牌管理器,而 Abstraction 层次结构将由不同的流实现组成。
    然后你可以将你想要的流程与你想要的令牌实现相匹配。

    您仍然需要将方法添加到接口中,并且:

    1. 在不相关的地方添加一个空实现。
    2. 抛出UnsupportedOperationException异常,表示流/令牌管理器组合非法。

    【讨论】:

      【解决方案4】:

      我先说说你的设计有什么问题:

      这是您的代码:

      TokenManager tm = TokenManagerFactory.create("JWT");
      tm.aMethodNotDefinedInInterface(); // <-- Compilation error.
      

      问题是,当你只有一个可能的值时,为什么 create 方法需要一个令牌?您不妨总是返回具体类型。

      你会告诉我你可能想要这样做:

      String token = getTokenFromSomewhere(); // it may or may not be JWT
      TokenManager tm = TokenManagerFactory.create(token);
      tm.aMethodNotDefinedInInterface(); // <-- Compilation error.
      

      在那种情况下,你不知道你返回了什么类型的TokenManager,所以你不能在它上面调用方法,除非它在接口中。

      您现在可能会说您想要执行两种情况中的任何一种 - 使用已知令牌或未知令牌。在这种情况下,已知的令牌案例有点滥用工厂方法,因为它的设计是返回任何类型的令牌。您以两种不同的方式使用工厂。因此,您可以为特定令牌创建特定的工厂方法,或者只使用强制转换。

      【讨论】:

      • 工厂需要令牌类型,在这种情况下令牌类型是 JWT,它不是实际的令牌。但是您的解决方案很明确,谢谢:)
      • 关键是,如果你知道令牌(令牌是文字),那么传递令牌就没有意义:你还不如有另一种方法。如果您不知道令牌,则无法对返回值进行任何假设。
      【解决方案5】:

      接口中未定义的方法,不能使用接口类型调用。所以接口类型 TokenManager 的引用变量 tm 必须在调用特定方法(不在接口中,而是在特定类中)之前转换为子类型之一。

      if (tm instanceof JwtTokenManagerImpl ) {
        JwtTokenManagerImpl  jwtTm = (JwtTokenManagerImpl) tm;
        jwtTm.aMethodNotDefinedInInterface();
      }
      

      【讨论】:

        猜你喜欢
        • 2011-11-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-11
        • 1970-01-01
        • 1970-01-01
        • 2013-09-22
        • 2023-04-02
        相关资源
        最近更新 更多