选择设计模式时的困境

jad*_*k12 5 java oop inheritance design-patterns factory

我正在寻找一个我正在研究的项目的设计模式,并想知道我是否可以得到一些输入.


背景

名为Ranker的接口定义如下.

interface Ranker<T> {
    public void rank(List<T> item);
}
Run Code Online (Sandbox Code Playgroud)

Ranker的每个实现都可以有多个Rankers作为成员变量,每个成员Rankers都可以有多个Rankers作为其成员变量.你明白了.排名者可以使用其他排名来帮助排名它的项目.

在我的特定情况下,我有27个不同的Rankers,每个Rankers都定义了不同的 dataType.其中一个Rankers将使用其中26个最终能够对这些项目进行排名.

RankerForTypeA
  RaknerForTypeB 
    RankerForTypeC
    RankerForTypeD
    RankerForTypeE
  RankerForTypeF
    RankerForTypeG
      RankerForTypeH
        RankerForTypeI 
      RankerForTypeJ
  RankerForTypeK
  ...
Run Code Online (Sandbox Code Playgroud)

意思是,RankerTypeA具有RankerTypeB,RankerTypeF和RankerTypeK作为成员并且需要它们以便适当地对A类项进行排序.


问题

每个Rankers的实例化逻辑都不同.我想使用一些帮助我创建这些助手的模块/工厂/等.我最终需要它来创建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) {
          ...
      }
 }
Run Code Online (Sandbox Code Playgroud)

这有点棘手.我想让人们轻松地说使用他们认为合适的定制助手.回到我之前绘制的层次结构,让我们说对于TypeB,人们想要使用CustomRankerForTypeB而不是RankerForTypeB,他们应该很容易.所以在这种情况下,层次结构看起来像

RankerForTypeA
  CustomRankerForTypeB 
    ...
  RankerForTypeF
    RankerForTypeG
      RankerForTypeH
        RankerForTypeI 
      RankerForTypeJ
  RankerForTypeK
  ...
Run Code Online (Sandbox Code Playgroud)

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

所以,对于这种情况,我有类似的想法.

 public class CustomRankerFactory extends DefaultRankerFactory  {

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

 }
Run Code Online (Sandbox Code Playgroud)

然而,这个解决方案对我来说似乎并不合适,因为它在很大程度上依赖于继承.CustomRankerFactory 不一定是 DefaultRankerFactory,因此该方法似乎不正确.此外,这里的方法似乎不像工厂模式,即使我继续回到那个术语.我正在寻找有关定义"工厂"的最佳方法的帮助,以便它是模块化的并且易于扩展.对所有形式的输入开放.

Jus*_*ano 1

如果您想避免自定义RankerFactory实现继承自DefaultRankerFactory,您可以创建一个抽象基类 (ABC), 和 都CustomRankerFactory继承自该抽象基DefaultRankerFactory类,其中 和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...
    }
}
Run Code Online (Sandbox Code Playgroud)

这实际上并不会改变您当前表现出的行为,但它会DefaultRankerFactoryCustomRankerFactory层次结构中删除该行为。因此,层次结构现在显示为CustomRankerFactoryis anAbstractRankerFactory而不是CustomRankerFactoryis a DefaultRankerFactory


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

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

public void forEach(Consumer<? super T> consumer);
Run Code Online (Sandbox Code Playgroud)

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

该关键字允许接口提供由所有实现类使用的默认实现,除非实现类手动覆盖默认实现。例如,forEachin的 JDK 10 实现Iterable<T>是:

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

实际上,这意味着所有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...
    }
}
Run Code Online (Sandbox Code Playgroud)

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