用于分析实例组合的设计模式?

时间:2016-02-22 10:58:41

标签: java arrays design-patterns match instance

我正在寻找有关可用于解释不同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类引用开发了一个可行的解决方案,但是很明显,在我的开发之后,我的一些模式将依赖于实例数据成员,而其他模式则不会。我希望模式具有灵活性,因此它们可能不必依赖于数组中的特定排序,或忽略多余的元素,或检测重复。

是否有适合这些要求的广泛使用的方法?

3 个答案:

答案 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 (这将使你们中间更多的人更容易受到影响);它可以是CakeSalad甚至是Colonoscopy;因此,我们有义务从基类规范中抽象出分析逻辑,以改善责任分离。这种方法激励我们开发一套适用于各种不同应用的通用工具,而不仅仅是我的想象吧。其次,它使我们能够处理自动装箱的Java Primitives或Java Bean,而无需构建任何接口。这种方法的另一个令人满意的副产品是它能够更清晰地显示整个Cocktails模式;没有'隐藏'课堂电话之间的互动。

因此,我决定将整个方法形式化为两个通用实体之间的有限交互,而不是处理不同Ingredient之间的关系; AliceBob。我决定将这些互动称为 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接口的实现者。您会在下面看到我们定义IIngredientRum和eugh ... Vodka等内容。我还为GinIIngredient定义了一个额外的扩展程序;这列举了呼叫者选择的某种成分的勺子数量。 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的这种基本形式来检测特定的类类型。

作为一个基本实现,下面我将演示如何检测某个实例是否是特定类型的BobCocktails 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本身,并且通常可以嵌套以提高代码内聚力。