我正在寻找有关可用于解释不同Object
个实例数组的设计模式的建议。理想情况下,这些模式定义将是静态定义的。
因此,作为一个例子,我正在寻找类似的东西:
Ingredient [] lIngredients = new Ingredient []{ new Lime (), new Soda (), new Sugar (), new Mint (), new Rum () };
Patterns.WHITE_RUSSIAN.isRecipe(lIngredients);//returns false
Patterns.MOJITO.isRecipe(lIngredients);//returns true
我设法使用Object
类引用开发了一个可行的解决方案,但是很明显,在我的开发之后,我的一些模式将依赖于实例数据成员,而其他模式则不会。我希望模式具有灵活性,因此它们可能不必依赖于数组中的特定排序,或忽略多余的元素,或检测重复。
是否有适合这些要求的广泛使用的方法?
答案 0 :(得分:1)
可能你可以使用这两个中的一个
来做到这一点interface Ingredient {
boolean belongsTo(Cocktail cocktail)
}
interface Cocktail {
boolean hasIngredient(Ingredient ingredient)
}
鸡尾酒本身可以是阵列或其他成分聚合
答案 1 :(得分:1)
当您具有要处理的类层次结构时,首选的设计模式是Visitor pattern。
public class Lime {
public void accept(IngredientVisitor visitor) {
visitor.visit(this);
}
}
public class Soda {
public void accept(IngredientVisitor visitor) {
visitor.visit(this);
}
}
public class MojitoVisitor extends IngredientVisitor {
public void visit(Lime lime) {
System.out.println("Visiting lime");
}
public void visit(Soda soda) {
System.out.println("Visiting soda");
}
}
编辑:我同意上面的解决方案会产生太多开销。然后我会选择像Hamcrest这样的匹配器,您可以在其中执行以下操作:
private Matcher mojitoMatcher = arrayContainingInAnyOrder(
instanceOf(Rum.class),
instanceOf(Mint.class),
instanceOf(SodaWater.class),
instanceOf(Lime.class),
instanceOf(Sugar.class)
);
public boolean isMojito(Ingredient[] ingredients) {
return mojitoMatcher.matches(ingredients);
}
答案 2 :(得分:0)
经过其他StackOverflow用户提供的精彩答案的一点思考和鼓励,我认为我偶然发现了一个合理的解决方案。
方案
我们想采用一系列Ingredient
系列解读可以从中复制的美味鸡尾酒配方系列。这些关系必须灵活,并且能够从我们的通用数组规范中访问数据成员。
技术
在这个应用程序中,我们将处理一组实例。在这方面,我们希望开发模式匹配代码,它充当聚合数据成员集的函数,而不是让数据成员自己定义配方的逻辑。原因是双重的;首先,Ingredient
不仅仅用于Cocktail
(这将使你们中间更多的人更容易受到影响);它可以是Cake
,Salad
甚至是Colonoscopy
;因此,我们有义务从基类规范中抽象出分析逻辑,以改善责任分离。这种方法激励我们开发一套适用于各种不同应用的通用工具,而不仅仅是我的想象吧。其次,它使我们能够处理自动装箱的Java Primitives或Java Bean,而无需构建任何接口。这种方法的另一个令人满意的副产品是它能够更清晰地显示整个Cocktails
模式;没有'隐藏'课堂电话之间的互动。
因此,我决定将整个方法形式化为两个通用实体之间的有限交互,而不是处理不同Ingredient
之间的关系; Alice
和Bob
。我决定将这些互动称为 Liason ,不仅因为我感到悲伤,而且还很孤单。
设计
首先,我构建了一个名为Interface
的{{1}}。我使用接口,因为这使设计中的现有类能够与模式匹配体系结构集成,而不会导致多重继承冲突。
ILiason
在/* Defines the basis of a ILiason. */
public interface ILiason<A, B> {
/* Defines whether a ILiason exists between Alice and Bob. */
public abstract boolean isLiason(final A pAlice);
/* Returns the source of comparison. */
public abstract B getBob();
}
中,我们定义了两个依赖于两种泛型类型的简单方法:ILiason
( Alice )和A
( Bob < /强>)。 B
将是我们希望与之比较的运行时集合或对象,A
将成为B
返回的比较的来源。在方法getBob()
中,我们将定义如何正确比较 Alice 和 Bob 的具体逻辑。
在isLiason(final A pAlice)
应用程序中,我决定将每个特定成分表示为Cocktails
接口的实现者。您会在下面看到我们定义IIngredient
,Rum
和eugh ... Vodka
等内容。我还为Gin
,IIngredient
定义了一个额外的扩展程序;这列举了呼叫者选择的某种成分的勺子数量。 ISpoonfuls
的具体实现在ISpoonfuls
类规范中定义。
`/ *基础成分界面。 * / 公共界面IIngredient {
IIngredient.Sugar
}`
从这个设计中可以清楚地看到,为了检查集合中是否存在某个/* Amount Interface. */
public static interface ISpoonfuls extends IIngredient {
/* Returns the Number of Teaspoons required for the concrete Ingredient. */
public abstract int getNumberOfTeaSpoons();
}
/* Define some example implementations. */
public static final class Rum implements IIngredient { };
public static final class Liquor implements IIngredient { };
public static final class Vodka implements IIngredient { };
public static final class Cream implements IIngredient { };
public static final class Gin implements IIngredient { };
public static final class Vermouth implements IIngredient { };
public static final class Orange implements IIngredient { };
public static final class Lime implements IIngredient { };
public static final class Soda implements IIngredient { };
public static final class Mint implements IIngredient { };
public static final class Sugar implements ISpoonfuls {
/* Member Variables. */
private final int mNumberOfTeaspoons;
/* Constructor. */
public Sugar(final int pNumberOfTeaspoons) {
/* Initialize Member Variables. */
this.mNumberOfTeaspoons = pNumberOfTeaspoons;
}
/* Getters. */
@Override public int getNumberOfTeaSpoons() { return this.mNumberOfTeaspoons; }
};
,我们希望测试它的类别。因此,我们将定义我们的第一个IIngredient
类型ILiason
:
Assignable
在抽象(不完整)/* A ILiason used to determine whether a given instance is assignable from a given class. */
public static abstract class Assignable<A> implements ILiason<A, Class<?>> {
/* Default Implementation Stub. */
public static final class Impl<A> extends Assignable<A> { private final Class<?> mBob; public Impl(final Class<?> pBob) { this.mBob = pBob; } @Override public final Class<?> getBob() { return this.mBob; } };
/* Determines whether Alice is an assignable form of Bob. */
@Override public final boolean isLiason(final A pAlice) {
/* Checks whether the source class of Alice is compatible with the concretely-defined Bob. */
return (this.getBob().isAssignableFrom(pAlice.getClass()));
}
};
类中,我们会看到我们定义Assignable
类型的通用规范,但我们断言Alice
必须是{ {1}}参考。在我们Bob
的具体定义中,我们使用Class<?>
返回的isLiason(final A pAlice)
引用和方法Class<?>
来调查Java是否可以检测{{1}之间的现有层次结构}和getBob()
。我们使用此指标作为isAssignableFrom(Class<?> c)
和Alice
之间是否存在现有联系的结果。在Bob
中,我们可以使用Alice
的这种基本形式来检测特定的类类型。
作为一个基本实现,下面我将演示如何检测某个实例是否是特定类型的Bob
:
Cocktails
ILiason
到目前为止,我们所取得的成就似乎是我们使用IIngredient
所取得的成果的大量开销,而且它是;但是当我们转向更复杂的关系时,这种好处很快就会显现出来,这种关系从根本上依赖于相同的架构。
我们设计的另一个要求是,对于给定的实例,我们要查询其实例成员以确定(new ILiason.Assignable<IIngredient>() { @Override public Class<?> getBob() { return Lime.class; } }).isLiason(Lime.class); // Returns true!
的有效性。为此,我们定义了(new ILiason.Assignable<IIngredient>() { @Override public Class<?> getBob() { return Lime.class; } }).isLiason(Mint.class); // Returns false!
的第二个实现者,即instanceof
。
到目前为止,我们所取得的成就似乎是我们使用ILiason
所取得的成果的大量开销,而且它是;但是当我们转向更复杂的关系时,这种好处很快就会显现出来,这种关系从根本上依赖于相同的架构。
我们设计的另一个要求是,对于给定的实例,我们要查询其实例成员以确定ILiason
的有效性。为此,我们定义了Intuition
的第二个实现者,即instanceof
。
ILiason
`
在ILiason
中,我们看到要计算Intuition
的状态,我们首先使用/* A ILiason which uses informed knowledge of a type to determine the state of a ILiason. */
public static abstract class Intuition<A, B> implements ILiason<A, Class<?>> { /*** TODO: to B **/
/* First, we determine if Alice is an instance of Bob. */ /** TODO: We suppress the unchecked type cast, however Assignable guarantees us the check. **/
@Override @SuppressWarnings("unchecked") public final boolean isLiason(final A pAlice) {
/* Determine if we can use intuitive processing via an Assignable. If we can't, return the default result. */
return (new ILiason.Assignable.Impl<A>(this.getBob()).isLiason(pAlice) ? this.onSupplyIntuition((B)pAlice) : false);
}
/* Defines concrete methodology towards handling alice in a class-specific manner. */
public abstract boolean onSupplyIntuition(final B pA);
};
测试可转让性;如果实现了这一点,我们就可以将类型转换Intuition
安全地转换为可分配类型,并允许具体实现者通过Liason
定义对实例的更密切分析。下面,我们使用它来检查ILiason.Assignable
的{{1}}:
Alice
通过这些定义,我们能够定义集合的通用和知情分析,并使用它们的结果来推断类的特定组合。例如,可以使用onSupplyIntuition
找到单个数组索引中给定NumberOfTeaspoons
的存在:
IIngredient.Sugar
使用类似方法,可以使用new ILiason.Intuition<IIngredient, Sugar>() {
/* Define the Number of Teaspoons of Sugar expected for a Mojito. (I made this up, it's probably a lot more.) */
@Override public final boolean onSupplyIntuition(final Sugar pSugar) { return (pSugar.getNumberOfTeaSpoons() == 2); }
/* Define the Searchable class reference. */
@Override public final Class<Sugar> getBob() { return Sugar.class; }
}; } }
处理ILiason
s数组对给定ILiason.Element
的适用性。在/* Determines whether a particular Liason is present within a specified Array, Alice. */
public static abstract class Element<A, B> implements ILiason<A[], ILiason<A, B>> {
/* Here we iterate through the entire specification of Alice in search of a matching Liason. */
@Override public final boolean isLiason(final A[] pAlice) {
/* Define the Search Metric. */
boolean lIsSupported = false;
/* Iterate Alice. */
for(int i = 0; i < pAlice.length && !lIsSupported; i++) {
/* Fetch Alice at this index. */
final A lAlice = pAlice[i];
/* Update the Search metric using Alice's Liason with Bob. */
lIsSupported |= this.getBob().isLiason(lAlice);
}
/* Return the Search Metric. */
return lIsSupported;
}
};
调用中定义ILiason
的顺序也定义了优先级:
Alice
使用这些工具,我们可以按如下方式定义不同的ILiason.Cohort
:
/ *定义Martini规范。 * /
ILiasons
然后,通过简单调用getBob()
/* Compares an array of Alice against an array of Liasons. Determines whether all Liasons are fully met. */
public static abstract class Cohort<A, B> implements ILiason<A[], ILiason<A[], B>[]> {
/* Iterates the array of Alice and returns true if all Liasons within the cohort are supported. */
@Override public final boolean isLiason(final A[] pAlice) {
/* Define the Search Metric. */
boolean lIsValid = true;
/* Fetch the associated Liasons. */
final ILiason<A[], B>[] lLiasons = this.getBob();
/* Iterate the Liasons whilst the delegation is Valid. */
for(int i = 0; i < lLiasons.length && lIsValid; i++) {
/* Fetch the Liason. */
final ILiason<A[], B> lLiason = lLiasons[i];
/* Update the search metric. */
lIsValid &= lLiason.isLiason(pAlice);
}
/* Return the search metric. */
return lIsValid;
}
};
方法,我们就能够在{{1}数组中测试是否存在适当的数据成员}}秒。
你有它!静态定义的模式定义,可以实现任意比较功能。
缺点
- 在创建新的Cocktail
定义时,编码开销非常大。由于模式可以为我们带来所有的灵活性,因此在简单性方面并不是很有效。
- 它很可能很慢。这里实现的大多数比较是针对每个private static final ILiason.Cohort<IIngredient, Class<?>> LIASON_COHORT_COCKTAIL_MARTINI = new ILiason.Cohort<IIngredient, Class<?>>() { @SuppressWarnings("unchecked") @Override public final ILiason<IIngredient[], Class<?>>[] getBob() { return ((ILiason<IIngredient[], Class<?>>[]) new ILiason<?, ?>[] {
/* Define the Dry-Gin. */
new ILiason.Element<IIngredient, Class<?>>() { @Override public final ILiason<IIngredient, Class<?>> getBob() { return new ILiason.Assignable<IIngredient>() { @Override public Class<?> getBob() { return Gin.class; } }; } },
new ILiason.Element<IIngredient, Class<?>>() { @Override public final ILiason<IIngredient, Class<?>> getBob() { return new ILiason.Assignable<IIngredient>() { @Override public Class<?> getBob() { return Vermouth.class; } }; } },
new ILiason.Element<IIngredient, Class<?>>() { @Override public final ILiason<IIngredient, Class<?>> getBob() { return new ILiason.Assignable<IIngredient>() { @Override public Class<?> getBob() { return Orange.class; } }; } },
}); } };
类型的2D阵列搜索。在简单的情况下,LIASON_COHORT_COCKTAIL_MARTINI
可能是更优选的选择。
- isLiason(final IIngredient[] pAlice)
定义中的责任重叠是可能的。幸运的是,核心IIngredient
类型是ILiason
本身,并且通常可以嵌套以提高代码内聚力。