我正在为正在从事的项目选择设计模式,并且想知道是否可以得到一些建议。
背景
名为 Ranker 的界面定义如下。
interface Ranker<T> {
public void rank(List<T> item);
}
Ranker的每个实现都可以具有多个Rankers作为成员变量,并且每个成员Rankers可以具有多个Rankers作为其成员变量。你明白了。排名者可以使用另一个排名者来帮助对其项目进行排名。
在我的特定情况下,我有27个不同的Rankers定义了每个,分别对不同数据类型进行Rank。其中一个等级将使用其中的26个等级来最终对商品进行排名。
RankerForTypeA
RaknerForTypeB
RankerForTypeC
RankerForTypeD
RankerForTypeE
RankerForTypeF
RankerForTypeG
RankerForTypeH
RankerForTypeI
RankerForTypeJ
RankerForTypeK
...
含义, RankerTypeA 具有RankerTypeB,RangererTypeF和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) {
...
}
}
这是棘手的部分。我想让人们容易地说,在他们认为合适的地方使用自定义排名。回到我之前绘制的层次结构,假设对于 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,因此这种方法似乎不正确。另外,即使我一直回到那个术语,这里的方法也不像是工厂模式。我正在寻求有关定义“工厂”的最佳方法的帮助,以使其模块化且易于扩展。打开所有形式的输入。
答案 0 :(得分:1)
不久前,我解决了类似的问题。解决方案是这样的:
public class RankerFactory {
public virtual Ranker<A> getRankerForA(paramsForA);
public virtual Ranker<B> getRankerForB(paramsForB);
public virtual Ranker<C> getRankerForC(paramsForC);
//...
}
制作CustomRankerForTypeB
的人可以轻松地从RankerFactory
继承并使用返回getRankerForB
的方法覆盖CustomRankerForTypeB
。各种各样的东西都采用了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(); }
//...
}
这也与传递的工厂堆叠在一起(根据我的经验,您希望每个模块的工厂的生产时间减少一半)。
答案 1 :(得分:1)
如果要避免自定义RankerFactory
实现从DefaultRankerFactory
继承,可以创建CustomRankerFactory
和DefaultRankerFactory
都继承的抽象基类(ABC), 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
而不是CustomRankerFactory
是DefaultRankerFactory
。
下一个示例的警告:这不一定是“最佳”设计方法,但它是有效的方法,并且可能适合您在特定情况下的需求。通常,创建一个ABC代替是一个好主意。本质上,请谨慎使用以下技术。
在Java 8中,现有接口增加了一些功能,从而破坏了Java库和实现这些接口的用户定义类中的现有功能。例如,Iterable<T>
界面添加了以下方法:
public void forEach(Consumer<? super T> consumer);
这将需要更新Iterable<T>
接口的所有实现,包括在用户定义的类中(不仅仅是在标准Java库中)。 Java团队认为这对开发人员来说负担太大(实质上会破坏大多数现有的Java代码),但是他们仍然想添加重要的功能,例如功能性forEach
方法,因此他们妥协并向Java添加了default
关键字。
此关键字允许接口提供所有实现类都使用的默认实现,除非实现类手动覆盖默认实现。例如,forEach
中Iterable<T>
的JDK 10实现是:
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
实现应仅实现(即覆盖)他们想要自定义行为的那些方法,而不是将所有方法实现为是实现接口的常规做法。