Wicket generic components design pattern - java

Im implementing some generic components and I just wonder if my design patters makes sense and if there are any improvements that can be made. E.g., here is a generic panel that can be used to filter stuff:
/**
* Abstract class for textfields used for filtering. When overriding abstract method onUpdateFilter, the first thing
* that must be done is to set the paramsobject, or else filtering wont work.
* #author fred
*
*/
public abstract class FilterFormPanel extends Panel {
private static final long serialVersionUID = 1L;
private FilterForm filterForm;
private Object paramsObject; //this is object because paramsobjects differ depending on entity type
public FilterFormPanel(String id) {
super(id);
filterForm = new FilterForm("filterForm");
add(filterForm);
}
public String getFilterString(){
return filterForm.getFilterString();
}
public void setParamsObject(Object paramsObject){
this.paramsObject = paramsObject;
}
/**
*For developers to implement in class that contains the correct references to params and facade objects, dataviews etc.
*e.g. they could do params.setFilter(<reference to an instance of this class>.getFilterString() and ajax stuff too)
*/
public abstract void onUpdateFilter(AjaxRequestTarget target, Object paramsObject);
private class FilterForm extends Form<Void> {
private static final long serialVersionUID = 1L;
private transient String filterString;
public FilterForm(String id) {
super(id);
final TextField<String> filterTextField = new TextField<String>("filterTextField", new PropertyModel<String>(this, "filterString")); //textField for user to enter filter string
add(filterTextField);
add(new AjaxButton("filterButton") { //button to click for performing overriden method
private static final long serialVersionUID = 1L;
#Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
onUpdateFilter(target, paramsObject);
}
});
}
public String getFilterString(){
return filterString;
}
}
}
Used as follows in another class:
filterFormPanel = new FilterFormPanel("filterFormPanel"){
private static final long serialVersionUID = 1L;
#Override
public void onUpdateFilter(AjaxRequestTarget target, Object paramsObject) {
filterFormPanel.setParamsObject(params);
params.setFilterString(filterFormPanel.getFilterString());
//ajax stuff
target.addComponent(dataViewContainer);
nav.setVisible(dataProvider.size()!=0);
target.addComponent(nav);
emptyLabel.setVisible(dataProvider.size()==0);
target.addComponent(emptyLabel);
}
};
settingsContainer.add(filterFormPanel);
Its kind of annoying that one is forced to use the setParamsObject method first thing when one overrides the method. Is there a nicer way of achieving a reference to that object? And is this is general a sane way of implementing reusable and relatively generic components in wicket? Any feedback would be greatly appreciated, Im sure theres room for improvement here.
EDIT I: Just for some context, what Im doing is Im implementing pages like these
where I present the user with a dataview and options for filtering it. There are lots of pages for lots of different entities, but the GUI components can and should be made as generic as possible as to not violate DRY. The example code is obviously the filter textfield and button part of the page.
EDIT II: I want this component to be even more loosely coupled if possible, e.g. make it able to do completely different things, not just modifying a params object (say, e.g. have another case where I need to update TWO params objects, then I wont be able to use this panel). The onSubmit method in the form as it is now requires a reference to the objects to be used in the overriden method are known beforehand. Is there any way to not have it that way or set the presence and/or types of those objects dynamically?
EDIT III: The point is that this panels core function really is only to allow the user to
enter a string
notify and give access to that string to some other part of the system when the user clicks the button.
What that "other part of the system" does with the string should not really have to concern this panel, but as it is now, it is coupled to the params object upon which "the other part of the system" must perform some operation. It is this coupling I would like to get rid of if possible. I might as well want to use the string from this panel for just printing to console or use it for some other arbitrary task.

You can use the constructor of the class to set the object.
A more generic approach would be if you let paramsObject make use of the Java Generics (hence the name :)). You could superclass the entities or let them implement an interface.

I work for more than 3 years on a web application using Wicket (started using 1.3.x now under 1.4.x and planning to upgrade to 1.5.x in a few weeks). The approach you are using is the one we use internally. We often use abstract classes to represents common panels. The only thing we do is what rotsch said in his answer, we use a lot of generics to infer the type arguments as much as possible.

