All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.groupon.grox.Store Maven / Gradle / Ivy

There is a newer version: 1.1.2
Show newest version
/*
 * Copyright (c) 2017, Groupon, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.groupon.grox;

import static java.util.Arrays.asList;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * Like Redux Stores, stores in grox are:
 *
 * 
    *
  • responsible for holding the state. *
  • state is immutable. *
  • stores accept {@link StateChangeListener} that will be notified of state changes. * Unlike in redux, the listeners are not unsubscribed automatically. *
  • stores also use {@link Middleware} like in redux. *
  • they are fully testable. *
* * @param the class of the state. */ public class Store { /** The current state of the store. */ private STATE state; /** The list of internal middle wares. */ private final List> middlewares = new ArrayList<>(); /** The list of all state change listeners that will get notified of state changes. */ private List> stateChangeListeners = new CopyOnWriteArrayList<>(); public Store(STATE initialState, Middleware... middlewares) { this.state = initialState; this.middlewares.add(new NotifySubscribersMiddleware()); this.middlewares.addAll(asList(middlewares)); this.middlewares.add(new CallReducerMiddleware()); } /** * Dispatches an action in the store. The action will go through the chain of the middle wares and * then the action will get executed, and create a new state that will replace the current state * in the store. The state change will be notified to listeners. * * @param action the action to be executed. * @see Middleware * @see StateChangeListener */ public synchronized void dispatch(Action action) { new RealMiddlewareChain<>(this, action, this.middlewares, 0).proceed(action); } /** @return the current state of the store. */ public STATE getState() { return state; } /** * Adds a new {@link StateChangeListener} to the list of listeners that will get notified of state * changes. * * @param listener the listener to be added. */ public void subscribe(StateChangeListener listener) { this.stateChangeListeners.add(listener); listener.onStateChanged(getState()); } /** * Removes a previously added {@link StateChangeListener} from the list of listeners. * * @param listener the listener to be removed. */ public void unsubscribe(StateChangeListener listener) { this.stateChangeListeners.remove(listener); } /** * Basically, a middle ware can intercept all actions being dispatched through a store. Unlike in * Redux, we recommend not to use middle wares to execute asynchronous tasks, like API calls. * Middle wares in Grox are more about adding general behavior to a store. e.g.: crash reporting, * logging, undo, etc. * * @param the class of the state. */ public interface Middleware { /** * Allows a middle ware to intercept actions as they go through the store.
A middle ware * must call {@link Chain#proceed} on the chain received as a parameter. This method * must be called once and only once in each intercept method.
The call to {@link * Chain#proceed} will trigger the remaining middle wares in the chain to have their intercept * method called.
The last middle ware in the store is an internal middle ware that will * call {@link Action#newState(Object)}.
Hence, a middle ware can do things before the * rest of the middle wares are executed (and the action is executed) and after the rest of the * middle wares are executed (and the action is executed).
Middle wares are added to a * store at construction time, see {@link #Store(Object, Middleware[])}. * * @param chain the chain of all middle wares for the store associated to this middleware. */ void intercept(Chain chain); /** * Represents the linked list of all middle wares int the store. The order of the middle ware * corresponds to the order in which the middle wares are passed to the store at construction * time. See {@link #Store(Object, Middleware[])}. * * @param the class of the state. */ interface Chain { /** @return the action being dispatched. */ Action action(); /** * @return the current state of the store. The state before the call to {@link * #proceed(Action)} is the state prior to the action being executed. The state after the * call is the state after the action being executed. */ STATE state(); /** * Triggers the rest of the middle wares to be executed, and ultimately the action to be * executed. * * @param action the action being dispatched in the store. */ void proceed(Action action); } } /** * A listener that will be notified of all state changes taking place in a store. * * @param the class of the state. * @see #subscribe(StateChangeListener) * @see #unsubscribe(StateChangeListener) */ public interface StateChangeListener { void onStateChanged(STATE newState); } /** Internal middle ware that actually executes the action of a middle ware chain. */ private class CallReducerMiddleware implements Middleware { @Override public void intercept(Chain chain) { state = chain.action().newState(state); } } /** * Internal middle ware that notifies the {@link StateChangeListener} that the store's state has * changed. */ private class NotifySubscribersMiddleware implements Middleware { @Override public void intercept(Chain chain) { chain.proceed(chain.action()); for (Iterator> iterator = stateChangeListeners.iterator(); iterator.hasNext(); ) { StateChangeListener stateChangeListener = iterator.next(); stateChangeListener.onStateChanged(state); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy