java - 选择设计模式时的困境

标签 java oop inheritance design-patterns factory

我想为我正在从事的项目选择一种设计模式,并且想知道我是否可以获得一些意见。


背景

名为Ranker 的接口(interface)定义如下。

interface Ranker<T> {
    public void rank(List<T> item);
}

Ranker的每个实现可以有多个Ranker作为成员变量,每个Ranker成员可以有多个Ranker作为他们的成员变量。你明白了。 Ranker 可以使用另一个 Ranker 来帮助对其项目进行排名。

在我的特殊情况下,我定义了 27 个不同的排序器,每个排序器都对一个不同数据类型进行排序。这些排序器中的 26 个将被其中一个排序器使用,最终能够对项目进行排序。

RankerForTypeA
  RaknerForTypeB 
    RankerForTypeC
    RankerForTypeD
    RankerForTypeE
  RankerForTypeF
    RankerForTypeG
      RankerForTypeH
        RankerForTypeI 
      RankerForTypeJ
  RankerForTypeK
  ...

意思是,RankerTypeA 有 RankerTypeB、RankerTypeF 和 RankerTypeK 作为成员,并且需要它们才能适本地对 A 类型的项目进行排名。


问题

每个Ranker 的实例化逻辑是不同的。我想使用某种模块/工厂/等来帮助我创建这些排名器。我最终需要它来创建 RankerForTypeA。我有这样的想法。

 interface RankerFactory {
      Ranker<A> getRankerForA(paramsForA);
      Ranker<B> getRankerForB(paramsForB);
      Ranker<C> getRankerForC(paramsForC);
      ...
 }

 public class DefaultRankerFactory implements RankerFactory  {
      Ranker<A> getRankerForA(paramsForA) {
          getRankerForB(paramsForB)
          ...
      }
      Ranker<B> getRankerForB(paramsForB) {
          getRankerForC(paramsForC)
          ...
      }
      Ranker<C> getRankerForC(paramsForC) {
          ...
      }
 }

这是有点棘手的部分。我想让人们更容易地让我们说在他们认为合适的地方使用自定义排名。回到我之前画的层次结构,假设对于TypeB,人们想使用CustomRankerForTypeB而不是RankerForTypeB,他们应该很容易能够到。所以在那种情况下,层次结构看起来像

RankerForTypeA
  CustomRankerForTypeB 
    ...
  RankerForTypeF
    RankerForTypeG
      RankerForTypeH
        RankerForTypeI 
      RankerForTypeJ
  RankerForTypeK
  ...

所以在这里,如果他们想创建RankerForTypeA,他们不能直接使用DefaultRankerFactory,因为它使用RankerForTypeB而不是 CustomRankerForTypeB。所以通常你会想,他们可以创建一个新的 RankerFactory 实现,但是它会有很多重复的代码,因为 RankerForTypeA 的很多其他部分的实例化逻辑 类似。

所以,对于这个场景,我有类似的想法。

 public class CustomRankerFactory extends DefaultRankerFactory  {

      Ranker<B> getRankerForB(paramsForB) {
          //Define instantiation logic for CustomRankerForTypeB
          ...
      }

 }

但是这个解决方案对我来说似乎并不合适,因为它严重依赖于继承。 CustomRankerFactory 不一定是 DefaultRankerFactory,因此该方法似乎不正确。另外,这里的方法看起来不像工厂模式,尽管我一直在回顾那个术语。我正在寻求有关定义“工厂”的最佳方法的帮助,以便它是模块化的并且易于扩展。对所有形式的输入开放。

最佳答案

如果你想避免自定义 RankerFactory实现继承自 DefaultRankerFactory ,您可以创建一个抽象基类 (ABC),这两个 CustomRankerFactoryDefaultRankerFactory继承,其中 DefaultRankerFactory不会覆盖任何默认行为。例如:

interface RankerFactory {
    Ranker<A> getRankerForA(paramsForA);
    Ranker<B> getRankerForB(paramsForB);
    Ranker<C> getRankerForC(paramsForC);
    // ...
}

