I have the following classes
public class MyCustomFactory extends SomeOther3rdPartyFactory {
// Return our custom behaviour for the 'string' type
#Override
public StringType stringType() {
return new MyCustomStringType();
}
// Return our custom behaviour for the 'int' type
#Override
public IntType intType() {
return new MyCustomIntType();
}
// same for boolean, array, object etc
}
Now, for example, the custom type classes:
public class MyCustomStringType extends StringType {
#Override
public void enrichWithProperty(final SomePropertyObject prop) {
super.enrichWithProperty(prop);
if (prop.getSomeAttribute("attribute01")) {
this.doSomething();
this.doSomethingElse();
}
if (prop.getSomeAttribute("attribute02")) {
this.doSomethingYetAgain();
}
// other properties and actions
}
}
But each custom type class like the string one above might have exactly the same if (prop.getSomeAttribute("blah")) { // same thing; }
Suppose I was to add another attribute, is there a nice way I can avoid having to duplicate if statements in each custom type class that needs it? I can move each if statement to utility class but I still need to add the call to the method in the utility class. I think we can do better.
You can create Map<String, Consumer<MyCustomStringType>>, where the key is your attribute name and value is the method call.
public class MyCustomStringType extends StringType {
private final Map<String, Cosnumer<MyCustomStringType>> map = new HashMap<>();
{
map.put("attribute01", o -> {o.doSomething(); o.doSomethingElse();});
map.put("attribute02", MyCustomStringType::doSomethingYetAgain);
// other properties and actions
}
#Override
public void enrichWithProperty(final SomePropertyObject prop) {
super.enrichWithProperty(prop);
map.entrySet().stream()
.filter(entry -> prop.getSomeAttribute(entry.getKey()))
.forEach(entry -> entry.getValue().accept(MyCustomStringType.this));
}
}
Depending on how you initialise this class (and whether this map is always the same), you might be able to turn in into static final immutable map.
I would also recommend naming it better, but a lot here depends on your domain and what this map and loop actually do.
Related
First of all, sorry for the bad title. I don't know how to describe the problem in a few words (maybe not even in many)...
I am refactoring some settings in our system to be more abstract. The current solution has multiple tables in the DB, one for each settings area. In order to add a new setting, you'll need to extend the schema, the hibernate class, all transfer object classes, getters/setters, etc. I felt that this is violating OCP (open-closed principle), thus the refactoring.
I've spent some time coming up with ideas on how to implement such an abstraction. My favourite idea so far is the following:
1 enum for each settings area
1 enum value for each setting
Each setting is a SettingsDefinition<T> class using a generic type
A SettingsService is using static get/set methods with generic types
So for example, a settings area could be:
public enum SettingsABC{
A(new SettingDefinition<Integer>("A", 123)),
B(new SettingDefinition<String>("B", "Hello")),
C(new SettingDefinition<Boolean>("C", false));
private SettingDefinition settingDefinition;
SettingsABC(SettingDefinition settingDefinition) {
this.settingDefinition = settingDefinition;
}
public SettingDefinition getDefinition() {
return settingDefinition;
}
}
Where the SettingDefinition is the following:
public class SettingDefinition<T> {
private String name;
private T defaultValue;
public SettingDefinition(String name, T defaultValue) {
this.name = name;
this.defaultValue = defaultValue;
}
public String getName() {
return name;
}
public T getDefaultValue() {
return defaultValue;
}
}
And the service to get/set the values would be:
public class SettingsService {
public static <T> T getSetting(SettingDefinition setting) {
// hit db to read
// return value
}
public static <T> void setSetting(SettingDefinition setting, T value) {
// hit db to write
}
}
And the consumer would look something like this:
String value = SettingsService.getSetting(SettingsABC.B.getDefinition());
SettingsService.setSetting(SettingsABC.A.getDefinition(), 123);
My problem is that I cannot enforce a compiler type check between the generic type of the SettingDefinition inside SettingsABC and the generic type of get/set methods of the service. So in essence, I can do this:
Integer value = SettingsService.getSetting(SettingsABC.B.getDefinition());
Where B's definition is of type String.
Also, I can do this:
SettingsService.setSetting(SettingsABC.A.getDefinition(), "A");
Where A's definition is an Integer.
Is there any way to use generics to force these two different generic types match?
You can convert the enum to the class:
public final class SettingsABC<T> {
public static final SettingsABC<Integer> A =
new SettingsABC<>(new SettingDefinition<>("A", 123));
public static final SettingsABC<String> B =
new SettingsABC<>(new SettingDefinition<>("B", "Hello"));
public static final SettingsABC<Boolean> C =
new SettingsABC<>(new SettingDefinition<>("C", false));
private final SettingDefinition<T> settingDefinition;
// private constructor, so nobody else would instantiate it
private SettingsABC(SettingDefinition<T> settingDefinition) {
this.settingDefinition = settingDefinition;
}
public SettingDefinition<T> getDefinition() {
return settingDefinition;
}
}
This way individual constants will be typed. Now you can use the type arguments for SettingService as well:
public static <T> T getSetting(SettingDefinition<T> setting) {
...
}
public static <T> void setSetting(SettingDefinition<T> setting, T value) {
...
}
Although it's not an enum anymore, it can be used mostly in the same way. If you need other methods which are usually available in enum, you can mimic them like this:
public String name() {
return settingDefinition.getName();
}
#Override
public String toString() {
return settingDefinition.getName();
}
// and so on
I have following code:
public class A {
private String type;
String getType() { return type;}
}
Now in many code places I have code like this
switch (a.geType()) {
case "A" : return new Bla();
case "B" : return new Cop();
}
or somewhere else
switch (a.geType()) {
case "A" : return new Coda();
case "B" : return new Man();
}
(Note that I know I should use an Enumeration in production code).
What I want to achive is that when a new type is added to class A the compiler should flag all the switch statements that need to be adjusted?
Is there a java idiomatic way to do this?
when a new type is added to class A the compiler should flag all the switch statements that need to be adjusted?
A good approach to this would be replacing switch statements with a more robust implementation of multiple dispatch, such as the Visitor Pattern:
interface VisitorOfA {
Object visitA(A a);
Object visitB(B b);
}
class A {
Object accept(VisitorOfA visitor) {
return visitor.visitA(this);
}
}
class B extends A {
Object accept(VisitorOfA visitor) {
return visitor.visitB(this);
}
}
With this infrastructure in place, you can remove your switch statements, replacing them with implementations of the visitor:
Object res = a.accept(new VisitorOfA() {
public Object visitA(A a) { return new Bla(); }
public Object visitB(B b) { return new Cop(); }
});
When you add a new subtype to A, say, class C, all you need to do is adding a new method to VisitorOfA:
Object visitC(C c);
Now the compiler will spot all places where this new method has not been implemented, helping you avoid problems at runtime.
Don't forget about good old-fashioned polymorphism. Having a "type" field with switch statements in a class is often a smell that indicates that subclassing might be useful. Consider:
public abstract class CommonSuperClass {
public abstract One getOne();
public abstract Two getTwo();
}
public class A extends CommonSuperClass {
#Override public One getOne() { return new Bla(); }
#Override public Two getTwo() { return new Coda(); }
}
public class B extends CommonSuperClass {
#Override public One getOne() { return new Cop(); }
#Override public Two getTwo() { return new Man(); }
}
If you were to add a new subclass C, you're required to provide implementations for the abstract methods (unless you make C itself be abstract).
You could have a map of string / suppliers:
Map<String, Supplier<Object>> map = new HAshMap<> ();
map.put("A", Bla::new);
map.put("B", Cop::new);
And your sample code would become:
return map.get(a.getType()).get(); //need null check
In perspective of abstraction, there is another approach for you to use. One way is via Polymorphism as shown here.
Some simple example:
public void EverythingYouWant (Animal animal) {
return animal.move();
}
When it's more about refactoring replace type code/checking with State/Strategy patterns. It's good solution to first consider is there any reason that prevents subclassing.
With the introduction of generics, I am reluctant to perform instanceof or casting as much as possible. But I don't see a way around it in this scenario:
for (CacheableObject<ICacheable> cacheableObject : cacheableObjects) {
ICacheable iCacheable = cacheableObject.getObject();
if (iCacheable instanceof MyObject) {
MyObject myObject = (MyObject) iCacheable;
myObjects.put(myObject.getKey(), myObject);
} else if (iCacheable instanceof OtherObject) {
OtherObject otherObject = (OtherObject) iCacheable;
otherObjects.put(otherObject.getKey(), otherObject);
}
}
In the above code, I know that my ICacheables should only ever be instances of MyObject, or OtherObject, and depending on this I want to put them into 2 separate maps and then perform some processing further down.
I'd be interested if there is another way to do this without my instanceof check.
Thanks
You could use double invocation. No promises it's a better solution, but it's an alternative.
Code Example
import java.util.HashMap;
public class Example {
public static void main(String[] argv) {
Example ex = new Example();
ICacheable[] cacheableObjects = new ICacheable[]{new MyObject(), new OtherObject()};
for (ICacheable iCacheable : cacheableObjects) {
// depending on whether the object is a MyObject or an OtherObject,
// the .put(Example) method will double dispatch to either
// the put(MyObject) or put(OtherObject) method, below
iCacheable.put(ex);
}
System.out.println("myObjects: "+ex.myObjects.size());
System.out.println("otherObjects: "+ex.otherObjects.size());
}
private HashMap<String, MyObject> myObjects = new HashMap<String, MyObject>();
private HashMap<String, OtherObject> otherObjects = new HashMap<String, OtherObject>();
public Example() {
}
public void put(MyObject myObject) {
myObjects.put(myObject.getKey(), myObject);
}
public void put(OtherObject otherObject) {
otherObjects.put(otherObject.getKey(), otherObject);
}
}
interface ICacheable {
public String getKey();
public void put(Example ex);
}
class MyObject implements ICacheable {
public String getKey() {
return "MyObject"+this.hashCode();
}
public void put(Example ex) {
ex.put(this);
}
}
class OtherObject implements ICacheable {
public String getKey() {
return "OtherObject"+this.hashCode();
}
public void put(Example ex) {
ex.put(this);
}
}
The idea here is that - instead of casting or using instanceof - you call the iCacheable object's .put(...) method which passes itself back to the Example object's overloaded methods. Which method is called depends on the type of that object.
See also the Visitor pattern. My code example smells because the ICacheable.put(...) method is incohesive - but using the interfaces defined in the Visitor pattern can clean up that smell.
Why can't I just call this.put(iCacheable) from the Example class?
In Java, overriding is always bound at runtime, but overloading is a little more complicated: dynamic dispatching means that the implementation of a method will be chosen at runtime, but the method's signature is nonetheless determined at compile time. (Check out the Java Language Specification, Chapter 8.4.9 for more info, and also check out the puzzler "Making a Hash of It" on page 137 of the book Java Puzzlers.)
Is there no way to combine the cached objects in each map into one map? Their keys could keep them separated so you could store them in one map. If you can't do that then you could have a
Map<Class,Map<Key,ICacheable>>
then do this:
Map<Class,Map<Key,ICacheable>> cache = ...;
public void cache( ICacheable cacheable ) {
if( cache.containsKey( cacheable.getClass() ) {
cache.put( cacheable.getClass(), new Map<Key,ICacheable>() );
}
cache.get(cacheable.getClass()).put( cacheable.getKey(), cacheable );
}
You can do the following:
Add a method to your ICachableInterface interface that will handle placing the object into one of two Maps, given as arguments to the method.
Implement this method in each of your two implementing classes, having each class decide which Map to put itself in.
Remove the instanceof checks in your for loop, and replace the put method with a call to the new method defined in step 1.
This is not a good design, however, because if you ever have another class that implements this interface, and a third map, then you'll need to pass another Map to your new method.
I want to have a Map object to contain specific value types.
Map<String,Object> foo = new HashMap<String,Object>();
foo.put("1",new Integer(1));
foo.put("2", new String("hello"):
for (Map.Entry<Integer, Integer> entry : foo.entrySet()) {
if(entry.getValue() instanceof String) {
//do something
}
else if(entry.getValue() instanceof Double) {
//throw Exception
}
}
You can see what i am trying to accomplish. I need specific Value types in my Map object so i don't have to put lots of if/else statements. How do i accomplish this ?
If you don't like the idea of having multiple maps, you can use the nice OOP feature called overriding.
see Override Methods for more information.
I would probably use composition pattern to wrap the value types and then define a interface for the action depending of the value types.
public abstract class ValueType {
public abstract void valueSpecificAction();
}
public class DoubleValueType extends ValueType {
private Double value;
public DoubleValueType(Double value) {
this.value = value;
}
public void valueSpecificAction() {
//do double specific actions
}
}
public class StringValueType extends ValueType {
private String value;
public void valueSpecificAction() {
//do string specific actions
}
}
You can then add the objects of type ValueType in the maps.
Map<String,ValueType> foo = new HashMap<String,ValueType>();
foo.put("key1", new DoubleValueType(42.0));
later you can get the value, which has the type ValueType with the public method valueSpecificAction() which do the stuff depending on the specific type.
I don't see how you can avoid to have an if-else somewhere in general.
Jan's solution would spare you the if-else only if the value types can be determine at compulation-time. That is the case in the toy example above but is it so in your actual problem?
If it is a matter of making the code look cleaner you can always hide the if-else and conversions in a set of private methods.
I want to be able to specify a list of keys and allowed values for each key programatically so that the code can be checked at compile time for errors and in the hope of better performance.
Imagine I am representing word in a database and each word has a number of features:
public class Word {
public Map<Feature, FeatureValue> features = new EnumMap<Feature, FeatureValue>();
}
And I have an enum class:
public enum Feature {
TYPE("Type") {
enum Value {
NOUN("Noun"),
VERB("Verb");
}
#Override
public Value[] getValues() {
return new Value[]{Value.NOUN, Value.VERB};
}
},
PLURALITY("Plurality") {
enum Value {
SING("Singular"),
PL("Plural");
}
#Override
public Value[] getValues() {
return new Value[]{Value.SING, Value.PL};
}
},
}
I would at least want to be able to do something like:
word.features.put(TYPE, TYPE.Value.NOUN);
word.features.put(PLURALITY, PLURALITY.Value.PL);
So that it's easy to see that the values match the key, but the enum within enum syntax doesn't seem to be allowed.
I also tried this:
TYPE("Type") {
public String NOUN = "Noun";
public String VERB = "Verb";
but I couldn't reference TYPE.NOUN since they aren't allowed to be static for some reason.
Please is there someone who know a good pattern to specifying something like this? I'm just worried if use strings in my code like
word.features.put(TYPE, "Noun");
I am asking for trouble with typos etc.
You can't do it like that but you can do it like this:
// define a type values as an enum:
enum TypeValue {
Noun, Verb
}
// define an attribute class parametrized by an enum:
public class Attribute<E extends Enum<E>> {
// define your attribute types as static fields inside this class
public static Attribute<TypeValue> Type = new Attribute<TypeValue>();
}
// and now define your method like this:
<E extends Enum<E>, Feature extends Attribute<E>> void put(Feature feature, E value) {
}
// you will then have a compilation error when trying to invoke the method with improper associated parameters.
// eg if we define
enum OtherValue { X }
features.put(Attribute.Type, TypeValue.Noun); // ok
features.put(Attribute.Type, OtherValue.X); // Fails