【问题标题】:Design patterns for analysing a combination of instances?用于分析实例组合的设计模式?
【发布时间】:2018-04-24 22:55:57
【问题描述】:

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

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

【问题讨论】:

    标签: java arrays design-patterns match instance


    【解决方案1】:

    也许,你可以使用这两个中的一个来做到这一点

    interface Ingredient {
      boolean belongsTo(Cocktail cocktail)
    }
    
    interface Cocktail {
      boolean hasIngredient(Ingredient ingredient)
    }
    

    鸡尾酒本身可以是一个数组或其他成分的集合

    【讨论】:

    • 我更喜欢后者,因为配料不需要知道它们是否属于鸡尾酒类型。正是这种混合使它成为鸡尾酒,就像房子里的砖块一样。
    【解决方案2】:

    当您有要处理的类层次结构时,首选的设计模式是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 这样的 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);
    }
    

    【讨论】:

    • 谢谢。这对于处理实例很有用,但不适用于分析集合的内容。我认为使用这种方法实现示例会有很大的开销!
    【解决方案3】:

    经过其他 StackOverflow 用户提供的精彩答案的一点思考和鼓励,我想我偶然发现了一个合理的解决方案。

    场景 我们想收集一组Ingredients,并系统地解释可以从中复制的美味鸡尾酒食谱的范围。这些关系必须灵活并且能够访问我们通用数组规范中的数据成员。

    技术 在这个应用程序中,我们将处理一组实例。在这方面,我们希望开发模式匹配代码,作为数据成员聚合集的函数,而不是让数据成员自己定义配方的逻辑。原因有两个;首先,Ingredient 不仅仅只用于Cocktail (这将使你们更容易参加聚会);它可以在CakeSalad 甚至Colonoscopy 中;因此,我们有义务从基类规范中抽象出分析逻辑,以改进职责分离。这种方法激励我们开发一套通用工具,适用于各种不同的应用程序,而不仅仅是我想象的酒吧。其次,它使我们能够处理自动装箱的 Java Primitives 或 Java Beans,而无需构造任何接口。这种方法的另一个令人高兴的副产品是,它使Cocktails 模式的整体可视化变得更加清晰。类调用之间没有“隐藏”的交互。

    因此,我决定将整个方法形式化为两个通用实体之间的有限交互,而不是处理不同 Ingredients 之间的关系; AliceBob。我决定将这些互动称为Liason,不仅因为我很伤心,还因为我很孤独。

    设计 首先,我构建了一个名为ILiasonInterface。我使用接口是因为这使设计中的现有类能够与模式匹配架构集成,而不会导致多重继承冲突。

    /* 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 中,我们定义了两个简单的方法,它们依赖于两个泛型类型,A (Alice) 和B (Bob)。 A 将是我们希望比较的运行时集合或对象,B 将是 getBob() 返回的比较源。在方法isLiason(final A pAlice)中,我们将定义如何正确比较AliceBob的具体逻辑。

    Cocktails 应用程序中,我决定将每个特定成分表示为IIngredient 接口的实现者。您将在下面看到我们定义了诸如RumVodka 和呃……Gin 之类的东西。我还为IIngredientISpoonfuls 定义了一个附加扩展;这会枚举调用者选择的某种成分的勺数。 ISpoonfuls 的具体实现在 IIngredient.Sugar 类规范中定义。

    `/* 基本成分接口。 */ 公共接口 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; } 
    }; 
    

    }`

    从这个设计中可以清楚地看出,为了检查集合中是否存在某个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 类型的通用规范,但是我们断言Bob 必须是Class&lt;?&gt; 引用。在我们对isLiason(final A pAlice) 的具体定义中,我们将getBob() 返回的Class&lt;?&gt; 引用与isAssignableFrom(Class&lt;?&gt; c) 方法结合使用来调查Java 是否可以检测到AliceBob 之间的现有层次结构。我们使用此指标作为AliceBob 之间是否存在联系的结果。在Cocktails 中,我们可以使用ILiason 的这种基本形式来检测特定的类类型。

    作为一个基本的实现,下面我演示如何检测某个实例是否是IIngredient的特定类型: (new ILiason.Assignable&lt;IIngredient&gt;() { @Override public Class&lt;?&gt; getBob() { return Lime.class; } }).isLiason(Lime.class); // Returns true! (new ILiason.Assignable&lt;IIngredient&gt;() { @Override public Class&lt;?&gt; 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 测试可分配性;如果实现了这一点,我们就可以安全地将Alice 类型转换为可分配类型,并允许具体实现者通过onSupplyIntuition 定义对实例的更深入的分析。下面,我们用这个来检查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; } }; } }

    通过这些定义,我们能够定义集合的通用分析和知情分析,并使用它们的结果来推断类的特定组合。例如,可以使用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; } };

    使用类似的方法,可以使用ILiason.Cohort 处理ILiasons 数组对给定Alice 的适用性。在对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; } };

    使用这些工具,我们可以定义不同的Cocktails,如下所示::

    /* 定义 Martini 规范。 */ 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; } }; } }, }); } };

    然后,通过对LIASON_COHORT_COCKTAIL_MARTINIisLiason(final IIngredient[] pAlice) 方法的简单调用,我们能够测试IIngredients 数组中是否存在适当的数据成员。

    你有它!静态定义的模式定义,可以实现任意比较功能。

    缺点 -- 创建新的ILiason 定义有相当大的编码开销。尽管该模式为我们提供了所有灵活性,但它在简单性方面并不完全符合标准。 ——很可能很慢。这里实现的大多数比较是对每个 ILiason 类型的二维数组搜索。 instanceof 在简单情况下可能是更可取的选择。 -- ILiason 定义中的职责可能重叠。幸运的是,核心 ILiason 类型本身就是 ILiasons,并且通常可以嵌套以提高代码凝聚力。

    【讨论】:

      猜你喜欢
      • 2015-04-18
      • 1970-01-01
      • 2011-08-09
      • 1970-01-01
      • 2020-02-20
      • 1970-01-01
      • 1970-01-01
      • 2012-02-22
      • 2012-07-06
      相关资源
      最近更新 更多