简体   繁体   English

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

[英]Design patterns for analysing a combination of instances?

I'm looking for a recommendation on a design pattern which could be used to interpret an array of different Object instances. 我正在寻找有关设计模式的建议,该建议可用于解释一系列不同的Object实例。 Ideally, these pattern definitions would be statically defined. 理想情况下,这些模式定义将是静态定义的。

So, as an example, I'm looking for something along the lines of this: 因此,作为示例,我正在寻找类似的东西:

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 Patterns.WHITE_RUSSIAN.isRecipe(lIngredients);//returns false Patterns.MOJITO.isRecipe(lIngredients);//returns true

I managed to develop a working solution using Object class references, however it's clear that later on in my development some of my patterns will rely upon instance data members, whilst others will not. 我设法使用Object类引用开发了一个可行的解决方案,但是很明显,在以后的开发中,我的某些模式将依赖于实例数据成员,而其他模式则不会。 I'd like the patterns to be flexible, so they may not have to depend on a specific ordering in the array, or ignore superfluous elements, or detect duplicates. 我希望这些模式具有灵活性,因此它们可能不必依赖于数组中的特定顺序,也不必忽略多余的元素或检测重复项。

Are there any widely-used approaches that suit these requirements? 是否有满足这些要求的广泛使用的方法?

Probably, you can do it using one of those two 可能您可以使用这两种方法之一

interface Ingredient {
  boolean belongsTo(Cocktail cocktail)
}

interface Cocktail {
  boolean hasIngredient(Ingredient ingredient)
}

A Cocktail itself can be an array or other aggregation of Ingredients 鸡尾酒本身可以是成分的数组或其他集合

The preferred design pattern for when you have a hierarchy of classes to be processed is the Visitor pattern . 当您具有要处理的类的层次结构时,首选的设计模式是Visitor模式

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");
    }
}

EDIT: I agree solution above creates too much overhead. 编辑:我同意上述解决方案会产生过多的开销。 I'd then go with a Matcher like Hamcrest where you can do something like: 然后,我将与Hamcrest之类的Matcher一起使用,您可以在其中执行以下操作:

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);
}

After a little contemplation and encouragement from the wonderful answers offered by other StackOverflow users, I think I've stumbled upon a reasonable solution. 在从其他StackOverflow用户提供的精彩回答中得到一些思考和鼓励之后,我认为我偶然发现了一个合理的解决方案。

Scenario We'd like to take an array of Ingredient s and systematically interpret the range of delicious cocktail recipes that can be reproduced from them. 场景我们想采用一系列的Ingredient并系统地解释可以从中复制的美味鸡尾酒配方的范围。 The relationships must be flexible and capable of accessing data members from our generic array specifications. 这些关系必须是灵活的,并且能够访问我们通用数组规范中的数据成员。

Technical In this application, we'll be processing a collection of instances. 技术在此应用程序中,我们将处理实例的集合。 In this regard we'd like to develop pattern-matching code that acts as a function of aggregate set of data members, rather than have the data members themselves define the logic of a recipe. 在这方面,我们希望开发模式匹配代码,以作为数据成员集合集合的函数,而不是让数据成员自己定义配方的逻辑。 The reason for this is two-fold; 原因有两个。 firstly, an Ingredient is not just only ever used in just a Cocktail (which will pertube the more party-prone amongst you) ; 首先,一种Ingredient不仅仅用于Cocktail (它会使您中更容易参加派对) it could be in a Cake , a Salad or even an Colonoscopy ; 它可以是CakeSalad甚至Colonoscopy ; therefore we're obliged to abstract the analysis logic from the base class specifications to improve separation of responsibilities. 因此,我们有义务从基类规范中抽象出分析逻辑,以改善职责分离。 This approach inspires us to develop a set of generic tools that are applicable for a whole range of different applications, not just my imaginary bar. 这种方法激励我们开发一套通用工具,这些工具不仅适用于我的想象中的杆,而且适用于各种不同的应用程序。 Secondly, it enables us to process autoboxed Java Primitives or Java Beans without having to construct any interfaces. 其次,它使我们能够处理自动装箱的Java原语或Java Bean,而无需构造任何接口。 Another happy biproduct of this approach is that it enables much clearer visualization of the Cocktails pattern as a whole; 这种方法的另一个好处是,它可以使整个Cocktails模式更加清晰可见。 there's no 'hidden' interactions between class calls. 类调用之间没有“隐藏”交互。

So, rather than handling the relationships between different Ingredient s, I decided to formalise the entire approach as finite interactions between two generic entities; 因此,不是决定处理不同的Ingredient之间的关系,而是决定将整个方法形式化为两个通用实体之间的有限相互作用。 Alice and Bob . AliceBob I've decided to refer to these interactions as a Liason , not only because I'm just sad, but also lonely. 我决定将这些互动称为联络人 ,不仅因为我很伤心,而且很孤独。

Design First, I've constructed an Interface called ILiason . 设计首先,我构建了一个名为ILiasonInterface I use an interface because this enables existing classes in a design to integrate with the pattern matching architecture without causing multiple-inheritance conflicts. 我使用一个接口,是因为这样可以使设计中的现有类与模式匹配体系结构集成在一起,而不会引起多继承冲突。

/* 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(); }

In ILiason , we define two simple methods that are dependent on two generic types, A ( Alice ) and B ( Bob ). ILiason ,我们定义了两个简单的方法,它们依赖于两种通用类型,即AAlice )和BBob )。 A will be the runtime collection or Object we wish to compare against, and B will be the source of Comparison returned by getBob() . A将是我们希望与之进行比较的运行时集合或对象,而B将是getBob()返回的getBob()的来源。 In the method isLiason(final A pAlice) , we will define the specific logic in how to properly compare Alice and Bob . isLiason(final A pAlice) ,我们将定义如何正确比较AliceBob的特定逻辑。

In the Cocktails application, I've decided to represent each specific Ingredient as an implementor of the IIngredient interface. Cocktails应用程序中,我决定将每种特定的成分表示为IIngredient接口的实现者。 You'll see below that we define things like Rum , Vodka and eugh... Gin . 您将在下面看到我们定义了RumVodka和eugh ... Gin I've also defined an additional extension to IIngredient , ISpoonfuls ; 我还定义了对IIngredientISpoonfuls的附加扩展; this enumerates the number of spoonfuls of a certain ingredient the caller has selected. 这将枚举呼叫者选择的某种成分的一匙汤匙的数量。 A concrete implementation of ISpoonfuls is defined within the IIngredient.Sugar class specification. 的具体实施ISpoonfuls是内定义IIngredient.Sugar类规范。

`/* The base ingredient interface. `/ *基本成分接口。 */ public interface IIngredient { * /公共接口IIngredient {

/* 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; } 
}; 

}` }`

It's clear from this design that in order to check for the presence of a certain IIngredient in a collection, we'll wish to test for it's Class. 从此设计中可以明显看出,为了检查集合中是否存在某个IIngredient ,我们希望对其进行测试。 So, we'll define our first ILiason type, the Assignable : 因此,我们将定义第一个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())); } };

In the abstract (incomplete) Assignable class, we'll see that we define a generic specification of the Alice type, however we assert that Bob must be a Class<?> reference. 在抽象的(不完整的) Assignable类中,我们将看到我们定义了Alice类型的通用规范,但是我们断言Bob必须是Class<?>引用。 In our concrete definition of isLiason(final A pAlice) , we use the Class<?> reference returned by getBob() in conjunction with the method isAssignableFrom(Class<?> c) to investigate if Java can detect an existing hierarchy between Alice and Bob . 在我们对isLiason(final A pAlice)的具体定义中,我们将getBob()返回的Class<?>引用与方法isAssignableFrom(Class<?> c)结合使用,以调查Java是否可以检测到AliceAlice之间的现有层次结构。 Bob We use this metric as the result for whether there's an existing liason between Alice and Bob . 我们使用此度量作为AliceBob之间是否存在联络人的结果。 In Cocktails , we're able to use this basic form of ILiason to detect specific Class types. Cocktails ,我们可以使用ILiason这种基本形式来检测特定的Class类型。

As a basic implementation, below I demonstrate how to detect if a certain instance is a specific type of IIngredient : (new ILiason.Assignable<IIngredient>() { @Override public Class<?> getBob() { return Lime.class; } }).isLiason(Lime.class); // Returns true! 作为一个基本的实现,下面我演示如何检测某个实例是否为特定类型的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(Lime.class); // Returns true! (new ILiason.Assignable<IIngredient>() { @Override public Class<?> getBob() { return Lime.class; } }).isLiason(Mint.class); // Returns false!

What we've achieved so far seems like a lot of overhead for what we could have achieved using instanceof , and it is; 到目前为止,我们使用instanceof可以实现的目标似乎需要很多开销。 but the benefit will soon be apparent when we move onto more complex relationships, which fundamentally rely upon the same architecture. 但是当我们转向更复杂的关系时,好处很快就会显现出来,这些关系从根本上依赖于同一体系结构。

Another requirement of our design is that for given instances, we'd like to query their instance members to determine the validity of an ILiason . 设计的另一个要求是对于给定的实例,我们想查询其实例成员以确定ILiason的有效性。 To achieve this, we define a second implementor of ILiason , the Intuition . 为了实现这一点,我们定义了ILiason的第二个实现者,即Intuition

What we've achieved so far seems like a lot of overhead for what we could have achieved using instanceof , and it is; 到目前为止,我们使用instanceof可以实现的目标似乎需要很多开销。 but the benefit will soon be apparent when we move onto more complex relationships, which fundamentally rely upon the same architecture. 但是当我们转向更复杂的关系时,好处很快就会显现出来,这些关系从根本上依赖于同一体系结构。

Another requirement of our design is that for given instances, we'd like to query their instance members to determine the validity of an ILiason . 设计的另一个要求是对于给定的实例,我们想查询其实例成员以确定ILiason的有效性。 To achieve this, we define a second implementor of ILiason , the Intuition . 为了实现这一点,我们定义了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); }; ` `

In Intuition , we see that to compute the status of the Liason we first test for assignability using an ILiason.Assignable ; Intuition ,我们看到要计算Liason的状态,我们首先使用ILiason.Assignable测试可分配ILiason.Assignable if this is achieved, we're able to safelty type cast Alice to the assignable type and allow concrete implementors to define a more intimate analysis of the instance via onSupplyIntuition . 如果实现了这一点,我们就可以安全地将Alice类型转换为可分配类型,并允许具体实现者通过onSupplyIntuition定义对实例的更紧密的分析。 Below, we use this to examine the NumberOfTeaspoons of IIngredient.Sugar : 下面,我们用它来检查IIngredient.SugarNumberOfTeaspoons

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; } }; } }

With these definitions, we're able to define both generic and informed analysis of Collections, and use their results to infer specific combinations of classes. 有了这些定义,我们就可以定义Collection的一般分析和知情分析,并使用它们的结果来推断类的特定组合。 For example, the presence of a given ILiason within an individual array index may be found using the ILiason.Element : 例如,可以使用ILiason.Element查找单个数组索引中给定的ILiason的存在:

/* 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; } };

Using a similar approach, the applicability of an array of ILiason s to a given Alice may be processed using a ILiason.Cohort . 使用类似的方法,可以使用ILiason.Cohort处理ILiason数组对给定AliceILiason.Cohort The order in which ILiasons are defined in the call to getBob() also defines priority: 在对getBob()的调用中定义ILiasons的顺序还定义了优先级:

/* 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; } };

Using these tools, we're able to define different Cocktail s as follows: : 使用这些工具,我们可以如下定义不同的Cocktail

/* Define the Martini Specification. / *定义马提尼酒规格。 */ 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; } }; } }, }); } }; * / 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; } }; } }, }); } }; 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; } }; } }, }); } };

Then, with a simple call to an LIASON_COHORT_COCKTAIL_MARTINI 's isLiason(final IIngredient[] pAlice) method, we're able to test for the existence of appropriate data members in our array of IIngredient s. 然后,只需简单调用LIASON_COHORT_COCKTAIL_MARTINIisLiason(final IIngredient[] pAlice)方法,我们就可以测试IIngredient数组中是否存在适当的数据成员。

And there you have it! 在那里,您拥有了! Statically defined pattern definitions which may implement arbitrary comparison functionality. 静态定义的模式定义,可以实现任意比较功能。

Drawbacks -- There's a pretty large coding overhead in creating new ILiason definitions. 缺点 -创建新的ILiason定义会有相当大的编码开销。 With all of the flexibility the pattern buys us, it doesn't quite measure up in simplicity. 尽管该模式为我们提供了所有灵活性,但它在简单性方面并没有达到标准。 -- It's quite likely slow. -很可能很慢。 Most of the comparisons implemented here are 2D array searches for every single ILiason type. 此处执行的大多数比较都是针对每种ILiason类型的2D阵列搜索。 instanceof might be the more preferable choice in simple cases. 在简单情况下, instanceof可能是更可取的选择。 -- It's possible that there's an overlapping of responsibilities in ILiason definitions. ILiason定义中的职责可能重叠。 Luckily, the core ILiason types are ILiason s themselves, and can often be nested in an effort improve code cohesion. 幸运的是, ILiason的核心类型是ILiason本身,并且经常可以嵌套以提高代码的内聚力。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM