Konstanter i Java: Mønstre og antimønstre

1. Introduktion

I denne artikel vil vi lære om at bruge konstanter i Java med fokus på almindelige mønstre og antimønstre.

Vi starter med nogle grundlæggende konventioner til definition af konstanter. Derfra flytter vi til almindelige antimønstre, inden vi afslutter med et kig på almindelige mønstre.

2. Grundlæggende

En konstant er en variabel, hvis værdi ikke ændres, efter at den er defineret.

Lad os se på det grundlæggende for at definere en konstant:

privat statisk endelig int OUR_CONSTANT = 1;

Nogle af de mønstre, vi vil se på, vedrører offentlig eller privat adgangsændringsbeslutning. Vi laver vores konstanter statisk og endelig og giv dem en passende type, hvad enten det er en Java-primitiv, en klasse eller en enum. Navnet skal være alle store bogstaver med ordene adskilt af understregninger, undertiden kendt som skrigende slangetaske. Endelig giver vi selve værdien.

3. Antimønstre

Lad os først starte med at lære, hvad man ikke skal gøre. Lad os se på et par almindelige antimønstre, vi kan støde på, når vi arbejder med Java-konstanter.

3.1. Magiske numre

Magiske tal er numeriske bogstaver i en blok kode:

hvis (nummer == 3.14159265359) {// ...}

De er svære for andre udviklere at forstå. Derudover, hvis vi bruger et tal i hele vores kode, er det svært at håndtere at ændre værdien. Vi skal i stedet definere tallet som en konstant.

3.2. En stor global konstante klasse

Når vi starter et projekt, kan det føles naturligt at oprette en klasse med navnet Konstanter eller Hjælpeprogrammer med den hensigt at definere alle konstanterne til applikationen der. For mindre projekter kan dette være ok, men lad os overveje et par grunde til, at dette ikke er en ideel løsning.

Lad os først forestille os, at vi har hundrede eller flere konstanter i vores konstante klasse. Hvis klassen ikke vedligeholdes, både for at holde trit med dokumentation og lejlighedsvis omlægge konstanterne til logiske grupperinger, bliver det ret ulæseligt. Vi kunne endda ende med duplikatkonstanter med lidt forskellige navne. Denne tilgang vil sandsynligvis give os læsbarheds- og vedligeholdelsesproblemer i alt andet end de mindste projekter.

Ud over logistikken for at vedligeholde Konstanter i selve klassen inviterer vi også andre vedligeholdelsesproblemer ved at tilskynde for meget indbyrdes afhængighed med denne ene globale konstante klasse og forskellige andre dele af vores applikation.

På en mere teknisk side, Java-kompilatoren placerer værdien af ​​konstanten i henvisningsvariabler i de klasser, hvor vi bruger dem. Så hvis vi ændrer en af ​​vores konstanter i vores konstante klasse og kun kompilerer denne klasse og ikke referenceklassen, kan vi få inkonsekvente konstante værdier.

3.3. Det konstante interface mod mønster

Det konstante grænseflademønster er, når vi definerer en grænseflade, der indeholder alle konstanterne til bestemt funktionalitet og derefter har de klasser, der har brug for disse funktionaliteter til at implementere grænsefladen.

Lad os definere en konstant grænseflade til en lommeregner:

offentlig interface CalculatorConstants {dobbelt PI = 3.14159265359; dobbelt UPPER_LIMIT = 0x1.fffffffffffffP + 1023; enum Operation {ADD, SUBTRACT, MULTIPLY, DIVIDE}; }

Dernæst implementerer vi vores LommeregnerKonstanter grænseflade:

offentlig klasse GeometryCalculator implementerer CalculatorConstants {public double operateOnTwoNumbers (double numberOne, double numberTwo, Operation operation) {// Kode for at udføre en operation}}

Det første argument mod at bruge en konstant grænseflade er, at det strider mod formålet med en grænseflade. Vi er beregnet til at bruge grænseflader til at oprette en kontrakt for den adfærd, vores implementeringsklasser vil give. Når vi opretter en grænseflade fuld af konstanter, definerer vi ikke nogen adfærd.

For det andet åbner det os ved hjælp af en konstant grænseflade for kørselsproblemer forårsaget af feltskygge. Lad os se på, hvordan det kan ske ved at definere en ØVERSTE GRÆNSE konstant inden for vores GeometryCalculator klasse:

offentlig statisk endelig dobbelt UPPER_LIMIT = 100000000000000000000.0;

Når vi først definerer den konstante i vores GeometryCalculator klasse skjuler vi værdien i LommeregnerKonstanter interface til vores klasse. Vi kunne så få uventede resultater.

Et andet argument mod dette antimønster er, at det forårsager navneområdet forurening. Vores LommeregnerKonstanter vil nu være i navneområdet for nogen af ​​vores klasser, der implementerer grænsefladen såvel som enhver af deres underklasser.

4. Mønstre

Tidligere så vi på den passende form til at definere konstanter. Lad os se på nogle andre gode fremgangsmåder til at definere konstanter i vores applikationer.

4.1. Generel god praksis

Hvis konstanter er logisk relateret til en klasse, kan vi bare definere dem der. Hvis vi ser et sæt konstanter som medlemmer af en opregnet type, kan vi bruge en enum for at definere dem.

Lad os definere nogle konstanter i a Lommeregner klasse:

public class Calculator {public static final double PI = 3.14159265359; privat statisk endelig dobbelt UPPER_LIMIT = 0x1.fffffffffffffP + 1023; public enum Operation {ADD, SUBTRACT, DIVIDE, MULTIPLY} public double operationOnTwoNumbers (double numberOne, double numberTwo, Operation operation) {if (numberOne> UPPER_LIMIT) {kast nyt IllegalArgumentException ("'numberOne' er for stor"); } hvis (numberTwo> UPPER_LIMIT) {kast nyt IllegalArgumentException ("'numberTwo' er for stort"); } dobbelt svar = 0; switch (operation) {case ADD: answer = numberOne + numberTwo; pause; sag SUBTRAKT: svar = antalOne - nummerTo; pause; sag DIVIDE: svar = nummerEn / nummerTo; pause; sag MULTIPLY: svar = antalEn * nummerTo; pause; } returner svar }}

I vores eksempel har vi defineret en konstant for ØVERSTE GRÆNSE at vi kun planlægger at bruge i Lommeregner klasse, så vi har sat det til privat. Vi ønsker, at andre klasser skal kunne bruge PI og Operation enum, så vi har sat dem til offentlig.

Lad os overveje nogle af fordelene ved at bruge en enum til Operation. Den første fordel er, at den begrænser de mulige værdier. Forestil dig, at vores metode tager en streng for operationens værdi med forventning om, at en af ​​fire konstante strenge leveres. Vi kan let forudse et scenario, hvor en udvikler, der kalder metoden, sender sin egen strengværdi. Med enum, er værdierne begrænset til dem, vi definerer. Vi kan også se, at enums er særligt velegnede til brug i kontakt udsagn.

4.2. Konstanter klasse

Nu hvor vi har set på nogle generelle god praksis, lad os overveje tilfældet, når en konstante klasse kan være en god idé. Lad os forestille os, at vores ansøgning indeholder en pakke med klasser, der skal udføre forskellige slags matematiske beregninger. I dette tilfælde giver det sandsynligvis mening for os at definere en konstante klasse i den pakke til konstanter, som vi bruger i vores beregningsklasser.

Lad os oprette en MathConstants klasse:

offentlig slutklasse MathConstants {offentlig statisk endelig dobbelt PI = 3.14159265359; statisk endelig dobbelt GOLDEN_RATIO = 1.6180; statisk endelig dobbelt GRAVITATIONAL_ACCELERATION = 9.8; statisk endelig dobbelt EULERS_NUMBER = 2.7182818284590452353602874713527; offentlig enum Operation {ADD, SUBTRACT, DIVIDE, MULTIPLY} private MathConstants () {}}

Den første ting vi skal bemærke er, at vores klasse er endelig for at forhindre, at den forlænges. Derudover har vi defineret en privat konstruktør, så det ikke kan instantieres. Endelig kan vi se, at vi har anvendt de andre gode fremgangsmåder, vi diskuterede tidligere i artiklen. Vores konstant PI er offentlig fordi vi forventer behov for at få adgang til det uden for vores pakke. De andre konstanter, vi har forladt som pakke-privat, så vi kan få adgang til dem inden for vores pakke. Vi har lavet alle vores konstanter statisk og endelig og navngav dem i en skrigende slangesag. Operationerne er et specifikt sæt værdier, så vi har brugt en enum for at definere dem.

Vi kan se, at vores specifikke pakke-niveau konstanter klasse er forskellig fra en stor global konstante klasse, fordi den er lokaliseret til vores pakke og indeholder konstanter, der er relevante for den pakke's klasser.

5. Konklusion

I denne artikel overvejede vi fordele og ulemper ved nogle af de mest populære mønstre og antimønstre, der blev set, når du bruger konstanter i Java. Vi startede med nogle grundlæggende formateringsregler, inden vi dækkede antimønstre. Efter at have lært om et par almindelige antimønstre, så vi på mønstre, som vi ofte ser anvendt på konstanter.

Som altid er koden tilgængelig på GitHub.