Singleton/Multiton w Javie

0

Witam,
Mam do rozwiązania pewne zadanie, które sprawia mi sporo problemów, bo nie wiem jak je rozwiązać, aby było jak najbardziej poprawne i zgodne ze wzorcami projektowania. Na wstępie zaznaczę, że wiem co to Singleton/Multiton i potrafię się nimi posługiwać, ale jak widać po tym zadaniu poniżej, nie potrafię zastosować tej wiedzy w zadaniach praktycznych, a więc do rzeczy...

Treść zadania:

Baza danych (tablica znaków) umożliwia dostęp jedynie przez klasę implementującą typ interfejsowy IPolaczenie. Baza danych powinna być **Singletonem ** ale jej klientem nie będzie funkcja **main ** (nie można pobrać z niej bezpośrednio referencji do bazy) tylko obiekt konkretnego połączenia. Z kolei konkretne połączenia będą **Multitonami ** (istnieją tylko 3, udostępniane naprzemiennie - 1 2 3 1 2 3 ..., obiekty je klasy), których klientem będzie Baza, a dokładniej jej metoda getPolaczenie.

Klient (funkcja main) pobiera obiekty połączenia i za ich pomocą korzysta z bazy.

Uzupełnij poniższy kod , a w funkcji **main ** pobierz 4 połączenia i pokaż, że wszystkie korzystają z tej samej bazy. Wykaż też, że obiekty połączeń są współdzielone.

Kod, który muszę uzupełnić:

interface IPolaczenie {
    char get(int indeks);
    void set(int indeks, char c);
    int length();
}
class Baza {
    private char[] tab = new char[100]; /* ... */
    public static IPolaczenie getPolaczenie() {
        return Polaczenie.getInstance();
    }
    private static class Polaczenie implements IPolaczenie {
        private Baza baza; /* ... */
        public static IPolaczenie getInstance() {
            /* ... */
        }
        public char get(int indeks) {
            return baza.tab[indeks];
        }
        public void set(int indeks, char c) {
            baza.tab[indeks] = c;
        }
        public int length() {
            return baza.tab.length;
        }
    }
}

A tutaj moje rozwiązanie, nie pełne:

public interface IPolaczenie {
	char get(int indeks);
	void set(int indeks, char c);
	int length();
}


public class Baza {

	private char[] tab = new char[100];
	
	public static IPolaczenie getPolaczenie() {
		return Polaczenie.getInstance();
	}
	
	private static class Polaczenie implements IPolaczenie {
		private Baza baza;
		
		public String connectionName;
		
		private static Polaczenie[] polaczenia = { 
			new Polaczenie("1"), 
			new Polaczenie("2"), 
			new Polaczenie("3") 
		};
		
		private static int kolejnosc = 0;
		private Polaczenie() { }
		private Polaczenie(String str) { this.connectionName = str; }
		
		public static IPolaczenie getInstance() {
			kolejnosc = (kolejnosc + 1) % polaczenia.length;
			return polaczenia[kolejnosc];
		}
		
		public char get(int indeks) {
			return baza.tab[indeks];
		}
		
		public void set(int indeks, char c) {
			baza.tab[indeks] = c;
		}
		
		public int length() {
			return baza.tab.length;
		}		
	}
}


public class BazaTest {
	public static void main(String[] args) {
		Baza baza = new Baza();
		IPolaczenie polaczenie1 = Baza.getPolaczenie();
		IPolaczenie polaczenie2 = Baza.getPolaczenie();
		IPolaczenie polaczenie3 = Baza.getPolaczenie();
		IPolaczenie polaczenie4 = Baza.getPolaczenie();
	}

}

Co z referencją do obiektu baza ("private Baza baza;") ?

Robiąc nowe połączenie, robię cały czas nowe obiekty ze wspomnianą składową, która nie odnosi się do niczego.

Czy powinienem w tym miejscu zrobić funkcję getBaza(), która zwróci mi jeden-jedyny obiekt klasy Baza - zgodnie z treścią zadania ?!

