Try to sort classes by enumeration type

environment

Java on this page 1.8 It is assembled with.

Change before

If you have the following interface

Logic.java


public interface Logic {
	public void printSkyColor();
}

It was implemented like this. It feels like calling the implementation class differently.

	public void execute(String type) {
		Logic logic = null;
		if("S".equals(type)){
			logic = new SunnyLogic();
		}else if("C".equals(type)){
			logic = new CloudyLogic();
		}else if("R".equals(type)){
			logic = new RainyLogic();
		}else{
			throw new UnsupportedOperationException("unknown type : "+ type);
		}

		//SunnyLogic -> "It is Blue."Is output as standard
		//CloudyLogic-> "It is White."Is output as standard
		//RainyLogic-> "It is Gray."Is output as standard
		logic.printSkyColor();
	}

You can think of this as "enumerating" the logic corresponding to the type, right? Is it a little cleaner if you use enums well?

I thought, and actually tried it.

Enumization

First, create an enumeration class like this.

LogicConstant.java


public enum LogicConstant {
	SUN("S", new SunnyLogic()),
	CLOUD("C", new CloudyLogic()),
	RAIN("R", new RainyLogic()),
	;

	private final String type;
	private final Logic logic;

	private LogicConstant(String type, Logic logic) {
		this.type = type;
		this.logic = logic;
	}

	public static Logic ofLogic(String type) {
		for(LogicConstant c : values()){
			if(c.type.equals(type)){
				return c.logic;
			}
		}
		return null;
	}
}

The side that uses the above enum looks like this.

	public void execute(String type) {
		Logic logic = LogicConstant.ofLogic(type);
		if(logic == null){
			throw new UnsupportedOperationException("unknown type : "+ type);
		}

		//System.out.print(logic.hashCode()+":");
		logic.printSkyColor();
	}

It still works, but it works, ** The logic class instance is decisively different from the one before the change. ** ** ** Before the change, an instance is created every time, but after the change, it is the same instance. ** ** That is the point.

As you can see from the source,

//System.out.print(logic.hashCode()+":");

You can check the hash value by uncommenting this comment out on the user side.


The same instance is more convenient! There may be a case like This time, I will try to make it a separate instance by saying "follow before change".

Enumization part 2

Separate instantiation means defining only the class to be generated It's like this because it means new when calling.

LogicConstant.java


public enum LogicConstant {
	SUN("S", SunnyLogic.class),
	CLOUD("C", CloudyLogic.class),
	RAIN("R", RainyLogic.class),
	;

	private final String type;
	private final Class<? extends Logic> logic;

	private LogicConstant(String type, Class<? extends Logic> logic) {
		this.type = type;
		this.logic = logic;
	}

	public static Logic ofLogic(String type) {
		for(LogicFactory c : values()){
			if(c.type.equals(type)){
				try {
					return c.logic.newInstance();
				} catch (InstantiationException | IllegalAccessException e) {
					e.printStackTrace();
					return null;
				}
			}
		}
		return null;
	}
}

A little more remodeling from here.

return c.logic.newInstance();

First Class's newInstance () is deprecated in java9

Class getDeclaredConstructor (). newInstance ()

Replace it here as specified in javadoc in java9.

In addition, parameters can be passed at the time of constructor anyway So rewrite it so that the parameters are routed.

Since another instance is created for each call, the class name is also set to Factory.

Finally Fixed to throw an exception instead of returning null. (Thank you for your advice!)

Reflecting this ... This is the finished product.

LogicFactory.java


public enum LogicFactory {
	SUN("S", SunnyLogic.class),
	CLOUD("C", CloudyLogic.class),
	RAIN("R", RainyLogic.class),
	;

	private final String type;
	private final Class<? extends Logic> logic;

	private LogicFactory(String type, Class<? extends Logic> logic) {
		this.type = type;
		this.logic = logic;
	}

	public static Logic ofLogic(String type, Object... initargs) {
		for(LogicFactory c : values()){
			if(c.type.equals(type)){
				try {
					return c.logic.getDeclaredConstructor().newInstance(initargs);
				} catch (
					InstantiationException | IllegalAccessException | IllegalArgumentException |
					InvocationTargetException | NoSuchMethodException | SecurityException e
				) {
					e.printStackTrace();
					throw new IllegalArgumentException("type : "+type, e);
				}
			}
		}
		throw new UnsupportedOperationException("unknown type : "+ type);
	}
}

Following these corrections The null judgment on the caller side is no longer necessary, so delete it.


I personally think that this is a little cleaner.

Well, this kind of refactoring work is often said to be a hobby. I think that it should be organized as much as possible in consideration of operation and maintenance, and that it should be consciously coded.

Studying or reviewing enums? I will describe it for some time.

Recommended Posts

Try to sort classes by enumeration type
Try to sort classes by enumeration type
java: How to write a generic type list [Note]
Try to implement UIAlertController by separating files
Enum (enumeration type)
I want to sort by tab delimited by ruby
[Java] Enumeration type
Try to display prime numbers using boolean type
Try to release gem
How to type backslash \