Statyczne zmienne strumieniowe w programie.

0

Witam. Chciałem sobie odświeżyć stary projekt napisany w Swingu z użyciem JDBC. Żadnych Springów, DI, ORMów itd. Mam wątpliwość dotyczącą jednego rozwiązania w kliencie programu. Mam klasę BaseService, w której znajduje się statyczna metoda configSocket(). Konfigurowane jest w niej połączenie z serwerem

	
Socket socket = new Socket(server, Integer.parseInt(port));
OBJ_IN_STREAM = new ObjectInputStream(socket.getInputStream());
OBJ_OUT_STREAM = new ObjectOutputStream(socket.getOutputStream());

Pola OBJ_IN_STREAM i OBJ_OUT_STREAM są statyczne, ponieważ po klasie BaseService dziedziczą konkretniejsze klasy, typu UserService, w której zaimplementowane są operacje dodawania, edycji, usuwania itd. Poza UserService jest jeszcze kilka serwisów, dlatego pomyślałem, że dziedziczenie i statyczne pola będą tu dobrym rozwiązaniem ponieważ raz utworzymy strumienie podczas startu programu i wszystkie klasy dziedziczące będą z nich korzystać. Klasy serwisowe są często tworzone w różnych JFrameach. Czy rozwiązanie ze zmiennymi statycznymi jest to dobre? Czy może jest to błąd i nie powinno się tak robić? Jeśli tak to jaką alternatywę można zastosować?

1

Bym do tego podszedł trochę inaczej. Stwórz klasę ServiceFactory - czy coś podobnego - która będzie w stanie tworzyć gotowe do użycia serwisy (serwisy najlepiej w postaci interfejsów). Następnie stwórz singleton z instancją tej klasy. BaseService zmień tak, aby trzeba było go tworzyć go podając mu referencje do strumieni i socketu. Wszystkie dziedziczące klasy będą też musiały zostać zmienione.

0

@wartek01 też właśnie myślałem o takiej fabryce. Zrefaktoryzowałem swój kod i wygląda to teraz tak, że mam tego singletona zwracającego instancję ServiceFactory. Wygląda on tak:

public class ServiceFactory {

	private HashMap<String, BaseService> serviceMap;

	public BaseService getService(String serviceName) {
		return serviceMap.get(serviceName);
	}

	public void initializeService(ObjectInputStream objInStream, ObjectOutputStream objOutStream) {
		serviceMap = new HashMap<>();
		serviceMap.put("CarGroupService", new CarGroupService(objInStream, objOutStream));
		serviceMap.put("CarService", new CarService(objInStream, objOutStream));
		serviceMap.put("CityService", new CityService(objInStream, objOutStream));
		serviceMap.put("ClientService", new ClientService(objInStream, objOutStream));
		serviceMap.put("LoginSerivice", new LoginService(objInStream, objOutStream));
		serviceMap.put("RentService", new RentService(objInStream, objOutStream));
		serviceMap.put("UserService", new UserService(objInStream, objOutStream));
	}

} 

Serwisy implementują wspólny interfejs BaseService i dodałem je do mapy, z której będą pobierane po nazwie. Mam jednak pewną wątpliwość. Mianowicie wszystkie serwisy wykonują podobne operacje typu dodaj, usuń, edytuj, zwróć listę. Tylko, że każdy z nich operuje na swojej klasie, np CarService w metodzie add przyjmie jako argument obiekt typu Car, a CityService obiekt typu City. I teraz żeby te wszystkie klasy mogły implementować ten interfejs to musiałbym zrobić interfejs tego typu:

add(Object object) 

Metoda add przyjmuje typ Object. W implementacajch zaimplementować tą metodę, ale wymusiłoby to na mnie rzutowanie np:

add(Object object) {
        Car car = (Car) object;
}

Mam wątpliwości czy to będzie dobre rozwiązanie.

1

Mam jednak pewną wątpliwość. Mianowicie wszystkie serwisy wykonują podobne operacje typu dodaj, usuń, edytuj, zwróć listę. Tylko, że każdy z nich operuje na swojej klasie, np CarService w metodzie add przyjmie jako argument obiekt typu Car, a CityService obiekt typu City.

A generyki znasz?

Jeśli rzeczywiście wszystkie serwisy implementują ten sam interfejs to:

  1. Stwórz interfejs generyczny, np:
public interface CrudService<T, Id> {
	public void add(T t);
	public T read(Id d);
	public void update(T t);
	public void delete(T t);
}
  1. Zaimplementuj go w oddzielnych klasach, czyli dla Car:
public class CarService extends BaseService implements CrudService<Car, Long> {
	public vod add(Car car){
		// ...
	}

	public Car read(Long id){
		// ...
	}

	// analogicznie dla update i delete
}
  1. Niech fabryka zamiast Map<String, BaseService> trzyma Map<Class, CrudService>
  2. Dodaj metodę, która będzie zwracała po klasie:
public class ServiceFactory {
	// ...
	private Map<Class, CrudService> map = new HashMap<>();
	public ServiceFactory(){
		map.put(CarService.class, new CarService(...));
		// ...
	}

	public <T> CrudService<T> getService(Class<T> clazz){
		if(map.containsKey(clazz){
			return map.get(clazz);
		} else {
			throw new IllegalArgumentException("Some info");
		}
	}
}

Dzięki temu będziesz miał de facto service locator, który będzie łatwo i bezboleśnie dało się przerobić na mechanizm DI jeśli zajdzie taka potrzeba. Nawet bez DI będziesz miał service locator który:

  1. Będzie odporny na literówki w nazwie
  2. Uprości sprawę dodawania innych serwisów.
  3. Nie będziesz musiał znać klasy obiektu, żeby wyciągnąć crud service do niego.

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