Actually, even though I didnt realize it at first, this could be achieved quite easily with by simply doing this:
/**
* Abstract class for textfields used for filtering.
* #author fred
*
*/
public abstract class FilterStringPanel extends Panel {
private static final long serialVersionUID = 1L;
private FilterForm filterForm;
public FilterStringPanel(String id) {
super(id);
filterForm = new FilterForm("filterForm");
add(filterForm);
}
public String getFilterString(){
return filterForm.getFilterString();
}
/**
*For developers to implement in class that contains the correct references to params and facade objects, dataviews etc.
*e.g. they could do params.setFilter(<reference to an instance of this class>.getFilterString() and ajax stuff too)
*/
public abstract void onUpdateFilter(AjaxRequestTarget target);
private class FilterForm extends Form<Void> {
private static final long serialVersionUID = 1L;
private transient String filterString;
public FilterForm(String id) {
super(id);
final TextField<String> filterTextField = new TextField<String>("filterTextField", new PropertyModel<String>(this, "filterString")); //textField for user to enter filter string
add(filterTextField);
add(new AjaxButton("filterButton") { //button to click for performing overriden method
private static final long serialVersionUID = 1L;
#Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
onUpdateFilter(target);
}
});
}
public String getFilterString(){
return filterString;
}
}
}
And then implementing it this way:
settingsContainer.add(new FilterStringPanel("filterStringPanel"){
private static final long serialVersionUID = 1L;
#Override
public void onUpdateFilter(AjaxRequestTarget target) {
params.setFilterString(getFilterString());
target.addComponent(dataViewContainer);
nav.setVisible(dataProvider.size()!=0);
target.addComponent(nav);
emptyLabel.setVisible(dataProvider.size()==0);
target.addComponent(emptyLabel);
}
});
This way we dont need to send any references to any objects (e.g. params objects or wicket components that needs to be targeted for updating with AJAX) and we can re-use this panel for whatever we want!

Related

Should constant details be part of the abstraction or the implementation?

I have a SaveSettings network operation in my application.
I am using an adapter so that I can be free to plug in another networking library in the future and not change any of my code.
public interface SaveSettingsOnServerAdapter {
void saveSettings(User user, OnSaveSettingsOnServerListener listener);
}
public class SaveSettingsOnServerAdapterImpl implements SaveSettingsOnServerAdapter {
private static final String API_USER_SESSION_HEADER = "Cookie";
private static final String API_SAVE_SETTINGS_PATH = "user/{user_id}/update";
private static final String API_SAVE_SETTINGS_USER_ID_PATH_PARAMETER = "user_id";
private static final String API_SAVE_SETTINGS_SYNC_WITH_CALENDAR_PARAMETER = "sync_with_calendar";
private static final String API_SAVE_SETTINGS_USE_MOBILE_NETWORK_PARAMETER = "use_mobile_network";
#Override
void saveSettings(User user, OnSaveSettingsOnServerListener listener) {
// Some implementation here, in my case - RETROFIT
}
}
My gut feeling tells me that those details outlined in the constants defined in the Implementation class are independent of whatever networking library I choose to use for the networking, so they should be part of the abstraction, right?
If so, should I declare SaveSettingsOnNetworkAdapter abstract and insert them there? And then SaveSettingsOnNetworkAdapterImpl extend it instead of implement it? Is it a problem to extend an abstract class instead of implementing an interface when employing the Adapter pattern?
Downgrading an interface to an abstract class isn't unproblematic. You force all future implementations to extend that class and only that class, whereas currently you can attach the interface to almost anything.
Plus, the parameters may be constant (as far as you can tell now) but is it really the responsibility of a SaveSettingsOnServerAdapter to keep & define them?
They are required in there for sure. But requirements can be provided by other places as well. The most versatile / abstract solution to that is to define another interface that provides the values and to add the dependency to that interface to the constructor of every SaveSettingsOnServerAdapter implementation.
If you don't like to repeat that constructor, that would be a good place to make an abstract class that implements SaveSettingsOnServerAdapterImpl and covers the common code to deal with the external interface.
But it's up to you. Whether you want more encapsulation and to which degree you want that depends on what you're working on. In a tiny tool that isn't going to change is pointless since abstractions aim to make change easy. And more abstraction also means more code and more code means more places that can break.
public interface ServerConfiguration {
String getHeader();
String getPath();
...
}
public enum DefaultServerConfiguration implements ServerConfiguration {
INSTANCE;
private static final String API_USER_SESSION_HEADER = "Cookie";
private static final String API_SAVE_SETTINGS_PATH = "user/{user_id}/update";
...
#Override
public String getHeader() {
return API_SAVE_SETTINGS_PATH;
}
#Override
public String getPath() {
return API_SAVE_SETTINGS_PATH;
}
...
}
public class SaveSettingsOnServerAdapterImpl implements SaveSettingsOnServerAdapter {
private final ServerConfiguration config;
public SaveSettingsOnServerAdapterImpl(ServerConfiguration config) {
this.config = config;
}
#Override
void saveSettings(User user, OnSaveSettingsOnServerListener listener) {
new Request().withHeader(config.getHeader());
}
}
public class UserOfAllThis {
new SaveSettingsOnServerAdapterImpl(DefaultServerConfiguration.INSTANCE);
}
The idea of abstraction is to make your code more generic. This should hide implementation as much as possible. Adding constants to the abstract layer will reveal some implementation details. The best way will probably be to add the constants to the implementation class or create a new class with these constants.

Serialization problems: objects that depend on each other and ArrayLists

I have to serialize a project and it's the first time I use serialization. After getting informed about it, I thought of two possible problems: my classes have atributes which type is another different class that has atributes which type is the first class (explained poorly, but can see in the code) and the fact that I use ArrayLists (which I've read can't be serialized). So I decided to try with a very simplified version of the project:
A group, this containts an ArrayList of Person:
public class Group implements Serializable {
private static final long serialVersionUID = 1L;
private Person leader;
private List<Person> members;
private int number;
public Group(Person leader, int number) {
this.leader = leader;
this.number = number;
this.members = new ArrayList<Person>();
this.members.add(leader);
}
public void addMember(Person p) {
this.members.add(p);
}
public int getNumber() {
return number;
}
}
A person, this contains an ArrayList of Groups:
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private List<Group> groups;
private String name;
public Person(String name) {
this.name = name;
this.groups = new ArrayList<Group>();
}
public Group createGroup(int number) {
Group g = new Group(this, number);
this.groups.add(g);
return g;
}
public void joinGroup(Group g) {
this.groups.add(g);
g.addMember(this);
}
}
And a main method which creates a few groups and people and uses writeObject() to write them into a file, plus another main that uses readObject() to get the objects back (it only uses readObject() and prints them).
I didn't expect this to work for the reasons mentioned above, but it worked perfectly, so I tried to serialize my main project (way more complex) but it didn't work (huge stack trace, simply saying "User", which is the equivalent to person, is not serializable).
Is there any reason for this or any major flaw that I should take into account?
I apologize for not including the two main methods I use, as well as none of the stacktrace or the main project, but I didn't want to make this question extremely long.
my classes have atributes which type is another different class that has atributes which type is the first class (explained poorly, but can see in the code)
Incomprehensible. Both Java and Serialization handle circular dependencies, if that's what you're talking about.
and the fact that I use ArrayLists (which I've read can't be serialized)
Wrong.
simply saying "User", which is the equivalent to person, is not serializable
So User doesn't implement Serializable.
Is there any reason for this or any major flaw that I should take into account?
Make User implement Serializable.Same for any other class that gives you the same message.
You need to read the Object Serialization Specification and the relevant Javadoc, and stop relying on arbitrary Internet rubbish.

Struts 2 and business objects

In a Struts 2 class where http get params are auto fetched by field variables. While there were repeated such class fields like userId,groupId, etc in many classes, I decided to make one business object class RequestParams in each class and put all the field there.
Then all my class will have just the RequestParams rp; with getRp(); and setRp(); the rp class will have the userId with getters / setters and all other fields.
Now I see I have to replace. e.g userId with getRp().getUserId(); at line 34 Now the code is looking ugly.
With this: messageId = ChatDao.saveMessage(userId,groupId , message);
would look like
rp.setMessageId( ChatDao.saveMessage(rp.getUserId(), rp.getGroupId(), rp.getMessag() ) );
what is a better way of doing such things?
public class SendMessageOrStartChatAction extends BaseActoinSupport{
private static final long serialVersionUID = 1L;
private int userId;
private int groupType;
private int groupId;
private String groupTitle;
private String groupMemberIds;
private int randomCode;
private String message;
private int messageId; //internal class ues
#Override
/** boo */
protected void doExecute() throws IOException {
//check if it had random code in db, (msg already saved in db)
if(ChatDao.randomCodeExists(randomCode)){
messageId = ChatDao.getMessageIdThatMatchesRandomCode(randomCode);
write(messageId);
}else{
if(groupId <= 0){
//create group
groupId = ChatDao.createChatGroup(userId, groupTitle, groupType);
String[] memberIdsArray = groupMemberIds.split("==");
ChatDao.addUsersToGroup(groupId, memberIdsArray);
}
//save message
messageId = ChatDao.saveMessage(userId,groupId , message);
// queued: may be put this in last viewed messages here. may be.
write(messageId);
}
}
}
Nothing wrong with this approach, if you aggregate a class and want to access its properties then public accessors are appropriate to you and you could also access them via OGNL. The action is on top of the valueStack, so expression will look much simpler "rp.userId" for example. Anyway there's no need to pass all params to the method, you can use a simplified method signature
ChatDao.saveMessage(rp);
and inside the method access those parameters.

How to use Java Enums being DRY with only a single parameter different between instantiations?

I'm trying to figure out if there is a clean way of doing this. I want to design an ENUM to maintain a list of constant values for different components in my application. Each enum would have the same configuration and same parameters, but would differ at the very least by component name.
In a normal Java class, I could build all the basic logic/code in a base abstract class, and have each component constants extend the abstract class and populate only its own pertinent information. However, Java enums do not allow extending existing classes.
Is there something I can do to avoid having to either push all my constants in a single Enum (ugggg!) or recreate the same enum class each time for each differing component? Definitely not DRY in that case, but I do not know how to avoid the issue.
For a quick use-case example off the top of my head. Say I want to keep a list of all my request mappings in an Enum for use elsewhere in my application. Fairly easy to design an enum that says:
public enum RequestMapping {
INDEX("index"),
GET_ALL_USERS( "getAllUsers");
private String requestMapping = "/users";
private String path;
RatesURI( String path ){
this.path = path;
}
public String getRequestMapping(){
return requestMapping;
}
public String getPath(){
return path;
}
public String getFullRequestPath(){
return requestMapping + "/" + path;
}
}
It becomes easy to use RequestMapping.GET_ALL_USERS.getFullRequestPath().
Now if I want to create this enum on a per-controller basis, I would have to recreate the entire Enum class and change the "requestMapping" value for each one. Granted, this enum has nearly no code in it, so duplicating it would not be difficult, but the concept still remains. The theoretical "clean" way of doing this would be to have an abstract AbstractRequestMapping type that contained all the methods, including an abstract getRequestMapping() method, and only have the extending Enums implement the controller-specific getReqeuestMapping(). Of course, since Enums cannot be extended, I can't think of a non DRY way of doing this.
Have you considered extending a class that takes Enum as a generic parameter? It is an amazingly flexible mechanism.
public class Entity<E extends Enum<E> & Entity.IE> {
// Set of all possible entries.
// Backed by an EnumSet so we have all the efficiency implied along with a defined order.
private final Set<E> all;
public Entity(Class<E> e) {
// Make a set of them.
this.all = Collections.unmodifiableSet(EnumSet.<E>allOf(e));
}
// Demonstration.
public E[] values() {
// Make a new one every time - like Enum.values.
E[] values = makeTArray(all.size());
int i = 0;
for (E it : all) {
values[i++] = it;
}
return values;
}
// Trick to make a T[] of any length.
// Do not pass any parameter for `dummy`.
// public because this is potentially re-useable.
public static <T> T[] makeTArray(int length, T... dummy) {
return Arrays.copyOf(dummy, length);
}
// Example interface to implement.
public interface IE {
#Override
public String toString();
}
}
class Thing extends Entity<Thing.Stuff> {
public Thing() {
super(Stuff.class);
}
enum Stuff implements Entity.IE {
One,
Two;
}
}
You can pass the nature of your implementation up to the parent class in many different ways - I use enum.class for simplicity.
You can even make the enum implement an interface as you can see.
The values method is for demonstration only. Once you have access to the Set<E> in the parent class you can provide all sorts of functionality just by extending Entity.
I will probably split the responsibilities into two parts:
Logic about how a request is structured, and put that into an immutable class.
Actual configurations of each request, stored in enums
The enum will then store an instance of that class, you can add new methods to the class, without modifying the different enums, as long as the constructor remains the same. Note that the class must be immutable, or your enum will not have a constant value.
You can use it like the:
ServiceRequest.INDEX.getRequest().getFullRequestPath()
With these classes:
public interface RequestType {
Request getRequest();
}
public class Request {
private final String requestMapping;
private final String path;
RatesURI(String requestMapping, String path){
this.requestMappint = requestMapping;
this.path = path;
}
public String getRequestMapping(){
return requestMapping;
}
public String getPath(){
return path;
}
public String getFullRequestPath(){
return requestMapping + "/" + path;
}
}
public enum ServiceRequest implements RequestType {
INDEX("index"),
GET_ALL_USERS( "getAllUsers");
private final Request;
ServiceRequest(String path) {
request = new Request("users/", path)
}
public String getRequest{
return request;
}
}
I think what you should be asking yourself is really why you want to use enums for this. First we can review some of the points that make Java enumerated types what they are.
Specifically
A Java enum is a class that extends java.lang.Enum.
Enum constants are static final instances of that class.
There is some special syntax to use them but that is all they boil down to. Because instantiating new Enum instances is disallowed outside of the special syntax (even with reflection, enum types return zero constructors) the following is also ensured to be true:
They can only be instantiated as static final members of the enclosing class.
The instances are therefore explicitly constant.
As a bonus, they are switchable.
What it really boils down to is what it is about the enums that makes them preferable over a simpler OOP design here. One can easily create a simple RequestMapping class:
/* compacted to save space */
public class RequestMapping {
private final String mapping, path;
public RequestMapping(String mapping, String path) {
this.mapping = mapping; this.path = path;
}
public String getMapping() {
return mapping; }
public String getPath() {
return path; }
public String getFullRequestPath() {
return mapping + "/" + path;
}
}
Which can easily be extended to break down the repeated code:
public class UserMapping extends RequestMapping {
public UserMapping(String path) {
super("/users", path);
}
}
/* where ever appropriate for the constants to appear */
public static final RequestMapping INDEX = new UserMapping("index"),
GET_ALL_USERS = new UserMapping("getAllUsers");
But I assume there is something about enums that is attractive to your design, such as the principle that instances of them are highly controlled. Enums cannot be created all willy-nilly like the above class can be. Perhaps it's important that there be no plausible way for spurious instances to be created. Of course anybody can come by and write in an enum with an invalid path but you can be pretty sure nobody will do it "by accident".
Following the Java "static instances of the outer class" enum design, an access modifier structure can be devised that generally abides by the same rule set as Enum. There are, however, two problems which we can't get around easily.
Two Problems
Protected modifier allows package access.
This can easily be surmounted initially by putting the Enum-analog in its own package. The problem becomes what to do when extending. Classes in the same package of the extended class will be able to access constructors again potentially anywhere.
Working with this depends on how stringent you want to be on creating new instances and, conversely, how clear the design ends up. Can't be a whole mess of scopes just so only a few places can do the wrong thing.
Static members are not polymorphic.
Enum surmounts this by not being extendable. Enum types have a static method values that appears "inherited" because the compiler inserts it for you. Being polymorphic, DRY and having some static features means you need instances of the subtype.
Defeating these two issues depends on how stringent you want your design to be and, conversely, how readable and stable you want your implementation to be. Trying to defy OOP principles will get you a design that's hard to break but totally explodes when you call that one method in a way you aren't supposed to (and can't prevent).
First Solution
This is almost identical to the Java enum model but can be extended:
/* 'M' is for 'Mapping' */
public abstract class ReturnMapping<M extends ReturnMapping> {
/* ridiculously long HashMap typing */
private static final HashMap <Class<? extends ReturnMapping>, List<ReturnMapping>>
VALUES = new HashMap<Class<? extends ReturnMapping>, List<ReturnMapping>>();
private final String mapping, path;
protected Mapping(String mapping, String path) {
this.mapping = mapping;
this.path = path;
List vals = VALUES.get(getClass());
if (vals == null) {
vals = new ArrayList<M>(2);
VALUES.put(getClass(), vals);
}
vals.add(this);
}
/* ~~ field getters here, make them final ~~ */
protected static <M extends ReturnMapping> List<M>(Class<M> rm) {
if (rm == ReturnMapping.class) {
throw new IllegalArgumentException(
"ReturnMapping.class is abstract");
}
List<M> vals = (List<M>)VALUES.get(rm);
if (vals == null) {
vals = new ArrayList<M>(2);
VALUES.put(rm, (List)vals);
}
return Collections.unmodifiableList(vals);
}
}
Now extending it:
public final class UserMapping extends ReturnMapping<UserMapping> {
public static final UserMapping INDEX = new UserMapping("index");
public static final UserMapping GET_ALL_USERS = new UserMapping("getAllUsers");
private UserMapping(String path) {
super("/users", path);
}
public static List<UserMapping> values() {
return values(UserMapping.class);
}
}
The huge static HashMap allows almost all of the values work to be done statically in the superclass. Since static members are not properly inherited this is the closest you can get to maintaining a list of values without doing it in the subclass.
Note there are two problems with the Map. The first is that you can call the values with ReturnMapping.class. The map should not contain that key (the class is abstract and the map is only added to in the constructor) so something needs to be done about it. Instead of throwing an exception you could also insert a "dummy" empty list for that key.
The other problem is that you can call values on the superclass before the instances of the subclass are instantiated. The HashMap will return null if this is done before the subclass is accessed. Static problem!
There is one other major problem with this design because the class can be instantiated externally. If it's a nested class, the outer class has private access. You can also extend it and make the constructor public. That leads to design #2.
Second Solution
In this model the constants are an inner class and the outer class is a factory for retrieving new constants.
/* no more generics--the constants are all the same type */
public abstract class ReturnMapping {
/* still need this HashMap if we want to manage our values in the super */
private static final HashMap <Class<? extends ReturnMapping>, List<Value>>
VALUES = new HashMap<Class<? extends ReturnMapping>, List<Value>>();
public ReturnMapping() {
if (!VALUES.containsKey(getClass())) {
VALUES.put(getClass(), new ArrayList<Value>(2));
}
}
public final List<Value> values() {
return Collections.unmodifiableList(VALUES.get(getClass()));
}
protected final Value newValue(String mapping, String path) {
return new Value(getClass(), mapping, path);
}
public final class Value {
private final String mapping, path;
private Value(
Class type,
String mapping,
String path) {
this.mapping = mapping;
this.path = path;
VALUES.get(type).add(this);
}
/* ~~ final class, field getters need not be ~~ */
}
}
Extending it:
public class UserMapping extends ReturnMapping {
public static final Value INDEX, GET_ALL_USERS;
static {
UserMapping factory = new UserMapping();
INDEX = factory.newValue("/users", "index");
GET_ALL_USERS = factory.newValue("/users", "getAllUsers");
}
}
The factory model is nice because it solves two problems:
Instances can only be created from within the extending class.
Anybody can create a new factory but only the class itself can access the newValue method. The constructor for Value is private so new constants can only be created by using this method.
new UserMapping().values() forces the values to be instantiated before returning them.
No more potential errors in this regard. And the ReturnMapping class is empty and instantiating new objects in Java is fast so I wouldn't worry about overhead. You can also easily create a static field for the list or add static methods such as in solution #1 (though this would deflate the design's uniformity).
There are a couple of downsides:
Can't return the subtyped values List.
Now that the constant values are not extended they are all the same class. Can't dip in to generics to return differently-typed Lists.
Can't easily distinguish what subtype a Value is a constant of.
But it's true this could be programmed in. You could add the owning class as a field. Still shaky.
Sum Of It
Bells and whistles can be added to both of these solutions, for example overriding toString so it returns the name of the instance. Java's enum does that for you but one of the first things I personally do is override this behavior so it returns something more meaningful (and formatted).
Both of these designs provide more encapsulation than a regular abstract class and most importantly are far more flexible than Enum. Trying to use Enum for polymorphism is an OOP square peg in a round hole. Less polymorphism is the price to pay for having enumerated types in Java.

Extending enum fields Java

I know that it isn't possible to extend enum in Java, but I am trying to find an elegant solution for the below
I am trying to model enums (or classes) which will contain http end points of various web services across regions, say I have service A and B, each will have 4 region specific end points in US, EU, JP or CN. (This is basically for some seperate debug code that I am writing, in production the end points will be picked from configuration)
I was hoping to do something like this (not compliant java code).
public enum IEndPoint {
NA_END_POINT,
EU_END_POINT,
JP_END_POINT,
CN_END_POINT,
}
public enum ServiceAEndPoint extends IEndPoint {
NA_END_POINT("http://A.com/");
EU_END_POINT("http://A-eu.com/");
JP_END_POINT("http://A-jp.com/");
CN_END_POINT("http://A-cn.com/");
}
I could do this using interfaces where I have a method for each region, but in my opinion the enum way is more expressive, is there any better way I could model this ? What I am looking for is if there is any better way to model the inheritence relation and also having the expressive power of enumerations.
ServiceAEndPoint.NA_END_POINT
vs
serviceAEndPoint.getNAEndPoint()
I'm assuming that you will also want a ServiceBEndPoint enum (and similar). In which case I don't think your model really makes that much sense.
IEndPoint is really an enumeration of the kind of environments/regions where a service might be running. It is not an enumeration of the services themselves. Each individual service (A, B or whatever) will have different addresses for each of the regions.
Therefore I would stick with just the IEndPoint enum, and then in some service-specific code have a lookup map that will give you the address for a given end-point. Something like this:
public enum IEndPoint {
NA_END_POINT,
EU_END_POINT,
JP_END_POINT,
CN_END_POINT,
}
public class ServiceABroker {
private static final Map<IEndPoint, String> addressesByEndPoint;
static {
addressesByEndPoint = new EnumMap<>();
addressesByEndPoint.put(NA_END_POINT, "http://A.com/");
addressesByEndPoint.put(EU_END_POINT, "http://A-eu.com/");
addressesByEndPoint.put(JP_END_POINT, "http://A-jp.com/");
addressesByEndPoint.put(CN_END_POINT, "http://A-cn.com/");
}
public String getAddressForEndPoint(IEndPoint ep) {
return addressesByEndPoint.get(ep);
}
}
If these are static final constants, then just put them in an interface. Name the interface something like IServiceAEndPointKeys, where the keys part is a convention.
Here's where I consider enums to be more appropriate and useful:
Example 1: File type. An enum containing jpg, pdf etc.
Example 2: Column definitions. If I have a table with 3 columns, I would write an enum declaring ID, Name, Description (for example), each one having parameters like column header name, column width and column ID.
Im not sure I understand you question, but you can add methods to an enum for example you could do something like the following:
public enum ServiceAEndPoint{
NA_END_POINT("http://A.com/");
EU_END_POINT("http://A-eu.com/");
JP_END_POINT("http://A-jp.com/");
CN_END_POINT("http://A-cn.com/");
private final String url;
private EndPoint(String url){
this.url=url;
}
public String getURL(){
return url;
}
}
Enums cannot be extended in such a manner, mostly because enums cannot be sub-classed or the constraints they must adhere to will not be possible to impose.
Instead leverage interfaces, like so
public interface IEndPoint;
public enum DefaultEndPoints implements IEndPoint {
NA_END_POINT,
EU_END_POINT,
JP_END_POINT,
CN_END_POINT,
}
public enum DefaultServiceEndPoints implements IEndPoint {
NA_END_POINT("http://A.com/");
EU_END_POINT("http://A-eu.com/");
JP_END_POINT("http://A-jp.com/");
CN_END_POINT("http://A-cn.com/");
}
public void doSomething(IEndPoint endpoint) {
...
}
The reason why one can't subclass in the manner you wish is related to the contract that enums will be both equal via .equals(object) and via ==. If you could subclass, would this make sense?
if ( (DefaultEndPoints)JP_END_POINT == (DefaultServiceEndPoints)JP_END_POINT) {
}
if you say "yes" then I would expect to be able to do this
DefaultEndPoint someEndpoint = DefaultServiceEndPoints.JP_END_POINT;
which would leave a door open for error, as there is no guarantee that a enum entry in one enum declaration is in the other enum declaration.
Could it be different? Perhaps, but it isn't, and changing it would definately introduce a lot of complications that would have to be thoroughly thought out (or it would open avenues to work around Java's strong static-type checking).
You may want to consider something like this:
public abstract class EndpointFactory {
public abstract String getNAEndPoint();
public abstract String getEUEndPoint();
}
public class ServiceAEndpointFactory extends EndpointFactory {
public static final String NA_END_POINT = "http://A.com/";
public static final String EU_END_POINT = "http://A-eu.com/";
public String getNAEndPoint() {
return ServiceAEndpointFactory.NA_END_POINT;
}
public String getEUEndPoint() {
return ServiceAEndpointFactory.EU_END_POINT;
}
}
public class ServiceBEndpointFactory extends EndpointFactory {
public static final String NA_END_POINT = "http://B.com/";
public static final String EU_END_POINT = "http://B-eu.com/";
public String getNAEndPoint() {
return ServiceAEndpointFactory.NA_END_POINT;
}
public String getEUEndPoint() {
return ServiceAEndpointFactory.EU_END_POINT;
}
}
Then you can refer to your strings directly like this:
ServiceAEndpointFactory.NA_END_POINT;
Or, you can use the base object if the type of service is not known until execution:
EndpointFactory ef1 = new ServiceAEndpointFactory();
String ep = ef1.getNAEndPoint();
The drawback of this is the redefinition of the get*Endpoint() functions in each sub-class. You could eliminate that by moving the static final variables to be not static in the base class and putting the getter/setter in the base class only one time. However, the drawback of that is you are not able to reference the values without instantiating an object (which essentially emulates what I find valuable with ENUMs).
How does a pattern like this appeal to you? I let the enum implement an interface and implement the interface in a Debug set and a Release set. The release set can then derive the property name from the enum name - which is neat.
public interface HasURL {
public String getURL();
}
public enum DebugEndPoints implements HasURL {
NA,
EU,
JP,
CN;
#Override
public String getURL() {
// Force debug to go to the same one always.
return "http://Debug.com/";
}
}
public enum NormalEndPoints implements HasURL {
NA,
EU,
JP,
CN;
final String url;
NormalEndPoints () {
// Grab the configured property connected to my name.
this.url = getProperty(this.name());
}
#Override
public String getURL() {
return url;
}
}

Categories

Resources