Kolejna moja wątpliwość to te zdanie z treści zadania "Wykaż też, że obiekty połączeń są współdzielone.", czyli że co w tym przypadku, bo nie mam pojęcia do czego to zdanie się odnosi ?

Inny problem to taki jak się dostać do prywatnej zmiennej tab ("private char[] tab = new char[100];") ? Bo aktualna wersja tj. metody get/set tego nie umożliwiają ? Zmienić na protected/public czy jest jakiś inny sposób dla wzorców ?

Na koniec chciałbym się jeszcze odnieść do tego zdania z treści zadania: "Uzupełnij poniższy kod , a w funkcji main pobierz 4 połączenia i pokaż, że wszystkie korzystają z tej samej bazy.". Czyli moja teza dot. zrobienia metody getBaza() w klasie Baza by się chyba sprawdziła i wtedy mógłbym zamienić "private Baza baza;" na "private Baza baza = Baza.getBaza();". Właśnie nie rozumiem po kiego są wstawione te komentarze, może po to żeby pokazać, że tam należy wprowadzić jakieś zmiany ?! :)

Proszę bardzo o pomoc,
Pozdrawiam JerzyKol

0
Softnyx napisał(a):

Jedno powiem singletony to zuo

https://sites.google.com/site/steveyegge2/singleton-considered-stupid

Chyba sobie żartujesz kolego prostując taką odpowiedź, ja potrzebuję pomocy w rozwiązaniu zadania, a nie opinii innych na temat singletona. Aktualnie przerabiam ten materiał na uczelni i muszę opanować ten materiał, nie ważne czy chce czy nie.

1

Hmm, klasa Polaczenie chyba powinna przyjmować Baza jako parametr konstruktora.

0
Wibowit napisał(a):

Hmm, klasa Polaczenie chyba powinna przyjmować Baza jako parametr konstruktora.

Yhy, chyba dobry pomysł ;) Ja wpadłem na takie coś, żeby napisać:

private Baza baza = Baza.getBaza();

Gdzie getBaza() jest funkcją statyczną w klasie Baza (singleton) i pilnuje aby był tylko 1 obiekt klasy baza.

Mój zmodyfikowany kod prezentuje się następująco:

public interface IPolaczenie {
	char get(int indeks);
	void set(int indeks, char c);
	int length();
}


public class Baza {

	protected char[] tab = new char[100];
	
	public static IPolaczenie getPolaczenie() {
		return Polaczenie.getInstance();
	}
	
	private Baza() { }
	private static Baza bazaDanych;
	private static Baza getBaza() {
		if(bazaDanych == null)
			return new Baza();
		return bazaDanych;
	}
	
	private static class Polaczenie implements IPolaczenie {
		private Baza baza = Baza.getBaza();
		
		public String connectionName;
		
		private static Polaczenie[] polaczenia = { 
			new Polaczenie("1"), 
			new Polaczenie("2"), 
			new Polaczenie("3") 
		};
		
		private static int kolejnosc = 0;
		private Polaczenie() { }
		private Polaczenie(String str) { this.connectionName = str; }
		
		public static IPolaczenie getInstance() {
			kolejnosc = (kolejnosc + 1) % polaczenia.length;
			return polaczenia[kolejnosc];
		}
		
		public char get(int indeks) {
			return baza.tab[indeks];
		}
		
		public void set(int indeks, char c) {
			baza.tab[indeks] = c;
		}
		
		public int length() {
			return baza.tab.length;
		}		
	}
}


public class BazaTest {
	public static void main(String[] args) {
		IPolaczenie polaczenie1 = Baza.getPolaczenie();
		IPolaczenie polaczenie2 = Baza.getPolaczenie();
		IPolaczenie polaczenie3 = Baza.getPolaczenie();
		IPolaczenie polaczenie4 = Baza.getPolaczenie();
		
		polaczenie1.set(10, 'x');
		System.out.println(polaczenie1.get(10));
	}

}

Czyli wg. Twojego pomysłu powinienem zrobić, o tak:

private Polaczenie {
baza = Baza.getBaza();
}

