18

假设我想设计一个类,其客户端需要以特定顺序调用函数,例如,

hasNext();
next();

或者,作为一个非常通用的示例,CookFood具有方法的类:

class CookFood {
  getListOfItems();
  mixAllItems();
  heat();
}

在第二个例子中,我想强制只在拿到物品后进行混合,并且只在混合后进行加热。是否有任何已知的模式或良好实践可以强制执行函数调用序列?

4

4 回答 4

32

您可能对Step Builder Pattern感兴趣。它不一定适合您介绍的所有案例,但其想法是每个操作都会返回一些实现接口的东西,该接口允许您执行下一个操作。由于您只能通过以正确的顺序执行操作来获取对象,因此您将被迫以正确的顺序执行它们。

虽然在迭代 (next/hasNext) 情况下感觉有点强迫,但你可以想象它

  • 穿上你的袜子,
  • 然后穿上你的鞋子

图案。不知何故,你得到了一个CanWearSocks接口的实例,它只有以下方法。

CanWearShoes putOnSocks()

当您调用 时putOnSocks(),您将获得您的CanWearShoes实例,该实例只有以下方法。

SockAndShoeWearer putOnShoes()

当你打电话时,putOnShoes()你现在有一些穿着袜子和鞋子的东西,你被迫以正确的顺序去做。

特别好的是你实际上可以在这两种情况下使用同一个对象,但是因为方法签名只返回接口类型,代码将只能使用接口方法(除非代码是偷偷摸摸的,并将对象转换为不同类型)。

例子

这是一个实现迭代模式的相当人为的示例,即确保在 NextGetter 之前使用 NextChecker。

public class StepBuilderIteration {

    interface NextChecker {
        NextGetter hasNext();
    }

    interface NextGetter {
        Object next();
        NextChecker more();
    }

    static class ArrayExample {
        final static Integer[] ints = new Integer[] { 1, 2, 3, 4 };

        public static NextChecker iterate() {
            return iterate( 0 );
        }

        private static NextChecker iterate( final int i ) {
            return new NextChecker() {
                public NextGetter hasNext() {
                    if ( i < ints.length ) {
                        return new NextGetter() {
                            public Object next() {
                                return ints[i];
                            }
                            public NextChecker more() {
                                return iterate( i+1 );
                            }
                        };
                    }
                    else {
                        return null;
                    }
                }
            };
        }
    }

    public static void main(String[] args) {
        NextChecker nc = ArrayExample.iterate();
        while (nc != null) {
            NextGetter ng = nc.hasNext();
            if (ng != null) {
                System.out.println(ng.next());
                nc = ng.more();
            }
        }
    }
}

输出是:

1
2
3
4
于 2013-06-23T00:35:03.047 回答
5

如果您可以完全访问源代码并且可以对其进行修改,那么是什么阻止您将工厂方法模式与模板方法模式结合使用。一个简单的例子:

public class CookFood {

    public Food MakeFood() {
        PrepareFood();
        HeatFood();
        ServeFood();
    }

    protected abstract void PrepareFood();
    protected abstract void HeatFood();
    protected abstract ServeFood();

}

现在代码的客户可以调用MakeFood这将强制执行步骤的顺序,如果您想自定义任何步骤,那么您可以子类化CookFood并实现该特定步骤。当然,这些步骤PrepareFood(), HeatFood(), ServeFood()不必是抽象的,您可以有一个默认实现,您可以在子类中覆盖它以进行自定义。

于 2013-06-23T13:20:33.150 回答
0

可以有不同的方法,这里列出了一种。尽管这种方法认为您需要在调用另一个函数之前调用另一个函数一次,但并非总是如此。您可以根据需要对其进行编辑:

  1. 创建变量以检查函数调用的状态。每当有人调用 listOfItems 时,您都可以将 isListed 变量设置为 true。然后检查 mixAllItems 中 isListed 的值,确保之前调用了 getListOfItems。

    class CookFood {
        boolean isListed;
        boolean isMixed;
        boolean isHeated;
    
        public String getListOfItems() {
    
            // do listing and mark as listed
            isListed = true;
    
            return "something";
        }
    
        public void mixAllItems() {
            // check if listed first
            if (isListed) {
                // do mixing
    
                // mark as mixed
                isMixed = true;
            } else {
                System.out.println("You need to call getListOfItems before mixing");
                return;
            }
        }    
    
        public void heat() {
            if (isMixed) {
                // do heating
    
                // mark as mixed
                isHeated = true;
            } else {
                System.out.println("You need to call isMixed before heating");
                return;
            }
        }
    }
    
于 2013-06-23T00:37:21.333 回答
0

一种方法是在调用函数时设置标志,然后在调用依赖函数时检查是否设置了该标志。

例如:

public void getListOfItems() {
    funcGetListOfItemsCalled = true;
    ....
}

public void mixAllItems() {
    if(funcGetListOfItemsCalled) {
        funcMixAllItemsCalled = true;
        ...
    }
}

public void mixAllItems() {
    if(funcMixAllItemsCalled ) {
        ...
    }
}
于 2013-06-23T00:37:39.257 回答