Общие положения
В предыдущих постах я начал описывать структуру валидации / фильтрации, которую мы создаем.
Показывая код, я пытаюсь показать чистый код, ориентацию теста и эволюцию кода.
У этого есть некоторая ловкость в процессе; Мы знаем конечные требования, но точные детали меняются со временем.
В ходе разработки мы изменили код, сделав его более общим, поскольку в нем мы увидели некоторые шаблоны.
Код развивался по мере развития потока.
Поток, как мы его понимаем.
Вот схема потока, который мы реализуем.
Шаблон
На каждом шаге последовательности (проверка, фильтрация, действие) мы распознавали один и тот же шаблон:
- У нас есть конкретные реализации (фильтры, проверки)
- У нас есть движок, который оборачивает конкретные реализации
- Нам нужно отобразить реализации по флагу, и по флагам запроса выбрать соответствующие реализации.
- Нам нужно иметь класс, который вызывает маппер, а затем двигатель
Диаграмма, показывающая рисунок
Исходный код
Чтобы показать некоторую эволюцию кода и то, как рефакторинг изменил его, я добавил теги в GitHub после серьезных изменений.
Примеры кода
Давайте посмотрим, что пришло из шаблона mapper.
MapperByFlag
public interface MapperByFlag<T> { List<T> getOperations(Request request); }
AbstractMapperByFlag
public abstract class AbstractMapperByFlag<T> implements MapperByFlag<T> { private List<T> defaultOperations; private Map<String, List<T>> mapOfOperations; public AbstractMapperByFlag(List<T> defaultOperations, Map<String, List<T>> mapOfOperations) { this.defaultOperations = defaultOperations; this.mapOfOperations = mapOfOperations; } @Override public final List<T> getOperations(Request request) { Set<T> selectedFilters = Sets.newHashSet(defaultOperations); Set<String> flags = request.getFlags(); for (String flag : flags) { if (mapOfOperations.containsKey(flag)) { selectedFilters.addAll(mapOfOperations.get(flag)); } } return Lists.newArrayList(selectedFilters); } }
Mapper Реализации
public RequestValidationByFlagMapper(List<RequestValidation> defaultValidations, map<String, List<RequestValidation>> mapOfValidations) { super(defaultValidations, mapOfValidations); } public ItemFiltersByFlagMapper(List<Filter> defaultFilters, Map<String, List<Filter>> mapOfFilters) { super(defaultFilters, mapOfFilters); }
Я создал тест для абстрактного класса, чтобы показать сам поток.
Тесты реализаций используют Отражение Java, чтобы проверить, что правильные введенные параметры отправлены супер.
Я также показываю импорт здесь. Чтобы иметь некоторые ссылки на статический импорт, пакеты и классы mockito и hamcrest.
AbstractMapperByFlagTest
import static org.hamcrest.Matchers.containsInAnyOrder; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.when; import java.util.List; import java.util.Map; import org.eyal.requestvalidation.model.Request; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.collect.Sets; @RunWith(MockitoJUnitRunner.class) public class AbstractMapperByFlagTest { private final static String FLAG_1 = "flag 1"; private final static String FLAG_2 = "flag 2"; @Mock private Request request; private String defaultOperation1 = "defaultOperation1"; private String defaultOperation2 = "defaultOperation2"; private String mapOperation11 = "mapOperation11"; private String mapOperation12 = "mapOperation12"; private String mapOperation23 = "mapOperation23"; private MapperByFlag<String> mapper; @Before public void setup() { List<String> defaults = Lists.newArrayList(defaultOperation1, defaultOperation2); Map<String, List<String>> mapped = ImmutableMap.<String, List<String>> builder() .put(FLAG_1, Lists.newArrayList(mapOperation11, mapOperation12)) .put(FLAG_2, Lists.newArrayList(mapOperation23, mapOperation11)).build(); mapper = new AbstractMapperByFlag<String>(defaults, mapped) { }; } @Test public void whenRequestDoesNotHaveFlagsShouldReturnDefaultFiltersOnly() { when(request.getFlags()).thenReturn(Sets.<String> newHashSet()); List<String> filters = mapper.getOperations(request); assertThat(filters, containsInAnyOrder(defaultOperation1, defaultOperation2)); } @Test public void whenRequestHasFlagsNotInMappingShouldReturnDefaultFiltersOnly() { when(request.getFlags()).thenReturn(Sets.<String> newHashSet("un-mapped-flag")); List<String> filters = mapper.getOperations(request); assertThat(filters, containsInAnyOrder(defaultOperation1, defaultOperation2)); } @Test public void whenRequestHasOneFlagShouldReturnWithDefaultAndMappedFilters() { when(request.getFlags()).thenReturn(Sets.<String> newHashSet(FLAG_1)); List<String> filters = mapper.getOperations(request); assertThat(filters, containsInAnyOrder(mapOperation12, defaultOperation1, mapOperation11, defaultOperation2)); } @Test public void whenRequestHasTwoFlagsShouldReturnWithDefaultAndMappedFiltersWithoutDuplications() { when(request.getFlags()).thenReturn(Sets.<String> newHashSet(FLAG_1, FLAG_2)); List<String> filters = mapper.getOperations(request); assertThat(filters, containsInAnyOrder(mapOperation12, defaultOperation1, mapOperation11, defaultOperation2, mapOperation23)); } }
RequestValidationByFlagMapperTest
@RunWith(MockitoJUnitRunner.class) public class RequestValidationByFlagMapperTest { @Mock private List<RequestValidation> defaultValidations; @Mock private Map<String, List<RequestValidation>> mapOfValidations; @InjectMocks private RequestValidationByFlagMapper mapper; @SuppressWarnings("unchecked") @Test public void verifyParameters() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { Field defaultOperationsField = AbstractMapperByFlag.class.getDeclaredField("defaultOperations"); defaultOperationsField.setAccessible(true); List<RequestValidation> actualFilters = (List<RequestValidation>) defaultOperationsField.get(mapper); assertThat(actualFilters, sameInstance(defaultValidations)); Field mapOfFiltersField = AbstractMapperByFlag.class.getDeclaredField("mapOfOperations"); mapOfFiltersField.setAccessible(true); Map<String, List<RequestValidation>> actualMapOfFilters = (Map<String, List<RequestValidation>>) mapOfFiltersField.get(mapper); assertThat(actualMapOfFilters, sameInstance(mapOfValidations)); } }
Задачи
Есть и другие классы, которые могут быть кандидатами на рефакторинг.
RequestFlowValidation и RequestFilter похожи.
And
RequestValidationsEngineImpl и FiltersEngine
To Do 2
Создайте Matcher для отражающей части.
Код
Как всегда, весь код можно найти по адресу:
Тег для этого поста: все компоненты в
Заключение
Инфраструктура почти готова.
В течение этого времени мы также реализуем фактические классы для потока (проверки, фильтры, действия).
Они не описаны ни в постах, ни в GitHub.
Инфраструктура будет подключена к сервису, который мы используем с помощью Spring.
Это будет объяснено в следующих постах.