[Java] Generics met meerdere types

Pagina: 1
Acties:

Vraag


Acties:
  • 0 Henk 'm!

  • Chris_147
  • Registratie: Juni 2005
  • Laatst online: 25-07 15:43
Java Generics heb ik nooit echt helemaal begrepen.
Op basis niveau begrijp ik het wel, maar als ik wat complexere zaken wil doen, heb ik geen idee hoe dit aan te pakken.
Wat hier uitgelegd wordt begrijp ik: YouTube: Generics In Java - Full Simple Tutorial
Voor wat ik echter hieronder wil bereiken vind ik niet gemakkelijk een uitleg.

Ik heb volgend stuk code:
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public List<Requirement> getAllRequirements(ApiClient apiClient, Integer environmentId) throws ApiException {
        RequirementsApi requirementsApi = new RequirementsApi(apiClient);
        List<Requirement> requirements = new ArrayList<Requirement>();

        Boolean loop = true;
        Integer offset = 0;
        System.out.print("Retrieving requirements, offset: ");
        while (loop) {
            System.out.print(offset + "...");
            InlineResponse2003 tmpReq = requirementsApi.getRequirements(environmentId, offset);
            requirements.addAll(tmpReq.getData());
            if (tmpReq.getLinks().getNext() != null) {
                offset = offset + 10;
            } else {
                loop = false;
                System.out.println("done!");
            }
        }
        return requirements;
    }


Er zijn meerdere zaken "variabel": de types Requirement, RequirementsApi en InlineResponse2003.
Exact dezelfde methode heb ik voor Products, met Product, ProductsApi en InlineResponse2002.
En nog enkele andere.
Kan dit generic geschreven worden en hoe?

Andere verbetervoorstellen zijn ook welkom, want ik vind de loop te omslachtig.

Alle reacties


Acties:
  • 0 Henk 'm!

  • Cyphax
  • Registratie: November 2000
  • Laatst online: 20:42

Cyphax

Moderator LNX
Je kunt in principe gewoon meerdere typeargumenten opgeven (List<Requirement, RequirementsApi, ...>), maar ik zou deze code denk ik zo refactoren dat je niet meer nieuwe instanties maakt in je methode, maar beter een factory oid maakt en injecteert die de instanties voor je maakt. Voor dat InlineResponse2003 zou ik denk ik eerst een abstractie maken (abstracte class of een interface), dan zou je dat hele type toch niet meer nodig moeten hebben?

Saved by the buoyancy of citrus


Acties:
  • 0 Henk 'm!

  • Chris_147
  • Registratie: Juni 2005
  • Laatst online: 25-07 15:43
Hoe zou dat injecteren er dan uitzien? Gewoon een parameter in de methode?

Ivm de InlineResponse2003: deze code komt uit een API die via Swagger editor heb gegenereerd aan de bechrijving van de tool waarvan ik de API wil uitlezen.
Dan zou ik dus in die genereerde client aanpassingen moeten gaan maken.
Maar ik verwacht dat de API nog gaat veranderen, dus zal ik opnieuw moeten genereren en mijn aanpassingen meenemen.
Ofwel eenmalig de API laten genereren, aanpassingen maken en dan bij iedere API update, manueel de aanpassingen implementeren.
Lastig...

Edit: nu ik er nog wat op doordenk, zie ik niet hoe je de methode getProducts ook kan generiek maken. Voor requirements is die natuurlijk getRequirement, daarna getTestCases, etc. Die moet je dan meegeven of zo.

[ Voor 15% gewijzigd door Chris_147 op 10-04-2024 13:50 ]


Acties:
  • 0 Henk 'm!

  • Cyphax
  • Registratie: November 2000
  • Laatst online: 20:42

Cyphax

Moderator LNX
Chris_147 schreef op woensdag 10 april 2024 @ 12:35:
Hoe zou dat injecteren er dan uitzien? Gewoon een parameter in de methode?
Dat zou kunnen inderdaad. Mocht je een van die dependencies ook in andere methodes gebruiken, dan is op klasseniveau misschien handiger. Afhankelijk van wat voor soort applicatie je maakt kun je misschien CDI ofzoiets gebruiken om het voor je te regelen.
Ivm de InlineResponse2003: deze code komt uit een API die via Swagger editor heb gegenereerd aan de bechrijving van de tool waarvan ik de API wil uitlezen.
Dan zou ik dus in die genereerde client aanpassingen moeten gaan maken.
Maar ik verwacht dat de API nog gaat veranderen, dus zal ik opnieuw moeten genereren en mijn aanpassingen meenemen.
Ofwel eenmalig de API laten genereren, aanpassingen maken en dan bij iedere API update, manueel de aanpassingen implementeren.
Lastig...

Edit: nu ik er nog wat op doordenk, zie ik niet hoe je de methode getProducts ook kan generiek maken. Voor requirements is die natuurlijk getRequirement, daarna getTestCases, etc. Die moet je dan meegeven of zo.
Op het moment dat de API gaat veranderen moet je iets van een nieuwe client laten genereren, dus hoe minder code je dan aan hoeft te passen, hoe beter. Je moet alleen een brug slaan tussen dat wat anders is geworden in de API en dat wat niet verandert in je code, en dat zou dan een abstractie zijn. Als je meer code wil delen of een klassediagram ofzo zou ik iets meer kunnen adviseren denk ik. :)

Saved by the buoyancy of citrus


Acties:
  • 0 Henk 'm!

  • Chris_147
  • Registratie: Juni 2005
  • Laatst online: 25-07 15:43
Zelf heb ik niet veel meer code.

Dit is de API beschrijving: https://testersuite.stopl...854ecfbc9-testersuite-api
En daar heb ik dan via editor.swagger.io een Java Client voor gegenereerd.

De code die ik heb is deze:
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
    public List<Product> getAllProducts(ApiClient apiClient, Integer environmentId) throws ApiException {
        ProductsApi productsApi = new ProductsApi(apiClient);
        List<Product> products = new ArrayList<Product>();

        Boolean loop = true;
        Integer offset = 0;
        System.out.print("Retrieving products, offset: ");
        while (loop) {
            System.out.print(offset + "...");
            InlineResponse2002 tmpProds = productsApi.getProducts(environmentId, offset);
            products.addAll(tmpProds.getData());
            if (tmpProds.getLinks().getNext() != null) {
                offset = offset + 10;
            } else {
                loop = false;
                System.out.println("done!");
            }
        }
        return products;
    }

    public List<Requirement> getAllRequirements(ApiClient apiClient, Integer environmentId) throws ApiException {
        RequirementsApi requirementsApi = new RequirementsApi(apiClient);
        List<Requirement> requirements = new ArrayList<Requirement>();

        Boolean loop = true;
        Integer offset = 0;
        System.out.print("Retrieving requirements, offset: ");
        while (loop) {
            System.out.print(offset + "...");
            InlineResponse2003 tmpReqs = requirementsApi.getRequirements(environmentId, offset);
            requirements.addAll(tmpReqs.getData());
            if (tmpReqs.getLinks().getNext() != null) {
                offset = offset + 10;
            } else {
                loop = false;
                System.out.println("done!");
            }
        }
        return requirements;
    }


    public List<CustomJsonTestCycle> getAllTestCycles(ApiClient apiClient, Integer environmentId) throws ApiException {
        TestCyclesApi testCyclesApi = new TestCyclesApi(apiClient);
        List<CustomJsonTestCycle> testCycles = testCyclesApi.getTestCycles(environmentId, null).getTestCycles();
        return testCycles;
    }

    public List<TestCase> getAllTestCases(ApiClient apiClient, Integer environmentId) throws ApiException {
        TestCasesApi testCasesApi = new TestCasesApi(apiClient);
        List<TestCase> testCases = new ArrayList<TestCase>();

        Boolean loop = true;
        Integer offset = 0;
        System.out.print("Retrieving testcases, offset: ");
        while (loop) {
            System.out.print(offset + "...");
            InlineResponse2005 tmpTestCases = testCasesApi.getTestCases(environmentId, null, null, offset);
            testCases.addAll(tmpTestCases.getData());
            if (tmpTestCases.getLinks().getNext() != null) {
                offset = offset + 10;
            } else {
                loop = false;
                System.out.println("done!");
            }
        }
        return testCases;
    }

    public List<TestRun> getAllTestRuns(ApiClient apiClient, Integer environmentId) throws ApiException {
        TestRunsApi testRunsApi = new TestRunsApi(apiClient);
        List<TestRun> testRuns = new ArrayList<TestRun>();

        Boolean loop = true;
        Integer offset = 0;
        System.out.print("Retrieving testruns, offset: ");
        while (loop) {
            System.out.print(offset + "...");
            InlineResponse20015 tmpTestRuns = testRunsApi.getTestRuns(environmentId, offset, null, null, null, null, null, null, null);
            testRuns.addAll(tmpTestRuns.getData());
            if (tmpTestRuns.getLinks().getNext() != null) {
                offset = offset + 10;
            } else {
                loop = false;
                System.out.println("done!");
            }
        }
        return testRuns;
    }

    public List<TestScenario> getAllTestScenarios(ApiClient apiClient, Integer environmentId) throws ApiException {
        TestScenariosApi testScenariosApi = new TestScenariosApi(apiClient);
        List<TestScenario> testScenarios = new ArrayList<TestScenario>();

        Boolean loop = true;
        Integer offset = 0;
        System.out.print("Retrieving test scenarios, offset: ");
        while (loop) {
            System.out.print(offset + "...");
            InlineResponse2007 tmpTestScenarios = testScenariosApi.getTestScenarios(environmentId, offset, null, null);
            testScenarios.addAll(tmpTestScenarios.getData());
            if (tmpTestScenarios.getLinks().getNext() != null) {
                offset = offset + 10;
            } else {
                loop = false;
                System.out.println("done!");
            }
        }
        return testScenarios;
    }

    public List<Defect> getAllDefects(ApiClient apiClient, Integer environmentId) throws ApiException {
        DefectsApi defectsApi = new DefectsApi(apiClient);
        List<Defect> defects = defectsApi.getDefects(environmentId, null, null, null,  null, null, null).getData();
        return defects;
    }

