statyczna metoda tworząca + cache

0

powiedzmy, że mam taką immutable klasę z dwoma polami. wiem, że będzie z 200 zduplikowanych obiektów custom. dwa pytania:

  1. jak napisać prawidłowy cache w takim wypadku? -bez użycia Guavy itd
  2. czy to ma sens przy 200 zduplikowanych obiektach?
import java.util.HashMap;
import java.util.Map;

public class Wall {

    private static final Map<Integer, Wall> cache = new HashMap<>();
    private static final Wall STANDARD = new Wall(10, 10);
    public final int height;
    public final int length;

    private Wall(int height, int length) {
        this.height = height;
        this.length = length;
    }

    public static Wall standard() {
        return STANDARD;
    }

    public static Wall custom(int height, int length) {
        Wall wall = cache.get(height);
        if (wall == null || wall.length != length) {
            wall = new Wall(height, length);
            cache.put(height, wall);
        }
        return wall;
    }
}
1

Tak sobie myślę, że jak obiekt jest immutable czy w takim przypadku wzorzec flyweight (pyłek) nie będzie rozwiązaniem? Wtedy nie ma duplikatów.

0

nie wiem wgl czy to jest sensowne czy sama sztuka dla sztuki

1

Sztuka dla sztuki raczej nie, bo jak pewnie wiesz flyweight jest masowo stosowane w bibliotece standardowej. Chociażby klasy jak String czy BigDecimal/BigInteger. Czasem nie ma sensu tworzyć wielu obiektów jak są immutable. Po prostu szkoda zasobów i można współdzielić jeden. Jest to od razu naturalny cache. Powstaje tyle obiektów ile jest wymaganych stanów.

1

To, co zrobiłeś to de facto prosta implementacja wzorca FlyWeight z lazy initialization.

  1. W kwestii sztuki:
  • IMO lepiej zrobić mapę z SoftReference jeśli założysz, że część obiektów może być użyta niewiele razy i po jakimś czasie będzie nieprzydatna
  • blok zwracający custom powinien być zsynchronizowany
public class Wall {
	// ...
	private Map<Integer, SoftReference<Wall>> cache = //... 
	// ...

	public synchronized static Wall custom(int height, int width){
		SoftReference<Wall> wallRef = cache.get(height);
		if(wallRef == null || wallRef.get() == null){
			// put new value into cache and return it
		} else {
			// return existing value
		}
	}
}
  1. W kwestii Twojego przypadku - obiekty Wall są bardzo lekkie. Rozumiem, że scacheowanych w ten sposób instancji jest mniej więcej dwieście. Jeśli z tych obiektów nie będziesz korzystał w wielu oddzielnych miejscach, a w obrębie jednego obiektu to takie coś jest rzeczywiście może być sztuką dla sztuki - ale przynajmniej "Wall.custom()" jest bardziej eleganckie od "new Wall()" :)
1

Ciężko analizować taki kod w odniesieniu jedynie do kontekstu jedenj klasy i specyficznych wymagań, które w skali całego projektu mogę nie być optymalne. Zacząłbym od zastanowienia się co chcemy wgl osiągnąć. Dlaczego Wall jako klasa strukturalna posiada dodatkową odpowiedzialność jaką ma być cache i do tego jeszcze statyczna metoda wytwórcza. Według mnie powinna istnieć osobna klasa fabrykująca, w której ewentualnie może znajdować się cache. Dodatkowo kod:

Wall wall = cache.get(height);

Nie jest idealnym określeniem "unikalności obiektu", skorzystałbym tutaj z automatycznej chociażby generacji hashCode.
No i tak naprawdę zastanowiłbym się czy taka optymalizacja jest potrzebna dla 200 obiektów. Koszt implementacji takiego zachowania jest dosyć spory, biorąc pod uwagę optymalizacje jaką można w ten sposób uzyskać.

0

możesz wyjaśnić dlaczego coś o nazwie Wall.custom() ma być bardziej eleganckie od new Wall()? jak dla mnie Wall.custom() to jakaś statyczna! metoda o nie określonym znaczeniu. . racja, ale tutaj to bardziej kwestia po prostu marnej nazwy tej metody

Według mnie powinna istnieć osobna klasa fabrykująca, w której ewentualnie może znajdować się cache. to wiem, ale to był prosty projekt i mi się nie chciało.

Dzięki za wszystkie odpowiedzi, za przypomnienie SoftReference też. Rozjaśniły mi to co chciałam.

0
wartek01 napisał(a):

To, co zrobiłeś to de facto prosta implementacja wzorca FlyWeight z lazy initialization.

  1. W kwestii sztuki:
  • IMO lepiej zrobić mapę z SoftReference jeśli założysz, że część obiektów może być użyta niewiele razy i po jakimś czasie będzie nieprzydatna
  • blok zwracający custom powinien być zsynchronizowany
public class Wall {
	// ...
	private Map<Integer, SoftReference<Wall>> cache = //... 
	// ...

	public synchronized static Wall custom(int height, int width){
		SoftReference<Wall> wallRef = cache.get(height);
		if(wallRef == null || wallRef.get() == null){
			// put new value into cache and return it
		} else {
			// return existing value
		}
	}
}
  1. W kwestii Twojego przypadku - obiekty Wall są bardzo lekkie. Rozumiem, że scacheowanych w ten sposób instancji jest mniej więcej dwieście. Jeśli z tych obiektów nie będziesz korzystał w wielu oddzielnych miejscach, a w obrębie jednego obiektu to takie coś jest rzeczywiście może być sztuką dla sztuki - ale przynajmniej "Wall.custom()" jest bardziej eleganckie od "new Wall()" :)

synchronized w cache to samobójstwo, fajnie zbudowany dostęp do takiego elementu jest budowany w java concurrency in practive, robią to za pomocą future, masz w tedy miejsce które blokuje, zminimalizowane dość mocno. Ale pomysł z SoftReference bardzo zacny.

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