To tylko półpoważna sugestia, ale możemy zmodyfikować mikera's answer, aby była bezpieczna dla typów.
Say mamy:
public class A {
private final String foo;
private final int bar;
private final Date baz;
}
Następnie piszemy:
public abstract class AProperty<T> {
public static final AProperty<String> FOO = new AProperty<String>(String.class) {};
public static final AProperty<Integer> BAR = new AProperty<Integer>(Integer.class) {};
public static final AProperty<Date> BAZ = new AProperty<Date>(Date.class) {};
public final Class<T> propertyClass;
private AProperty(Class<T> propertyClass) {
this.propertyClass = propertyClass;
}
}
oraz:
public class APropertyMap {
private final Map<AProperty<?>, Object> properties = new HashMap<AProperty<?>, Object>();
public <T> void put(AProperty<T> property, T value) {
properties.put(property, value);
}
public <T> T get(AProperty<T> property) {
return property.propertyClass.cast(properties.get(property));
}
}
miłośników zaawansowanych wzorców projektowych i/lub niejasnych sztuczek Java rozpozna to jako pojemnik heterogeniczny typu. Po prostu bądź wdzięczny, że nie użyłem też getGenericSuperclass()
.
Następnie z powrotem w klasie docelowej:
public A(APropertyMap properties) {
foo = properties.get(AProperty.FOO);
bar = properties.get(AProperty.BAR);
baz = properties.get(AProperty.BAZ);
}
To wszystko jest wykorzystywane tak:
APropertyMap properties = new APropertyMap();
properties.put(AProperty.FOO, "skidoo");
properties.put(AProperty.BAR, 23);
A a = new A(properties);
Tylko dla lulz, możemy nawet dać mapie płynny interfejs:
public <T> APropertyMap with(AProperty<T> property, T value) {
put(property, value);
return this;
}
Który pozwala dzwoniącym pisać:
A a = new A(new APropertyMap()
.with(AProperty.FOO, "skidoo")
.with(AProperty.BAR, 23));
Istnieje wiele drobnych ulepszeń, które można w tym celu wprowadzić. Typy w AProperty
mogą być obsługiwane bardziej elegancko. APropertyMap
może mieć statyczną fabrykę zamiast konstruktora, pozwalając na bardziej płynny styl kodu, jeśli robisz coś takiego. APropertyMap
może rozwinąć metodę build
, która wywołuje konstruktor A
, zasadniczo przekształcając go w program budujący.
Niektóre z tych obiektów można uczynić bardziej ogólnymi. AProperty
i APropertyMap
może mieć ogólne klasy bazowe, które wykonały bity funkcjonalne, z bardzo prostymi podklasami specyficznymi dla A
.
Jeśli czujesz się szczególnie przedsiębiorcą, a obiekty domeny są jednostkami JPA2, możesz użyć atrybutów metamodelu jako obiektów właściwości. Pozostawia to mapę/konstruktorowi nieco więcej pracy, ale wciąż jest dość proste; Mam generycznego konstruktora pracującego w 45 liniach, z podklasą na jednostkę zawierającą jedną metodę jednoliniową.
Nie potrzebujesz nawet modułów pobierających w konstruktorze, a konstruktor A może być prywatny. Pozwala to również na sprawdzanie parametrów w metodzie build() zamiast w konstruktorze. –
Prawda. Zastanawiałem się nad uczynieniem cora A prywatnym. To prawdopodobnie czystsze. –
Kolejną zaletą jest to, że budowniczy może zwrócić instancję A, ABis lub ATer (Abis i Ater będącą podklasą A), w zależności od parametrów. –