Lista obiektów klas dziedziczących z JSona

0

Witam. Ostatnio zastanawiałem się jak w projekcie: widok w html i js, a kontroler w Springu możnaby przekazać zestaw funkcji, które mogą być użyte, ale nie muszą przez użytkownika.
https://zapodaj.net/2dce187a10d43.png.html
Tak więc nie wybierając nic, program pokazywałby wszystkie miasta na świecie. Dla wybranego języka i wskaźnika przestępczości, mogłyby to być np. miasta w państwach hiszpańskojęzycznych o określonym współczynniku przestępczości itd. Nie wiem czy słusznie, ale zacząłem od kontrolera. Uznałem, że dobrze gdyby wszystkie polecenia, które chcę przekazać w widoku były w klasach dziedziczących po jednej klasie abstrakcyjnej, tak żeby można było po nich iterować. Klasa abstrakcyjna wyglądałaby w ten sposób:

import java.util.List;

public abstract class Task <T> {

    T t;

    public Task(T t){
        this.t = t;
    }

    public T getT(){
        return t;
    }

    public abstract List<City> filter (T t, List<City> list);
}

a jej implementacje:

public class CountryNameFilter extends Task<String> {

    public CountryNameFilter(String country){
        super(country);
    }
    @Override
    public List<City> filter(String country, List <City> list) {
        return list.stream().filter(a->a.getCountry()==country).collect(Collectors.toList());
    }
}
public class PopulationFilter extends Task<Integer> {

    public PopulationFilter(int population){
        super(population);
    }
    @Override
    public List<City> filter(Integer population, List <City> list) {
        return list.stream().filter(a->a.getPopulation()>population).collect(Collectors.toList());
    }
}

W kontrolerze metoda, która na wszystkich miastach wywołuje wybrane polecenia wygląda tak:

    public List<City> getSelectedCities(List<City> allCities, List<Task> allFunctions){
        if(allFunctions.isEmpty())
            return allCities;
        else{
            Task function = allFunctions.get(0);
            allCities=function.filter(function.getT(),allCities);
            allFunctions.remove(0);
            return getSelectedCities(allCities, allFunctions);
        }
    }

Teraz mam jeden problem. W międzyczasie połapałem się, że nie wiem jak i nie wiem czy w ogóle się da przedstawić w JSonie listę implementacji jakiegoś interfejsu, albo klasy abstrakcyjnej. Na próbę chciałem zobaczyć jakby wyglądało zrobienie JSona za pomocą JSonArray i wyglądało to tak:

[{"t":"Spain"},{"t":50000}]

Więc gdybym coś takiego próbował z powrotem przekształcić w listę obiektów, to na bank by się wszystko rozlazło. Ktoś by mi mógł podpowiedzieć czy jednak by się dało jakoś przekazać przez JSona taką listę, albo czy w ogóle cały sposób w jaki próbuję to zrobić ma sens?

2

Nie wiem, czy rozumiem, czy częściowo.

Idea filtra mi w ogóle nie pasuje do dziedziczenia. Zdecydowanie nie.

Gryzł bym to jakimś wzorcem, być może łańcuch odpowiedzialności.

To nie jest wrost odp na Twoje pytania, ale przynajmniej odciągam od konia, z którym się kopiesz

0

Bardziej bym zrobił coś takiego:

public class SearchFilters {
    public String country;
    public Integer population;
    ... inne pola ...
}

Spring powinien takie coś łyknąć (w pola nieprzekazane wstawi nulle) i z niewielką dozą refleksji jesteś w stanie sobie takie coś obrobić.

Poza tym biblioteki do baz danych często potrafią sobie automagicznie coś takiego ogarnąć, gdybyś zdecydował się zapisać te dane w bazie danych. To może być dobry pomysł, jeśli tych danych masz dużo.

0

@AnyKtokolwiek: Przysiądę pewnie do nauki tego wzorca, bo wydaje się bardzo przydatny, ale na tą chwilę chyba ogarnąłem tamto zmieniając po prostu JSona tak żeby dla metody, która nie ma być wywołana, była przyjmowana wartość "null". Poza tym uznałem, że dobrze by było gdyby jedno wyszukiwanie mogło się tyczyć paru państw, dystryktów, albo przedziałów wartości liczbowych, tak więc zamieniłem wartości na tablice:

