Reputation: 314
We have a class called Variable which represents a singlevalue or compound value. For example, it can hold an integer,or a boolean,or a String etc... (single valued) or some compound value which can be list of Strings, integers or other Variables.
We serialize these objects and in the stream all these values are represented as strings. Whenever we serialize or deserialize there is a type conversion happening.
There are also some optional features or ways you can fill values in these variables. For example you can define a Variable to be populated from a webpage - For a given Variable we query a cache to understand if it should be populated from a webpage. Whenever someone does getValue() on the Variable we populate the value.
We also want to track changes of some variables. For example, I can choose to record or do some action whenever the value of a variable is read or changed.
As you can see that this is a hierarchical structure because variable can contain other variables. We wanted to find the best way to solve this.
Currently we have only one class called Variable which has so many if/else conditions and the code is very complex. For example, getValue() code does the following:
if(query the cache to see if it needs population from webpage) do something else(---) do something else(if read should be recorded-find from cache) do something etc...
Is there any pattern to design my classes in such a way that all my population from webpage logic can go in to one class, tracking logic in some other class, type conversion logic in some other class etc... to make it more readable.
Upvotes: 2
Views: 2099
Reputation: 10475
Chain of Responsibility Each chained element in the Composite gets to do it's bit, but you have to spend some time configuring the runtime structure just so.
Possibly just a Composite or Observer for the getValue() scenario (but sounds more like Composite to me).
EDIT:
One could argue that the implementation below is in fact a case of "Chain of Responsibility", as a composite variable will delegate the responsibility of setting values to its children.
END EDIT
Here's a simple example using Observer and Composite. NOT TESTED just to give you the general feel for the solution...
I have not implemented stuff like serializing/deserializing.
In this solution you have compound values and atomic values, and you can add some observer to be executed before value is set.
package dk.asj.variables;
public abstract class VariableBase {
public interface Observer {
void onSet(final Value val, final VariableBase var);
}
private Observer obs = null;
public void setObserver(final Observer obs) {
this.obs = obs;
}
public void setValue(final Value val) {
if (obs != null) {
obs.onSet(val, this);
}
internalSetValue(val);
}
protected abstract void internalSetValue(final Value val);
public abstract Value getValue();
}
package dk.asj.variables;
import java.util.List;
public interface Value {
int getIntValue();
String getStringValue();
List<Value> getCompositeValue();
}
package dk.asj.variables;
public class SimpleVariable extends VariableBase {
private Value val = null;
@Override
protected void internalSetValue(final Value val) {
this.val = val;
}
@Override
public Value getValue() {
return val;
}
}
package dk.asj.variables;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class CompoundVariable extends VariableBase {
final List<VariableBase> children = new LinkedList<VariableBase>();
public void addChild(final VariableBase c) {
children.add(c);
}
@Override
protected void internalSetValue(final Value val) {
for (int i = 0; i < val.getCompositeValue().size(); ++i) {
children.get(i).setValue(val.getCompositeValue().get(i));
}
}
@Override
public Value getValue() {
final List<Value> res = new ArrayList<Value>(children.size());
for (final VariableBase var : children) {
res.add(var.getValue());
}
return new Value() {
@Override
public int getIntValue() {
throw new RuntimeException("This is a composite value");
}
@Override
public String getStringValue() {
throw new RuntimeException("This is a composite value");
}
@Override
public List<Value> getCompositeValue() {
return res;
}
};
}
}
Upvotes: 2
Reputation: 16526
I'm not sure if this answers your question, however, this could lead to some new ideas, here is what I came up with in a similar situation:
And this appeared to be quite a flexible approach - I even implemented a trivial dependency injection by introducing InjectionContext
which is a thread-safe context holding an object being wired.
You might want to have a look at an example of how this is used in a deployment tool I'm currently developing. Configuration management and shared application logic there is built upon these variables. Code is under bear.context package, but it's rather raw at the moment.
Upvotes: 0