Needing to create an unspecified number of objects, I tried to create a builder that do that. All was well until I realized that my builder creates all objects with their properties having the same values.
So when I call the builder:
ValidationHelper v = new ValidationHelper.HelperBuilder()
.addHelper("ICAO Identifier", icaoIdentifier, rulesICAO)
.addHelper("Long Name", longName, rulesLongName)
.build();
... I'll have 2 objects and their properties will have values of the last object the builder was asked to create.
To start with, is factory builder the prudent approach to this? Secondly, is my builder salvageable?
Builder:
public class ValidationHelper {
private static ArrayList<HelperBuilder> validatorHelpers = new ArrayList();
public static class HelperBuilder {
private String txtFieldName;
private String txtFieldValue;
private List<Integer> valCodes = new ArrayList<Integer>();
private ArrayList<HelperBuilder> innerValidatorHelpers = new ArrayList<HelperBuilder>();
public HelperBuilder() {}
public final HelperBuilder addHelper(String txtFieldName, String txtFieldValue, int[] validationCodes) {
this.txtFieldName = txtFieldName;
this.txtFieldValue = txtFieldValue;
for( int i = 0; i < validationCodes.length; i++ ){
getValCodes().add((Integer) validationCodes[i]);
}
innerValidatorHelpers.add(this);
return this;
}
public final ValidationHelper build() {
return new ValidationHelper(this);
}
public String getTxtFieldName() {
return txtFieldName;
}
public String getTxtFieldValue() {
return txtFieldValue;
}
public List<Integer> getValCodes() {
return valCodes;
}
}//end HelperBuilder
private ValidationHelper(HelperBuilder helperBuilder) {
validatorHelpers = helperBuilder.innerValidatorHelpers;
}
public void setHelpers(ArrayList validatorHelpers) {
validatorHelpers = validatorHelpers;
}
public ArrayList getHelpers() {
return validatorHelpers;
}
}
EDIT/FIXED:
So for what it's worth, here's the revised builder. It needed another constructor that could properly initialize an instance of what it's supposed to build.
public class ValidationHelper {
private static ArrayList<HelperBuilder> validatorHelpers = new ArrayList();
public static class HelperBuilder {
private String txtFieldName;
private String txtFieldValue;
private List<Integer> valCodes = new ArrayList<Integer>();
private ArrayList<HelperBuilder> innerValidatorHelpers = new ArrayList<HelperBuilder>();
public HelperBuilder() {}
public HelperBuilder(String txtFieldName, String txtFieldValue, int[] validationCodes) {
this.txtFieldName = txtFieldName;
this.txtFieldValue = txtFieldValue;
for (int i = 0; i < validationCodes.length; i++) {
valCodes.add((Integer) validationCodes[i]);
}
}
public final HelperBuilder addHelper(String txtFieldName, String txtFieldValue, int[] validationCodes) {
innerValidatorHelpers.add( new HelperBuilder(txtFieldName, txtFieldValue, validationCodes) );
return this;
}
public final ValidationHelper build() {
return new ValidationHelper(this);
}
public String getTxtFieldName() {
return txtFieldName;
}
public String getTxtFieldValue() {
return txtFieldValue;
}
public List getValCodes() {
return valCodes;
}
}//end HelperBuilder
private ValidationHelper(HelperBuilder helperBuilder) {
validatorHelpers = helperBuilder.innerValidatorHelpers;
}
public ArrayList getHelpers() {
return validatorHelpers;
}
}
Each time you just overwrite the values in
private String txtFieldName;
private String txtFieldValue;
and the last one winns. So you create only 1 HelperInstance here
ValidationHelper v = new ValidationHelper.HelperBuilder()
and the fields name and value are overwritten each time you call addHelper(). But you need to create an instance for each "configuration". So addHelper should create a new Instance and add it into
private ArrayList<HelperBuilder> innerValidatorHelpers = ...;
If you want to build objects with different values you have to either
alter the builder between creating the objects so it will build something different.
instruct the builder to change the values automatically e.g. use a counter, or filename based on the date, or provide a list of values.
Related
public class GPSping {
private double pingLat;
private double pingLon;
private int pingTime;
}
The Trip class
public class Trip {
private ArrayList<GPSping> pingList;
public Trip() {
pingList = new ArrayList<>();
}
public Trip(ArrayList<GPSping> triplist) {
pingList = new ArrayList<>();
}
public ArrayList<GPSping> getPingList() {
return this.pingList;
}
public boolean addPing(GPSping p) {
int length = pingList.size();
int Time = pingList.get(length);
if (p.getTime() > this.pingList[length]) {
pinglist.add(p);
return True;
} else {
return False;
}
}
}
I am trying to add a GPS ping to this trip list but only if the time of p is after the last time in this trip list. I am very new to Java and am struggling with wrapping my head around the syntax some help would be greatly appreciated.
First element in List has index 0, to to get the last one:
int Time = pingList.get(length - 1);
But I think, it's better to store maxPingTime to check it before add new GPSping:
class Trip {
private final List<GPSping> pingList = new ArrayList<>();
private int maxPingTime = Integer.MIN_VALUE;
public List<GPSping> getPingList() {
return pingList.isEmpty() ? Collections.emptyList() : Collections.unmodifiableList(pingList);
}
public boolean addPing(GPSping p) {
if (p.getPingTime() <= maxPingTime)
return false;
pingList.add(p);
maxPingTime = p.getPingTime();
return true;
}
}
final class GPSping {
private final double pingLat;
private final double pingLon;
private final int pingTime;
public GPSping(double pingLat, double pingLon, int pingTime) {
this.pingLat = pingLat;
this.pingLon = pingLon;
this.pingTime = pingTime;
}
}
P.S. Pay attention on Encapsulation OOP principle: GPSping should be final and pingList should not be directly retrieved.
Im trying to get the id of a posto from an hashmap to compare the value in another class:
My class Posto:
public class Posto {
private int id;
private Point posicao;
private int capacidade;
private int quantidadeAtual;
private int gastoMedio;
public Posto(int id, Point posicao, int capacidade, int quantidadeAtual, int gastoMedio) {
this.id = id;
this.posicao = posicao;
this.capacidade = capacidade;
this.quantidadeAtual = quantidadeAtual;
this.gastoMedio = gastoMedio;
}
public int getPostoId() {
return id;
}
public void setPostoId(int id) {
this.id = id;
}
public Point getPostoPosicao() {
return posicao;
}
public void setPostoPosicao(Point posicao) {
this.posicao = posicao;
}
public int getPostoCapacidade() {
return capacidade;
}
public void setPostoCapacidade(int capacidade) {
this.capacidade = capacidade;
}
public int getPostoQuantidadeAtual() {
return quantidadeAtual;
}
public void setPostoQuantidadeAtual(int quantidadeAtual) {
this.quantidadeAtual = quantidadeAtual;
}
public int getPostoGastoMedio() {
return gastoMedio;
}
public void setPostoGastoMedio(int gastoMedio) {
this.gastoMedio = gastoMedio;
}
My MAIN class where i fill the hashmaps like this:
public class Main {
public static void main(String[] args) {
Central c = new Central( new Point(20, 300) );
setupCentral( c );
MenuCentral mc = new MenuCentral( c );
mc.menuPrincipal();
}
private static void setupCentral(Central c) {
//Posto p1 = new Posto(1,new Point(2,3),24,40,30);
c.addPosto(new Posto(1,new Point(10,10),10,200,180));
c.addPosto(new Posto(2,new Point(700,15),15,300,200));
}
}
And now my CENTRAL class where i have the method "addPosto" to fill the hashmap and i need the method "getPosto" to get the ids to compare in other class but i can't do it, i'm a little bit confused about the hashmaps.
public class Central {
private Point posicao;
private Map<Integer, Object> camioes = new HashMap<Integer,Object>( );
private Map<Integer,Object> postos = new HashMap<Integer,Object>( );
public Central(Point posicao) {
this.posicao = posicao;
}
public Point getPosicao() {
return posicao;
}
public void setPosicao(Point posicao) {
this.posicao = posicao;
}
public void addPosto( Posto p ){
postos.put(p.getPostoId(), p);
}
***public int getPosto (int id){
}***
}
Your Map has only one value type.
private final Map<Integer, Posto> postos = new HashMap<>();
And you only add this type.
public void addPosto( Posto p ){
postos.put(p.getPostoId(), p);
}
so it makes sense to expect this type.
public Posto getPosto(int id) {
return postos.get(id);
}
If you want to leave the Map as it is (which is a bad idea IMHO you can use an explicit cast)
public Posto getPosto(int id) {
return (Posto) postos.get(id);
}
This is needlessly verbose and error prone. At some point doing this will almost certainly lead to a bug which never needed to happen.
public Posto getPosto (int id)
{
return postos.get(id);
}
sice you are adding posto class to the hashmap use generics
private Map<Integer, Posto> camioes = new HashMap<Integer,Posto>( );
private Map<Integer,Posto> postos = new HashMap<Integer,Posto>( );
you can iterate the hashmap and get all the values
for (Integer key : camioes.keySet()) {
Posto p = postos.get(key);
System.out.println(p.getPostoId());
}
}
When i was reviewing Builder pattern in Josh's Bloch book, i came up with simpler implementation, but i'm not sure whether it's proper.
For example:
public class Test {
public static void main(String[] args) {
Numbers first = new Numbers.Builder().setD(3.14).build();
System.out.println(first);
Numbers second = new Numbers.Builder().setI(17).setF(1.24F).build();
System.out.println(second);
System.out.println(first);
}
}
final class Numbers {
private int i;
private long l;
private float f;
private double d;
private Numbers() {}
public static class Builder {
private final Numbers instance = new Numbers();
public Builder setI(int i) {
instance.i = i;
return this;
}
public Builder setL(long l) {
instance.l = l;
return this;
}
public Builder setF(float f) {
instance.f = f;
return this;
}
public Builder setD(double d) {
instance.d = d;
return this;
}
public Numbers build() {
return instance;
}
}
#Override
public String toString() {
return String.format("%4d %4d %7.3f %7.3f", i, l, f, d);
}
}
Is it can still be considered as a Builder pattern or i missed something?
EDIT
What about this?
//...
private Numbers() {}
private Numbers(Numbers o) {
i = o.i;
l = o.l;
f = o.f;
d = o.d;
}
public static class Builder {
private final Numbers instance = new Numbers();
//...
public Numbers build() {
return new Numbers(instance);
}
}
The problem with your code is that if you call build twice for the same Builder instance, you'll get the same Numbers instance. And if you call methods of the Builder after you called build and got the Numbers instance, you will change that instance. The instance created by the builder should be independent of the Builder once it's built.
How do you make these two array lists into one array list so that it still works in my arrayAdapter. I cant remember how to go about doing this.
public class MyArrayAdapter extends ArrayAdapter<String>{
Context context;
ArrayList<String>mtitle;
ArrayList<String>mdesc;
public MyArrayAdapter(Context c, ArrayList<String>title, ArrayList<String>desc) {
super(c, R.layout.single_row, R.id.viewtitle, title);
...
use java been type array list.
Like:-
public class JBContacts {
public String strID = "";
public String strTitle = "";
public String getStrID() {
return strID;
}
public void setStrID(String strID) {
this.strID = strID;
}
public String getStrTitle() {
return strTitle;
}
public void setStrTitle(String strTitle) {
this.strTitle = strTitle;
}
}
ArrayList<JBContacts> mArrayListContacts;
JBContacts mJbContacts;
mArrayListContacts = new ArrayList<JBContacts>();
mJbContacts = new JBContacts();
strId = mJsonObjectContacts.getString(Constants.TAG_ID);
strTitle = mJsonObjectContacts
.getString(Constants.TAG_TITLE);
mJbContacts.setStrTitle(strTitle);
public class ObjectToProxy
{
List<ObjectToProxy> potentiallyCircularReference;
}
public class SubClass
{
private ObjectToProxy aField;
Set<ObjectToProxy> aSetOfObjectsToProxy;
}
public class CrazyObject
{
Map<Integer, ObjectToProxy> proxiedObjects;
List<SubClass> manySubClasses;
}
public class ComplexObject
{
List<CrazyObject> crazyObjects;
private final ObjectToProxy storedAsAField;
}
I have a complex object graph. Lets say it looks a little like the one above (even though it is much deeper in the real system). I would like, after being given ComplexObject, to be able to traverse the object graph and replace all ObjectToProxys with a proxying object.
Is this doable?
The reason for this is that we have some pretty big nasty objects which we partially load on the servers side (legacy, you're my friend!). We have a semi-working solution that uses proxying on the client side to go through and loads the full object when needed.
edit I would like to replace every instance of ObjectProxy connected to a ComplexObject.
public static class ProxyObject extends ObjectToProxy
{
private final ObjectToProxy objectToProxy;
public ProxyObject(ObjectToProxy objectToProxy)
{
this.objectToProxy = objectToProxy;
}
#Override
public String toString()
{
return "ProxyObject";
}
}
public static class ObjectToProxy
{
List<ObjectToProxy> potentiallyCircularReference;
public ObjectToProxy()
{
potentiallyCircularReference = new ArrayList<>();
potentiallyCircularReference.add(this);
}
#Override
public String toString()
{
return "ObjectToProxy";
}
}
public static class SubClass
{
ObjectToProxy aField;
Set<ObjectToProxy> aSetOfObjectsToProxy;
}
public static class CrazyObject
{
Map<Integer, ObjectToProxy> proxiedObjects;
List<SubClass> manySubClasses;
public CrazyObject()
{
proxiedObjects = new HashMap<>();
proxiedObjects.put(1, new ObjectToProxy());
}
}
public static class ComplexObject
{
List<CrazyObject> crazyObjects;
final ObjectToProxy storedAsAField;
public ComplexObject()
{
this.storedAsAField = new ObjectToProxy();
crazyObjects = new ArrayList<>();
crazyObjects.add(new CrazyObject());
}
#Override
public String toString()
{
return "myField: " + storedAsAField.toString();
}
}
public static void main(String[] args) throws Exception
{
ComplexObject obj = new ComplexObject();
Set<Object> visitedObjects = Sets.newIdentityHashSet();
Queue<Object> objectsToVisit = new LinkedList<>();
visitedObjects.add(obj);
objectsToVisit.add(obj);
while (!objectsToVisit.isEmpty())
{
handleFields(objectsToVisit.poll(), visitedObjects, objectsToVisit);
}
System.out.println(obj.toString());
}
private static void handleFields(Object obj, Set<Object> visitedObjects, Queue<Object> objectsToVisit) throws Exception
{
List<Field> fields = getAllFields(obj);
for (Field field : fields)
{
field.setAccessible(true);
Object fieldValue = field.get(obj);
if (fieldValue != null && !visitedObjects.contains(fieldValue))
{
if (fieldValue instanceof Object[])
{
visitedObjects.add(fieldValue);
Object[] array = (Object[])fieldValue;
for (Object arrayObj : array)
{
if (arrayObj != null && !objectsToVisit.contains(arrayObj))
{
visitedObjects.add(arrayObj);
if (!DontLookAt.contains(arrayObj.getClass()))
objectsToVisit.add(arrayObj);
}
}
}
else
{
if (!DontLookAt.contains(fieldValue.getClass()))
objectsToVisit.add(fieldValue);
}
if (fieldValue.getClass().equals(ObjectToProxy.class))
{
field.set(obj, new ProxyObject((ObjectToProxy)fieldValue));
}
else if (fieldValue instanceof ObjectToProxy[])
{
ObjectToProxy[] array = (ObjectToProxy[])fieldValue;
for (int i = 0; i < array.length; i++)
{
if (array[i] != null)
array[i] = new ProxyObject(array[i]);
}
}
}
}
}
private static final Set<Class> DontLookAt = getDontLookAtSet();
private static Set<Class> getDontLookAtSet()
{
Set<Class> set = new HashSet<>();
set.add(Long.class);
set.add(Boolean.class);
set.add(Integer.class);
set.add(String.class);
set.add(Byte.class);
set.add(Double.class);
set.add(Float.class);
set.add(Class.class);
return set;
}
private static List<Field> getAllFields(Object obj) throws Exception
{
List<Field> output = new ArrayList<>();
Class klazz = obj.getClass();
while (!klazz.equals(Object.class))
{
Field[] fields = klazz.getDeclaredFields();
output.addAll(Arrays.asList(fields));
klazz = klazz.getSuperclass();
}
return output;
}
For anyone wondering, The above simulates and does what I'm after. I'm sure it isn't perfect, but it is good enough for my purposes.