Statyczne metody wytwórcze vs konstruktory vs Factory

0

Tak mnie naszło po dzisiejszej dyskusji. Kiedy korzystacie, co preferujecie do tworzenia obiektów?

U mnie to wygląda tak:

public class Pojo {
	private static final int DEFAULT_INT_VALUE = -1;

	private final String someString; 
	private final int intValue;
	
	// Podstawowy konstruktor - warto pamiętać, 
	// żeby liczba wstrzykiwanych pól nie była 
	// za długa
	public Pojo(String someString, int intValue){
		// opcjonalnie guard-block
		this.someString = someString;
		this.intValue = intValue;
	}
	
	public Pojo(String someString){
		super(someString, DEFAULT_INT_VALUE);
	}
	
	// gettery, equals, hashCode i inna magia
	
	public static Pojo fromCSV(String line) throws IllegalArgumentException {
		Pojo result = ... // parsowanie i guard-block

		return result;
	}

	public static Pojo fromOtherPojo(OtherPojo otherPojo){
		return ... // mapowanie do Pojo etc. 
	}
	
	public static class Factory {
		private final int defaultIntValue; 
		
		public Factory(int defaultIntValue){
			this.defaultIntValue = defaultIntValue;
		}
		
		public Pojo get(String someString){
			return new Pojo(someString, defaultIntValue); 
		}
	}
}

Oczywiście jeśli klasa puchnie to rozdzielam je na mniejsze klasy w stylu PojoCSVParser, PojoFactory itd. Jeśli jest potrzeba to dorzucam Buildera.

Mutacje wyżej wymienionego, z którymi się spotkałem:

  • konstruktory tylko prywatne, jak chcesz jakiś upublicznić to przez static
  • odwrotnie do powyższego - "static Pojo" tylko w oddzielnych klasach
  • Factory.get powinno być zawsze bezargumentowe
  • w konstruktorach guard-block na nulle

Jak to jest u was? Macie lepsze patenty? Czy też po prostu nie zawracacie sobie tym głowy i jedziecie wszystko Lombokiem?

0

Kodzę na co dzień w Scali, ale dorzucę swoje trzy grosze.

konstruktory tylko prywatne, jak chcesz jakiś upublicznić to przez static

Nie widzę sensu jako ogólnej reguły.

w konstruktorach guard-block na nulle

Preferuję konstruktory, które nie mają żadnych efektów ubocznych (a więc nie mogą się sypnąć), nawet null-checków. Zwiększa to mocno testowalność i możliwości rozszerzania klasy. W testach mogę wstawić nulle tam gdzie wiem, że parametr nie będzie używany. Jeśli trzeba wykonać coś co powoduje efekty uboczne to zamykam taką logikę w statycznej metodzie wytwórczej.

Gdybym chciał np zabronić tworzenia klasy bez odpalania null-checków to mogę wystawić publiczną statyczną metodę wytwórczą oraz konstruktor o dostępie protected.

0

to mogę wystawić publiczną statyczną metodę wytwórczą

No ale jak to będziesz mockował, w sytuacji gdy ten obiekt zostanie wyprodukowany statyczną metodą we wnętrzu innego obiektu? W tym innym obiekcie nie będziesz mógł podstawić mocka. Podobny problem jak tworzenie obiektów przez new.

Myślałem, że obiekty nie powinny być tworzone ani przez new ani przez statiki. Tylko przekazywanie przez konstruktor (przekazywanie obiektu albo przekazywanie fabryki).

0

Ja generalnie korzystam tylko z 2 metod jak moge:
1)Konstruktor z argumentami
2)Statyczna metoda wytwórczaii
Zaletą te 2 jest to że ma nazwe inną niż kostruktor, co może się przydać w przypadku gdy chcesz stworzyć metode z tym samym zbiorem argumentów (czyli np. 2 metody które przyjmują Stringa ale inaczej sie nazywają) + możesz łatwiej kontrolować wytwarzanie obiektów (np. stworzyć tylko 1 obiekt i zawsze go zwracać). Wadą może być overkill czyli strzelanie z armaty do muchy, więc generalnie konstruktory z argumentami, jesli bardziej złożony projekt to jeszcze kontener IoC

