Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to enforce sequence in function calls

Let's say I want to design a class whose clients would need to call functions in a particular sequence, e.g.,

hasNext();
next();

or, as a very generic example, a class CookFood with methods:

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

In the second example, I want to enforce that mixing should be done only after getting items, and that heating should be done only after mixing. Are there any known patterns or good practices that enforce sequence of function calls?

like image 427
JavaDeveloper Avatar asked Jun 23 '13 00:06

JavaDeveloper


2 Answers

You might be interested in the Step Builder Pattern. It's not necessarily a good match for all the cases that you've presented, but the idea is that each operation returns something implementing an interface that lets you perform the next operation. Since you can only get the objects by performing the operations in the correct order, you'll be forced to perform them in the correct order.

While it would feel a bit forced in the iteration (next/hasNext) situation, you can imagine it for the

  • put on your socks,
  • then put on your shoes

pattern. Somehow you get an instance of the CanWearSocks interface, which has only the following method.

CanWearShoes putOnSocks()

When you call putOnSocks(), you get your CanWearShoes instance, which has only the following method.

SockAndShoeWearer putOnShoes()

When you call putOnShoes() you now have something wearing socks and shoes, and you were forced to do it in the correct order.

What's particularly nice is that you can actually use the same object in both cases, but because the method signatures only return the interface type, code will only be able to use the interface methods (unless the code is sneaky, and casts the object to a different type).

Example

Here's a rather contrived example that implements the iteration pattern, i.e., that makes sure that you use a NextChecker before a NextGetter.

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

The output is:

1
2
3
4
like image 152
Joshua Taylor Avatar answered Oct 23 '22 12:10

Joshua Taylor


If you have full access to the source code and you can modify it then what's preventing you from using a combination of Factory Method Pattern with Template Method Pattern. A simple example:

public class CookFood {

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

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

}

Now clients of the code can call MakeFood which will enforce the order of steps and if you want to customize any step then you can subclass CookFood and implement that certain step. Of course the steps PrepareFood(), HeatFood(), ServeFood() need not be abstract, you can have a default implementation which you can override in the subclass for customization.

like image 24
Ibrahim Najjar Avatar answered Oct 23 '22 14:10

Ibrahim Najjar