Tak ?

Czy powyżej wklejony kod jest poprawny ? Czy poprawnie zaimplementowałem singletona/multitona ?

1

Kojarzę cię z poprzednich postów :p i w poprzednich postach wystarczało, gdy zastępowało się pola /* ... */ kodem, nie modyfikując żadnych innych linii. Ty chcesz zmieniać kod, a więc pewnie nie o to chodzi wykładowcy.

W Polaczenie.getInstance możesz zrobić coś w ten deseń (kod wymyślony na szybko):

if (polaczenia == null) {
  Baza baza = new Baza();
  polaczenia = new Polaczenie[] { new Polaczenie(baza), new Polaczenie(baza), new Polaczenie(baza) };
}
kolejnosc = (kolejnosc + 1) % polaczenia.length;
return polaczenia[kolejnosc];

Plus do tego synchronizacja (jeśli chodzi o singletony w Javie to jestem zwolennikiem rozwiązania Billa Pugha http://en.wikipedia.org/wiki/Singleton_pattern#The_solution_of_Bill_Pugh ).

0
Wibowit napisał(a):

Kojarzę cię z poprzednich postów :p i w poprzednich postach wystarczało, gdy zastępowało się pola /* ... */ kodem, nie modyfikując żadnych innych linii. Ty chcesz zmieniać kod, a więc pewnie nie o to chodzi wykładowcy.

W Polaczenie.getInstance możesz zrobić coś w ten deseń (kod wymyślony na szybko):

if (polaczenia == null) {
  Baza baza = new Baza();
  polaczenia = new Polaczenie[] { new Polaczenie(baza), new Polaczenie(baza), new Polaczenie(baza) };
}
kolejnosc = (kolejnosc + 1) % polaczenia.length;
return polaczenia[kolejnosc];

Plus do tego synchronizacja (jeśli chodzi o singletony w Javie to jestem zwolennikiem rozwiązania Billa Pugha http://en.wikipedia.org/wiki/Singleton_pattern#The_solution_of_Bill_Pugh ).

Ale zmienna baza nie jest statyczna...

Mimo wszystko w ten sposób zastąpione jest tylko 1 z 3 pól oznaczonych

/* ... */

, a co z dwoma pozostałymi ?

Baza danych ma być singletonem, a więc moim zdaniem bez dodania prywatywnego konstruktora się tutaj nie obejdzie, czy mylę się ?

Kolejna wątpliwość to pole oznaczone w ten sposób:

private char[] tab = new char[100]; /* ... */

Bo niby na co miałbym je zamienić ?

Bardzo bym prosił o kontynuowanie pomocy, bo chyba nikomu więcej się nie chce ;)

1

Wiem, że nie jest statyczna. W konstruktorze trzeba wziąć parametr typu Baza i przypisać go do tej zmiennej.

Spróbuj coś takiego:

  • pierwsze /* ... */ zamień na:
private Baza() {}
  • drugie /* ... */ zamień na:
private Polaczenie(Baza baza) { this.baza = baza; }
  • trzecie /* ... */ zamień na to co ci podałem we wcześniejszym poście,

Jak pokazać, że mamy do czynienia z pulą połączeń o sztywnym rozmiarze? Myślę, że można by np 100 razy wywołać getPolaczenie(), wyniki włożyć do IdentityHashMap, a potem wywołać metodę size() na tej mapie i pokazać, że jest równe 3.

Jak pokazać, że baza jest współdzielona. Wyciągnąć dwa połączenia, zmieniać bazę w jednym połączeniu, a w drugim odczytywać wartości na tych samych indeksach i pokazać, że są odpowiednio zmieniane.

0
Wibowit napisał(a):

Wiem, że nie jest statyczna. W konstruktorze trzeba wziąć parametr typu Baza i przypisać go do tej zmiennej.

Spróbuj coś takiego:

  • pierwsze /* ... */ zamień na:
private Baza() {}
  • drugie /* ... */ zamień na:
private Polaczenie(Baza baza) { this.baza = baza; }
  • trzecie /* ... */ zamień na to co ci podałem we wcześniejszym poście,

Jak pokazać, że mamy do czynienia z pulą połączeń o sztywnym rozmiarze? Myślę, że można by np 100 razy wywołać getPolaczenie(), wyniki włożyć do IdentityHashMap, a potem wywołać metodę size() na tej mapie i pokazać, że jest równe 3.

Jak pokazać, że baza jest współdzielona. Wyciągnąć dwa połączenia, zmieniać bazę w jednym połączeniu, a w drugim odczytywać wartości na tych samych indeksach i pokazać, że są odpowiednio zmieniane.

Dodałem jeszcze te dwie linijki kodu:

private static int kolejnosc = 0;
private static Polaczenie[] polaczenia;

i kod prezentuje się teraz następująco:

public interface IPolaczenie {
	char get(int indeks);
	void set(int indeks, char c);
	int length();
}

class Baza {
    private char[] tab = new char[100]; 
    /* ... */
    private Baza() {}
    public static IPolaczenie getPolaczenie() {
        return Polaczenie.getInstance();
    }
    private static class Polaczenie implements IPolaczenie {
        private Baza baza; 
        /* ... */
        private Polaczenie(Baza baza) {
        	this.baza = baza;
        }
        private static int kolejnosc = 0;
        private static Polaczenie[] polaczenia;
        public static IPolaczenie getInstance() {
            if(polaczenia == null) {
            	Baza baza = new Baza();
            	polaczenia = new Polaczenie[] { new Polaczenie(baza), new Polaczenie(baza), new Polaczenie(baza) };
            }
            kolejnosc = (kolejnosc + 1) % polaczenia.length;
            return polaczenia[kolejnosc];
        }
        public char get(int indeks) {
            return baza.tab[indeks];
        }
        public void set(int indeks, char c) {
            baza.tab[indeks] = c;
        }
        public int length() {
            return baza.tab.length;
        }
    }
}


public class BazaTest {
	public static void main(String[] args) {
		IPolaczenie polaczenie1 = Baza.getPolaczenie();
		IPolaczenie polaczenie2 = Baza.getPolaczenie();
		IPolaczenie polaczenie3 = Baza.getPolaczenie();
		IPolaczenie polaczenie4 = Baza.getPolaczenie();
		
		polaczenie1.set(10, 'x');
		System.out.println(polaczenie1.get(10));
	}

}

Teraz wszystko zgodnie ze wzorcami jest ?

Czy zaproponowana przeze mnie powyżej wersja tego zadania też jest poprawna ze wzorcami ? ( Pomińmy tutaj fakt, że Twoja jest bardziej poprawna :) )

Dzięki za pomoc!!

1

Wydaje się zgodna z treścią zadania, a o to chodzi :]
No może oprócz tego, że synchronizacja by się przydała, ale może akurat się wykładowca nie przyczepi.

Wzorce to ogólnie coś abstrakcyjnego, ogólnego i niezbyt dobrze zdefiniowanego - tak wynika przynajmniej z mojego doświadczenia. Ja traktuję wzorce jako swego rodzaju inspirację. Przydają się także do opisów rozwiązań, np zamiast mówić "tu stworzę klasę z jedną instancją statyczną, którą będę pobierał przez statycznego gettera" powiem "tu stworzę singleton" i wszystko jasne.

0
Wibowit napisał(a):

Wydaje się zgodna z treścią zadania, a o to chodzi :]
No może oprócz tego, że synchronizacja by się przydała, ale może akurat się wykładowca nie przyczepi.

Wzorce to ogólnie coś abstrakcyjnego, ogólnego i niezbyt dobrze zdefiniowanego - tak wynika przynajmniej z mojego doświadczenia. Ja traktuję wzorce jako swego rodzaju inspirację. Przydają się także do opisów rozwiązań, np zamiast mówić "tu stworzę klasę z jedną instancją statyczną, którą będę pobierał przez statycznego gettera" powiem "tu stworzę singleton" i wszystko jasne.

Dzięki za pomoc!

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