I've recently read the books Clean Code and Effective Java. I am a huge fan of Object-Oriented Java.
I am trying to write as professionally as possible. I have been an Android Developer for 20 months now.
Here is a class that I wrote that is supposed to communicate with Google Static Maps API. Is this code Clean, Effective and SOLID?
Also, there are a few nested classes here, should I create a package out of this or contain it in a single file as it is right now?
import static com.example.constants.GoogleStaticMapsAPIKeys.*;
public final class ParametrizedStaticMapsRequest {
private static final int DEFAULT_ZOOM_LEVEL = 15;
private static final int DEFAULT_WIDTH = 2 * SizeInPixels.MIN_WIDTH;
private static final int DEFAULT_HEIGHT = SizeInPixels.MIN_WIDTH;
private LatLngPoint centerValue = new LatLngPoint(0, 0);
private GoogleMapsImageFormat imageFormat = GoogleMapsImageFormat.JPG;
private GoogleMapsZoomLevel zoomLevel = new GoogleMapsZoomLevel(DEFAULT_ZOOM_LEVEL);
private SizeInPixels size = new SizeInPixels(DEFAULT_WIDTH, DEFAULT_HEIGHT);
private ParametrizedStaticMapsRequest() {
throw new AssertionError();
}
private ParametrizedStaticMapsRequest(Builder builder) {
centerValue = builder.centerValue;
imageFormat = builder.imageFormat;
zoomLevel = builder.zoomLevel;
size = builder.size;
}
@Override
public String toString() {
final String AMPERSAND = "&";
final String EQUALS = "=";
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(API_ROOT);
stringBuilder.append(AMPERSAND);
stringBuilder.append(PARAMETER_API_KEY);
stringBuilder.append(EQUALS);
stringBuilder.append(GOOGLE_STATIC_MAPS_API_KEY);
stringBuilder.append(AMPERSAND);
stringBuilder.append(PARAMETER_CENTER);
stringBuilder.append(EQUALS);
stringBuilder.append(this.centerValue.toString());
stringBuilder.append(AMPERSAND);
stringBuilder.append(PARAMETER_FORMAT);
stringBuilder.append(EQUALS);
stringBuilder.append(this.imageFormat.toString());
stringBuilder.append(AMPERSAND);
stringBuilder.append(PARAMETER_ZOOM);
stringBuilder.append(EQUALS);
stringBuilder.append(this.zoomLevel.toString());
stringBuilder.append(AMPERSAND);
stringBuilder.append(PARAMETER_SIZE);
stringBuilder.append(EQUALS);
stringBuilder.append(this.size.toString());
return stringBuilder.toString();
}
public static class Builder {
private LatLngPoint centerValue;
private GoogleMapsImageFormat imageFormat;
private GoogleMapsZoomLevel zoomLevel;
private SizeInPixels size;
public ParametrizedStaticMapsRequest build() {
return new ParametrizedStaticMapsRequest(this);
}
public Builder imageFormat(GoogleMapsImageFormat imageFormat) {
this.imageFormat = imageFormat;
return this;
}
public Builder zoomLevel(GoogleMapsZoomLevel zoomLevel) {
this.zoomLevel = zoomLevel;
return this;
}
public Builder size(SizeInPixels size) {
this.size = size;
return this;
}
public Builder centerValue(LatLngPoint centerValue) {
this.centerValue = centerValue;
return this;
}
} // End of Builder
public enum GoogleMapsImageFormat {
PNG(PARAMETER_FORMAT_PNG),
JPG(PARAMETER_FORMAT_JPEG),
GIF(PARAMETER_FORMAT_GIF);
private final String text;
private GoogleMapsImageFormat(final String text) {
this.text = text;
}
@Override
public String toString() {
return text;
}
}
private final class GoogleMapsZoomLevel {
public static final int MIN_ZOOM_LEVEL = 0;
public static final int MAX_ZOOM_LEVEL = 21;
private int zoomLevel;
private static final String ERROR_ZOOM_LEVEL_OUT_OF_BOUNDS = "Zoom level should be within " + MIN_ZOOM_LEVEL + " and " + MAX_ZOOM_LEVEL;
private GoogleMapsZoomLevel() {
throw new AssertionError();
}
public GoogleMapsZoomLevel(int zoomLevel) {
if(zoomLevel < 0 || zoomLevel > 21) {
throw new IllegalArgumentException(ERROR_ZOOM_LEVEL_OUT_OF_BOUNDS);
}
this.zoomLevel = zoomLevel;
}
public String toString() {
return String.valueOf(zoomLevel);
}
}
private final class SizeInPixels {
public static final int MIN_WIDTH = 180;
public static final int MIN_HEIGHT = 1;
private static final String ERROR_WIDTH_MUST_BE_VALID = "Width must be more than " + MIN_WIDTH + " and less than " + Integer.MAX_VALUE;
private static final String ERROR_HEIGHT_MUST_BE_VALID = "Height must be more than " + MIN_HEIGHT + " and less than " + Integer.MAX_VALUE;
private int width;
private int height;
private SizeInPixels(){
throw new AssertionError();
}
public SizeInPixels(int width, int height){
setWidth(width);
setHeight(height);
}
public void setWidth(int width) {
if(width <= MIN_WIDTH || width > Integer.MAX_VALUE) {
throw new IllegalArgumentException(ERROR_WIDTH_MUST_BE_VALID);
}
this.width = width;
}
private void setHeight(int height) {
if(height <= MIN_HEIGHT || height > Integer.MAX_VALUE) {
throw new IllegalArgumentException(ERROR_HEIGHT_MUST_BE_VALID);
}
this.height = height;
}
@Override
public String toString() {
return width + "x" + height;
}
}
private final class LatLngPoint {
public static final double MIN_LONGITUDE = -180;
public static final double MAX_LONGITUDE = 180;
public static final double MIN_LATITUDE = -90;
public static final double MAX_LATITUDE = 90;
private static final String ERROR_LONGITUDE_OUT_OF_BOUNDS = "Longitude should be within " + MIN_LONGITUDE + " and " + MAX_LONGITUDE;
private static final String ERROR_LATITUDE_OUT_OF_BOUNDS = "Latitude should be within " + MIN_LATITUDE + " and " + MAX_LATITUDE;
private double longitude;
private double latitude;
private LatLngPoint() {
throw new AssertionError();
}
public LatLngPoint(double latitude, double longitude){
setLatitude(latitude);
setLongitude(longitude);
}
private void setLatitude(double latitude) {
if(latitude < MIN_LATITUDE || latitude > MAX_LATITUDE) {
throw new IllegalArgumentException(ERROR_LATITUDE_OUT_OF_BOUNDS);
}
this.latitude = latitude;
}
public void setLongitude(double longitude) {
if(longitude < MIN_LONGITUDE || longitude > MAX_LONGITUDE) {
throw new IllegalArgumentException(ERROR_LONGITUDE_OUT_OF_BOUNDS);
}
this.longitude = longitude;
}
@Override
public String toString() {
return latitude + "," + longitude;
}
} // End of LatLngPoint
} // End of Class
public final class GoogleStaticMapsAPIKeys {
private GoogleStaticMapsAPIKeys() {
throw new AssertionError();
}
public static final String GOOGLE_STATIC_MAPS_API_KEY = "asd";
public static final String API_ROOT = "https://maps.googleapis.com/maps/api/staticmap?";
public static final String PARAMETER_CENTER = "center";
public static final String PARAMETER_ZOOM = "zoom";
public static final String PARAMETER_ZOOM_DEFAULT_VALUE = "15";
public static final String PARAMETER_SIZE = "size"; // example: 500x400
public static final String PARAMETER_FORMAT = "format"; // PNG, JPEG, GIF
public static final String PARAMETER_API_KEY = "key";
public static final String PARAMETER_FORMAT_PNG = "png";
public static final String PARAMETER_FORMAT_JPEG = "jpg";
public static final String PARAMETER_FORMAT_GIF = "gif";
}