Как обеспечить последовательность вызовов функций - программирование
Подтвердить что ты не робот

Как обеспечить последовательность вызовов функций

Скажем, я хочу создать класс, чьи клиенты должны будут вызывать функции в определенной последовательности, например,

hasNext();
next();

или, как очень общий пример, класс CookFood с методами:

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

Во втором примере я хочу обеспечить, чтобы это смешивание должно выполняться только после получения предметов, и что нагрев должен выполняться только после смешивания. Существуют ли какие-либо известные шаблоны или передовые методы, обеспечивающие последовательность вызовов функций?

4b9b3361

Ответ 1

Вам может быть интересен Шаблон построителя шагов. Это не обязательно хорошее соответствие для всех случаев, которые вы представили, но идея состоит в том, что каждая операция возвращает что-то, реализующее интерфейс, который позволяет выполнить следующую операцию. Поскольку вы можете получить объекты только путем выполнения операций в правильном порядке, вы будете вынуждены выполнять их в правильном порядке.

В то время как в случае итерации (next/hasNext) она будет немного зависеть, вы можете представить ее для

  • наденьте носки,
  • затем наденьте обувь

шаблон. Как-то вы получаете экземпляр интерфейса CanWearSocks, который имеет только следующий метод.

CanWearShoes putOnSocks()

Когда вы вызываете putOnSocks(), вы получаете свой экземпляр CanWearShoes, который имеет только следующий метод.

SockAndShoeWearer putOnShoes()

Когда вы звоните putOnShoes(), у вас теперь есть что-то носки и обувь, и вы были вынуждены сделать это в правильном порядке.

Что особенно приятно, так это то, что вы можете использовать один и тот же объект в обоих случаях, но поскольку сигнатуры метода возвращают только тип интерфейса, код сможет использовать только методы интерфейса (если код не скрыт и не выполняет объект другого типа).

Пример

Вот довольно надуманный пример, который реализует шаблон итерации, т.е. гарантирует, что вы используете NextChecker перед 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();
            }
        }
    }
}

Вывод:

1
2
3
4

Ответ 2

Если у вас есть полный доступ к исходному коду, и вы можете его изменить, то что мешает вам использовать комбинацию Factory Method Шаблон с Шаблонный метод Шаблон. Простой пример:

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() не обязательно абстрактны, вы можете иметь реализацию по умолчанию, которую вы можете переопределить в подклассе для настройки.

Ответ 3

Здесь могут быть разные подходы, один из которых приведен здесь. Хотя этот подход предполагает, что вам нужно вызвать другую функцию один раз перед вызовом другого, но не всегда. Вы можете отредактировать его по своему усмотрению:

  • Создайте переменные, чтобы проверить состояние вызовов функций. Всякий раз, когда кто-то вызывает listOfItems, вы можете установить переменную isListed в true. Затем проверьте значение isListed в mixAllItems, чтобы убедиться, что 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;
            }
        }
    }
    

Ответ 4

Один из способов сделать то, что вы хотите, - установить флаги всякий раз, когда вызывается функция, а затем проверить, установлен ли этот флаг при вызове зависимой функции.

Например:

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

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

public void mixAllItems() {
    if(funcMixAllItemsCalled ) {
        ...
    }
}