0
Wibowit napisał(a):

Kodzę na co dzień w Scali, ale dorzucę swoje trzy grosze.

konstruktory tylko prywatne, jak chcesz jakiś upublicznić to przez static

Nie widzę sensu jako ogólnej reguły.

w konstruktorach guard-block na nulle

Preferuję konstruktory, które nie mają żadnych efektów ubocznych (a więc nie mogą się sypnąć), nawet null-checków.

Niby ok, ale to wyciąga nam kontrakt klasy do metod statycznych. O ile to może się sprawdzać w momencie, kiedy wszystkie pola są final/efektywnie final o tyle w przypadku klas mutowalnych to idziemy w stronę:

  • "głupich" klas będących pojemnikami na wartości
  • serwisów operujących na obiektach wyżej wymienionych klas.

W sumie właśnie sobie uświadomiłem, że to zagadnienie trzeba różnicować ze względu na przeznaczenie obiektu. Klasy będące de facto serwisami rzeczywiście powinny być możliwie łatwo testowalne, przy czym klasy będące opakowaniami na dane takie być nie muszą.

jarekczek napisał(a):

to mogę wystawić publiczną statyczną metodę wytwórczą

No ale jak to będziesz mockował, w sytuacji gdy ten obiekt zostanie wyprodukowany statyczną metodą we wnętrzu innego obiektu? W tym innym obiekcie nie będziesz mógł podstawić mocka. Podobny problem jak tworzenie obiektów przez new.

A po co mockować?
Uważam, że takie statici nie powinny mieć dużych ilości kodu. Idąc dalej - powinny być też przetestowane. Tak samo jak nie mockujesz np. Integer.parseInt, tak samo nie ma potrzeby mockowania takich metod.

Myślałem, że obiekty nie powinny być tworzone ani przez new ani przez statiki. Tylko przekazywanie przez konstruktor (przekazywanie obiektu albo przekazywanie fabryki).

Co to znaczy "przez konstruktor"? Konstruktor wołasz ze słówkiem kluczowym "new" chyba?

0

Niby ok, ale to wyciąga nam kontrakt klasy do metod statycznych. O ile to może się sprawdzać w momencie, kiedy wszystkie pola są final/efektywnie final o tyle w przypadku klas mutowalnych to idziemy w stronę:

  • "głupich" klas będących pojemnikami na wartości
  • serwisów operujących na obiektach wyżej wymienionych klas.

Napisałem przecież, że możesz zrobić konstruktor protected oraz publiczną metodę wytwórczą. Wtedy dalej masz kontrakt w jednej klasie. Jaki jest konkretnie minus mojego podejścia?

0

A po co mockować?
Uważam, że takie statici nie powinny mieć dużych ilości kodu. Idąc dalej - powinny być też przetestowane. Tak samo jak nie mockujesz np. Integer.parseInt, tak samo nie ma potrzeby mockowania takich metod.

A kto powiedział, że mówimy tylko o tworzeniu małych klas? Ja mówię raczej o dużych.

Myślałem, że obiekty nie powinny być tworzone ani przez new ani przez statiki. Tylko przekazywanie przez konstruktor (przekazywanie obiektu albo przekazywanie fabryki).

Co to znaczy "przez konstruktor"? Konstruktor wołasz ze słówkiem kluczowym "new" chyba?

Przekazuję obiekt klasy B nowotworzonemu obiektowi klasy A przez konstruktor, więc klasa A nie używa już słówka new. A jeżeli klasa A potrzebuje wytworzyć więcej obiektów klasy B, dostanie w konstruktorze fabrykę. Myślałem, że to ogólnie obowiązujące i dobrze znane zasady, stąd ten skrót myślowy.

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