public abstract class AbstractRankerFactory {

    public Ranker<A> getRankerForA(paramsForA) {
        // ...default behavior...
    }

    public Ranker<B> getRankerForB(paramsForB) {
        // ...default behavior...
    }

    public Ranker<C> getRankerForC(paramsForC) {
        // ...default behavior...
    }
}

public class DefaultRankerFactory extends AbstractRankerFactory {}

public class CustomRankerFactory extends AbstractRankerFactory {

    @Override
    public Ranker<C> getRankerForC(paramsForC) {
        // ...custom behavior...
    }
}

这实际上并没有改变您当前表现出的行为,但它删除了 DefaultRankerFactory来自 CustomRankerFactory等级制度。因此,层次结构现在显示为 CustomRankerFactory是一个 AbstractRankerFactory而不是 CustomRankerFactoryDefaultRankerFactory .


下一个示例的注意事项:这不一定是“最佳”设计方法,但它是一种有效的方法,可能适合您在特定情况下的需求。不过,一般来说,创建 ABC 是个好主意。基本上,请谨慎使用以下技术。

在 Java 8 中,对现有接口(interface)进行了添加,这些接口(interface)会破坏 Java 库和实现这些接口(interface)的用户定义类中的现有功能。例如,Iterable<T>接口(interface)添加如下方法:

public void forEach(Consumer<? super T> consumer);

这将需要 Iterable<T>所有实现接口(interface),包括在用户定义的类中(不仅仅是在标准 Java 库中)被更新。 Java 团队认为这给开发人员带来的负担太大了(这基本上会破坏大部分现有的 Java 代码),但他们仍然希望添加重要的功能,例如函数式 forEach。方法,所以他们妥协并添加了 default Java 的关键字。

此关键字允许接口(interface)提供所有实现类都使用的默认实现,除非实现类手动覆盖默认实现。例如,forEach 的 JDK 10 实现在Iterable<T>是:

default void forEach(Consumer<? super T> action) {
    Objects.requireNonNull(action);
    for (T t : this) {
        action.accept(t);
    }
}

在实践中,这意味着所有 Iterable<T>实现有 forEach添加方法而无需重写任何代码。对于您的排名系统,您可以利用这个 default关键字删除 ABC 并降低工厂层次结构的复杂性。例如:

interface RankerFactory {

    default Ranker<A> getRankerForA(paramsForA) {
        // ...default behavior...
    }

    default Ranker<B> getRankerForB(paramsForB) {
        // ...default behavior...
    }

    default Ranker<C> getRankerForC(paramsForC) {
        // ...default behavior...
    }

    // ...
}

public class DefaultRankerFactory implements RankerFactory {}

public class CustomRankerFactory implements RankerFactory {

    @Override
    public Ranker<C> getRankerForC(paramsForC) {
        // ...custom behavior...
    }
}

这确实降低了层次结构的复杂性,但请记住它使用了 default关键字用于最初并非预期的目的,这可能会导致客户端代码困惑。如果使用此方法,应记录所有默认行为都包含在界面中,并且自定义 RankerFactory实现应该只实现(即覆盖)他们想要自定义行为的那些方法,而不是像实现接口(interface)的正常做法那样实现所有方法。

关于java - 选择设计模式时的困境,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54085456/

相关文章:

java - 无法在 Windows 命令行上使用 json-simple

java - for 循环阻止我从数据库检索数据?

java - 每隔一分钟向数据库发送一次查询

java - 创建对象时"Non-static variable this cannot be referenced from a static context"

c++ - 我们可以覆盖继承类的纯虚函数吗?

java - 升级到 Spring 3 和 junit 11 - 运行测试后上下文变为 null。没有可用于第二次测试的 Autowiring bean

php - 像谷歌一样的分页背后的逻辑

java - 如何在使用什么时间单位时给出 "hint"?

C++ 类继承和运算符重载;运算符重载会被继承吗?

Java:父方法访问子类的静态变量?