Java zaawansowane #8: tworzenie biznesowych wyjątków

Wyjątki biznesowe, chodź brzmią bardzo skomplikowanie, są po prostu klasami, które rozszerzają wyjątki istniejące już w Twoim kodzie. Będą to przeważnie wyjątki znajdujące się w standardzie Javy, ale biznesowym wyjątkiem może być też wyjątek pochodzący z jakiejś zewnętrznej biblioteki.

Jak już wiesz, wyjątki dzielą się na te, które obsługiwanie wymuszane jest przez kompilator (ang. checked) oraz te, które nie wymagają tego, bo są pochodną RuntimeException (ang. unchecked).

Przykłady biznesowych wyjątków

Prześledźmy kilka ciekawych wyjątków biznesowych pochodzących z innych bibliotek:

  • AnnotationException – biblioteka Hibernate, problem z użyciem adnotacji w niewłaściwy sposób.
  • InvalidMappingException – biblioteka Hibernate, błąd przy mapowaniu tabeli bazy danych o obiektu Javy.
  • NoTransactionException – biblioteka Spring, niepożądane użycie transakcji bazodanowych
  • DuplicateKeyException – także Spring, występuje, gdy w bazie danych istnieje już rekord a danym numerze klucza Primary Key.

Jak widzisz biznesowe wyjątki są powszechnie używane w Javie. Poniżej postaram pokazać Ci, jak stworzyć zarówno wyjątki checked jak i unchecked.

public class BankAccount {
	private int money;
	
	public BankAccount(int money) {
		this.money = money;
	}
	
	public void sendTransfer(int amount) {
		if (money < amount) {
			throw new NotEnoughFundsException("Lack of money in your account.");
		}
	}
	public int getMoney() {
		return money;
	}
	public void setMoney(int money) {
		this.money = money;
	}
}

Własny wyjątek unchecked

Metoda sendTransfer wyrzuca wyjątek unchecked dla przelewów większych niż saldo na koncie. Nie ma tu specjalnie nic skomplikowanego. Twój wyjątek biznesowy, będzie po prostu dziedziczył po klasie RuntimeException i wywoływał odpowiedni konstruktor klasy rodzica. W jego parametrze może wpisać swój komentarz, który powinien pomóc osobie czytającej wyjątek zdiagnozować problem. Pamiętaj też, że ponieważ wyjątki mogą być powszechnie użyte w każdym miejscu w Twoim systemie, obowiązkowo powinieneś wygenerować UID dla mechanizmu serializacji (albo przynajmniej użyć domyślnej wartości 1L).

public class NotEnoughFundsException extends RuntimeException{
	private static final long serialVersionUID = 1L;
	public NotEnoughFundsException(String errorMessage) {
        super(errorMessage);
    }
}

Tworzenie wyjątku checked

Teraz stworzę wyjątek checked, który pomoże mi sprawdzić czy wczytany plik ma rozszerzenie xml.

public class WrongExtensionException extends Exception{
	private static final long serialVersionUID = 1L;
	public WrongExtensionException(String errorMessage) {
        super(errorMessage);
    }
}

Wyjątek rozszerza tym razem klasę Exception zamiast RuntimeException. Teraz zaimplementuję prostą metodę, która sprawdzi, czy wczytany plik posiada odpowiednie rozszerzenie. Kompilator Java nie może znaleźć odpowiedniego pliku, przez co wchodzi do klauzuli catch, a w niej został dodany warunek, który sprawdza podane rozszerzenie pliku. Metoda isCorrectFileName powinna zwracać typ boolean, po sprawdzeniu wartości rozszerzenia. Jeśli rozszerzenie nie jest typu xml kod wyrzuci wyjątek:

Exception in thread "main" basics.exception.WrongExtensionException: Incorrect file extension.

Kod klasy z metodą main:

public class NullpointerCatch {
	private static final String FILENAME = "somefile.txt";
	public static void main(String [] args) throws IOException, WrongExtensionException {
		fileCheck();
	}
	private static void fileCheck() throws IOException, WrongExtensionException {
		FileReader file = null;
		try {
			file = new FileReader("c:\\" + FILENAME);
		} catch (FileNotFoundException e) {
			if (!isCorrectFileName(FILENAME)) {
		        throw new WrongExtensionException("Incorrect file extension.");
		    }
		} 
	}

Multiple catch

Czasami się zdarza, że często w kodzie łapiemy więcej niż jeden wyjątek na raz. Jest to logiczne, ponieważ im bardziej skomplikowany kod, tym więcej wyjątków będzie wymagał do obsłużenia. Ponieważ implementacja klauzuli catch z reguły ogranicza się do wypisywania za pomocą loggera odpowiednich informacji diagnostycznych dla programisty lub administratora aplikacji, często zdarza się, że każda z tych sekcji zawiera ten sam kod. Nie jest to zgodne z zasadą Don’t repeat yourself. Jednak dzięki drobnemu usprawnieniu, które zostało wprowadzone w Javie w wersji 7, istnieje możliwość zoptymalizowania takiego kodu.

Ogólna zasada jest bardzo prosta. Zamiast brzydkiego kodu takiego jak poniżej:

try {
  // sekcja try
} catch (ExceptionType1 ex) { 
  // łapanie wyjątku 1
} catch (Exceptiontype2 ex) { 
  // łapanie wyjątku 2
}

można uprościć go w poniższy sposób:

try {
  // sekcja try
} catch (ExceptionType1 | Exceptiontype2 ex) { 
  // łapanie obu wyjątków we wspólnym bloku
}

Kod wygląda teraz dużo lepiej. Możesz też mieszać dwie strategie obsługi wyjątków:

try {
  // sekcja try
} catch (ExceptionType1 | Exceptiontype2 ex) { 
  // łapanie obu wyjątków we wspólnym bloku
} catch (Exceptiontype3 ex) { 
  // trzeci wyjątek jest obsługiwany w innym bloku
}

To wszystko, jeśli chodzi o tworzenie biznesowych, czyli Twoich własnych, wyjątków.

Link do kodu: https://github.com/developeronthego/java-advanced/tree/master/src/main/java/advanced/lesson8

Może Ci się również spodoba

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *