选择设计模式时的困境

Dilemma while selecting a design pattern

我正在寻找 select 我正在从事的项目的设计模式,想知道我是否可以获得一些意见。


背景

一个叫做Ranker的接口定义如下。

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 的实例化逻辑是不同的。我想使用某种 Module/Factory/etc 来帮助我创建这些排名器。我最终需要它来创建 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 的实现,但是它会有很多重复的代码,因为 RankerFactory 的很多其他部分的实例化逻辑=39=]RankerForTypeA 类似。

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

 public class CustomRankerFactory extends DefaultRankerFactory  {

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

 }

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

不久前我解决了一个类似的问题。解决方案是这样的:

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

制作 CustomRankerForTypeB 的人可以轻松继承 RankerFactory 并使用返回 CustomRankerForTypeB 的方法覆盖 getRankerForB。各种各样的东西都采用 RankerFactory 类型的参数。提示:如果您在其他地方使用它的好地方,这将是依赖注入的好地方。你也可以使用穷人的依赖注入并做

public class RankerFactory {
    private static RankerFactory factory = new RankerFactory();
    public static RankerFactory get_RankerFactory() { return factory; }
    public static void set_RankerFactory(RankerFactory newFactory ){ factory = newFactory; }
    //...

如果您想要这种非常灵活的方式(这在这里可能是一把双刃剑),可以改为这样做

public class RankerFactory {
    // I can't quite do this as generics sorry for the duplication
    public class FactoryA { public virtual getRanker() { return new RankerA(); } }
    private FactoryA a;
    public FactoryA getFactoryForA() { return a; }
    public void setFactoryForA(FactoryA value) { a = value };
    // ...

    public Ranker<A> getRankerForA(paramsForA) { return a.getRanker(); }
    public Ranker<B> getRankerForB(paramsForB) { return b.getRanker(); }
    public Ranker<C> getRankerForC(paramsForC) { return c.getRanker(); }
    //...
}

这也与路过的工厂叠加(根据我的经验,你希望每个模块的工厂有一半的时间)。

如果您想避免自定义 RankerFactory 实现继承自 DefaultRankerFactory,您可以创建一个抽象基础 class (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...
    }
}

这实际上并没有改变您当前表现出的行为,但它从 CustomRankerFactory 层次结构中删除了 DefaultRankerFactory。因此,层次结构现在读取 CustomRankerFactory is an AbstractRankerFactory 而不是 CustomRankerFactory is a DefaultRankerFactory.


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

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

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

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

此关键字允许接口提供所有实现 classes 使用的默认实现,除非实现 class 手动覆盖默认实现。例如Iterable<T>forEach的JDK10实现是:

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 实现应该只实现(即覆盖)他们想要自定义行为的那些方法,而不是将所有方法实现为是实现接口的正常做法。