Acties:
  • 0 Henk 'm!

  • Tweaketweak
  • Registratie: April 2019
  • Laatst online: 28-09 19:49
Je wil het liefst geen methods hebben die zowel low-level logica als high-level logica bevatten. Low-level is hier het herhaald aanroepen van de API tot je alle items (requirements, products, etc.) binnen hebt. High-level is hier de communicatie met de API.

Als je de high-level code weghaalt, dan blijft er dit over:
Java:
1
2
3
4
5
6
7
8
9
10
11
public <T> List<T> getAll(BatchFetcher<T> batchFetcher) throws ApiException {
    List<T> result = new ArrayList<>();
    int offset = 0;
    while (true) {
        Batch<T> batch = batchFetcher.getBatch(offset); 
        result.addAll(batch.getItems());
        if (batch.isLast()) break;
        offset += 10; 
    }
    return result;
}


Deze method maakt gebruik van deze interfaces:
Java:
1
2
3
4
5
6
7
8
9
@FunctionalInterface
interface BatchFetcher<T> {
    Batch<T> getBatch(int offset) throws ApiException;
}

interface Batch<T> {
    List<T> getItems();
    boolean isLast();
}


De eerste interface is een functional interface, dus die kan je met een lambda expressie implementeren. Bijvoorbeeld, voor requirements:
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public List<Requirement> getAllRequirements(ApiClient apiClient, Integer environmentId) throws ApiException {
    RequirementsApi requirementsApi = new RequirementsApi(apiClient);
    return getAll(offset -> {
        InlineResponse2003 tmpReqs = requirementsApi.getRequirements(environmentId, offset);
        return new Batch<Requirement>() {
            @Override
            public List<Requirement> getItems() {
                return tmpReqs.getData(); 
            }
            @Override
            public boolean isLast() {
                return tmpReqs.getLinks().getNext() == null;
            }
        };
    });
}
Pagina: 1