{
    "districts":null,
    "populationRange":[100000,200000]
}
@RestController
public class CityService {

    @Autowired
    CityRepository cityRepository;

    @GetMapping("/all")
    public List<City> showAll(){
        return cityRepository.findAll();
    }
    public static boolean IsMatch(String district, String[] districts){
        return  districts == null || Arrays.asList(districts.clone()).contains(district);
    }
    public static boolean IsMatch(Integer population, Integer[] populationRange){
        return populationRange == null || (population>populationRange[0]
                &&population<populationRange[1]);
    }
    @PostMapping("/all")
    @ResponseBody
    public List<City> showSelected(@RequestBody CityRequest cityRequest) throws JsonProcessingException{
        return cityRepository.findAll().stream()
                .filter(a->IsMatch(a.getDistrict(),cityRequest.getDistricts()))
                .filter(a->IsMatch(a.getPopulation(),cityRequest.getPopulationRange()))
                .collect(Collectors.toList());
    }
}

Tak czy inaczej mam wrażenie, że to nie do końca tak jak powinno być. Czy przy pomocy optionali nie mógłbym jakoś pominąć warunek: "jeśli pole równa się null". Druga sprawa, że dopiero teraz zacząłem ogarniać testy. Postanowiłem napisać po trzy dla przeciążonych metod "IsMatch":

@SpringBootTest
class CityFilterApplicationTests {

	@Test
	void contextLoads() {
	}
	@Test
	void shouldSayTrueForString(){ ;
		assert(CityService.IsMatch("Slaskie", new String[]{"Slaskie", "Podkarpackie"}));
	}
	@Test
	void shouldSayFalseForString(){
		assertFalse(CityService.IsMatch("Mazowieckie", new String[]{"Slaskie", "Podkarpackie"}));
	}
	@Test
	void shouldSayTrueForTextNull(){
		assert(CityService.IsMatch("Slaskie",null));
	}
	@Test
	void shouldSayTrueForInteger(){
		assert(CityService.IsMatch(500000, new Integer[]{400000, 1000000}));
	}
	@Test
	void shouldSayFalseForInteger(){
		assertFalse(CityService.IsMatch(300000, new Integer[]{400000, 1000000}));
	}
	@Test
	void shouldSayNullForNumberNull(){
		assert(CityService.IsMatch(2000000, null));
	}
}

No i tutaj dwa testy do obsługi nulla wywalają się. Próbowałem jakoś to przerobić, żeby zamiast Stringa i Integera był optional, ale też nie działało, no i wolałem zapytać zanim się znowu będę mocował parę dni z tym.

0
paranoise napisał(a):

@AnyKtokolwiek: Przysiądę pewnie do nauki tego wzorca
...
Poza tym uznałem, że dobrze by było gdyby jedno wyszukiwanie mogło się tyczyć paru państw, dystryktów, albo przedziałów wartości liczbowych, tak więc zamieniłem wartości na tablice:

Ależ właśnie łańcuch odpowiedzialności to ma, tzn można łączyć elementy w stos, idealnie pasuje do wyszukiwań po wielu elementach
Wiem, że "wzorce" może brzmiec strasznie, ale od twoich prób różni się zaledwie przemyślanym kontruktorem

ps. nie jestem religijnym - sekciarskim apostołem wzorców. Rozsądek nad wzorcami, a nie na odwrót

0

@szatkus: Sorry, ale nie rozumiem o co Ci chodzi. Jeśli o oddzielną klasę dla encji i oddzielną do zapytań o encję, to teraz to poprawiłem:

public class City{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    private String Name;
    @Transient
    private String CountryCode;
    private String District;
    private Integer Population;
}
public class CityRequest {

    private String [] districts;
    private Integer [] populationRange;
}

1 użytkowników online, w tym zalogowanych: 0, gości: 1