I am now making a DiffUtil class to update only changed items in the RecyclerView.
I have seen several other sample code.
When comparing two objects, they compared unique values such as id defined in the Model(Data) class in areItemsTheSame().
However, I think it is difficult to assign an id or unique value to the List, or the code is messy.
Do I have to define and compare id like this?
Do I really need to define a unique Id variable in the Model class that separates each object?
Or shouldn't I use simply the equals()?
Using this Is it not just comparing the address of the object, but also the contents of the object?
As an additional question
What is the difference between DiffUtil.CallBack and DiffUtil.ItemCallBack?
This is my code.
RoutineModel.java
public class RoutineModel {
private ArrayList<RoutineDetailModel> routineDetailModels;
private String routine;
public RoutineModel(ArrayList<RoutineDetailModel> items, String routine) {
this.routine = routine;
this.routineDetailModels = items;
}
public ArrayList<RoutineDetailModel> getDetailItemList() {
return routineDetailModels;
}
public int getDetailItemSize() {
return routineDetailModels.size();
}
public String getRoutine() {
return routine;
}
public void setRoutine(String routine) {
this.routine = routine;
}
}
RoutineDiffUtil.java
public class RoutineDiffUtil extends DiffUtil.Callback {
private final List<RoutineModel> oldRoutineList;
private final List<RoutineModel> newRoutineList;
public RoutineDiffUtil(ArrayList<RoutineModel> oldRoutineList, ArrayList<RoutineModel> newRoutineList) {
this.oldRoutineList = oldRoutineList;
this.newRoutineList = newRoutineList;
}
#Override
public int getOldListSize() {
return oldRoutineList.size();
}
#Override
public int getNewListSize() {
return newRoutineList.size();
}
#Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return oldRoutineList.equals(newRoutineList);
}
#Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
return oldRoutineList.equals(newRoutineList);
}
}
You got wrong the meaning of areItemsTheSame() and areContentsTheSame() callbacks. As you see, there are oldItemPosition and newItemPosition arguments in them. You should use them to compare specific items – not lists themselves.
In areItemsTheSame() you have to check whether model at "old" position in the old list equals a model at "new" position in the new list. This is how DiffUtil knows if it has to make reordering animations.
areContentsTheSame() will be called for two items if and only if you return true for them in the previous callback. Here you have to check whether visual representation of "old" and "new" models is the same. This is how DiffUtil knows if it has to make "item changing" animations.
To compare two models you have to override equals() and hashCode(). There you specify conditions under which you consider two models the same. For example, if they have same routine. I don't the know context of your task so I can't tell you exactly how to implement them, but usually you just compare all fields. Probably adding an id field is a good idea too. Then you can consider models "equal" if they have same id. And in hashCode() you can just return Objects.hash(id).
Now, speaking about your question about ItemCallback. Formally, here is the explanation from docs:
DiffUtil.Callback serves two roles - list indexing, and item diffing. ItemCallback handles just the second of these, which allows separation of code that indexes into an array or List from the presentation-layer and content specific diffing code.
Practically, ItemCallback just has less methods to implement and is used together with AsyncListDiffer. It's just because missing methods are already implemented under the hood in AsyncListDiffer.
You have to override the equals and hashcodes of your model classes.
RoutineModel:
class RoutineModel {
private ArrayList<RoutineDetailModel> routineDetailModels;
private String routine;
public RoutineModel(ArrayList<RoutineDetailModel> items, String routine) {
this.routine = routine;
this.routineDetailModels = items;
}
public ArrayList<RoutineDetailModel> getDetailItemList() {
return routineDetailModels;
}
public int getDetailItemSize() {
return routineDetailModels.size();
}
public String getRoutine() {
return routine;
}
public void setRoutine(String routine) {
this.routine = routine;
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
RoutineModel that = (RoutineModel) o;
return Objects.equals(routineDetailModels, that.routineDetailModels) &&
Objects.equals(routine, that.routine);
}
#Override
public int hashCode() {
return Objects.hash(routineDetailModels, routine);
}
}
RoutineDiffUtil:
public class RoutineDiffUtil extends DiffUtil.Callback {
private final List<RoutineModel> oldRoutineList;
private final List<RoutineModel> newRoutineList;
public RoutineDiffUtil(ArrayList<RoutineModel> oldRoutineList, ArrayList<RoutineModel> newRoutineList) {
this.oldRoutineList = oldRoutineList;
this.newRoutineList = newRoutineList;
}
#Override
public int getOldListSize() {
return oldRoutineList.size();
}
#Override
public int getNewListSize() {
return newRoutineList.size();
}
#Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return oldRoutineList.get(oldItemPosition).getRoutine().equals(newRoutineList.get(newItemPosition).getRoutine());
}
#Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
return oldRoutineList.get(oldItemPosition).equals(newRoutineList.get(newItemPosition));
}
}
And don't forget to override the equals and hashcode of your RoutineDetailModel.
Related
I have two objects(which contain of list of objects) of same class.I need to find whether both are same or not.
Consider below example:
class Device {
String deviceName;
String devLocation;
String devType;
String devID;
public String getDeviceName() {
return deviceName;
}
public void setDeviceName(String deviceName) {
this.deviceName = deviceName;
}
public String getDevLocation() {
return devLocation;
}
public void setDevLocation(String devLocation) {
this.devLocation = devLocation;
}
public String getDevType() {
return devType;
}
public void setDevType(String devType) {
this.devType = devType;
}
public String getDevId() {
return devID;
}
public void setDevId(String devId) {
this.devID = devId;
}
}
class DevList {
List<Device> deviceList;
public List<Device> getDevices() {
return deviceList;
}
public void setDevices(List<Device> deviceList) {
this.deviceList = deviceList;
}
}
Need to compare two objects of DevList class.
Will get a new DevList object for every regular interval of time.
Every time i need to verify current object with previous object and update DB if there is any difference else ignore.
The list(deviceList) in current and previous objects might not be in same order.
for example:
Consider below two objects which are in Json format(Please ignore json format errors).
Object 1:
{
"devList":[
{
"deviceName":"ABC",
"devLocation":"India",
"devType":"Router",
"devID":"1111"
},
{
"deviceName":"XYZ",
"devLocation":"India",
"devType":"Router",
"devID":"2222"
}
]
}
Object 2:
{
"devList":[
{
"deviceName":"XYZ",
"devLocation":"India",
"devType":"Router",
"devID":"2222"
},
{
"deviceName":"ABC",
"devLocation":"India",
"devType":"Router",
"devID":"1111"
}
]
}
We can do it by iterating lists in both objects by checking devID. But complexity would be M*N.
Is there any other way?
You could override equals in Device to compare each Device object.
Something similar
class Device {
...
...
//your getter and setter
public boolean equals (Object obj)
{
if (Device.class != obj.getClass()) {
return false;
}
if (obj instanceof Device) {
return (deviceName.equals(((Device) obj).deviceName) &&
devLocation.equals(((Device) obj).devLocation) &&
devType.equals(((Device) obj).devType) &&
devID.equals(((Device) obj).devID));
}
return false;
}
}
Now, call removeAll, which will remove same object list from List1
List1.equals(List2);
So, if both list has same object list, then List1 would be empty.
Step 1: First override equal method in Device Class
Step 2: override the equal() in your DevList class with
with below logic
1. use one Set collection
2. add object into it from 1st Object's list
3. check size() of set
3. add object from 2nd Object's list
5. check size() of set every time you add from 2nd list
6. if more than the size of one list it is not equal(As Set will not keep duplicate value)
I have an Item object having 4 String fields and 3 boolean fields.
I have to construct this object based on the 3 boolean variables.
The target is whenever any one of the boolean variable is true we have to create the object having that/those boolean variable set.
If for any situation none of the boolean variables are true, we wont create the object.
I am using a COR to check whether any of the boolean fields will be set or not based on some business logic.
I was trying this with builder, but then I have to construct so many objects and later discard them when none of the boolean variables found true.
Can anyone have any better idea, to solve this kind of problem ?
Well thanks for the 2 delete flag for this question. Thank for the thoughts on this question as well.
I did something to achieve what I want. Which is quite flexible I believe. Only part if there is a dependency on If loop, but that is acceptable since Report class can have extra boolean so when that class is changed, it's builder should be touched to cater that change. Rest this is flexible which I wanted.
public class Report {
private String acftNo;
private Date plannedDate;
private String plannedStn;
private Integer mntncId;
private Set<String> capableStations;
private String routedStn;
private boolean isRoutedNEQPlannedStn; //Inconsistency type 1
private boolean isCapableAtPlannedStn; //Inconsistency type 2
private boolean isPlannedOrRoutedStationExists; //Inconsistency type 3/5
public Report(String acftNo, Integer mntncId) {
super();
this.acftNo = acftNo;
this.mntncId = mntncId;
}
public Report(String acftNo, Date plannedDate, String plannedStn,
Integer mntncId) {
super();
this.acftNo = acftNo;
this.plannedDate = plannedDate;
this.plannedStn = plannedStn;
this.mntncId = mntncId;
}
//setters and getters. Removed for space.
public static Report buildReport(Maintenance<?> task, Set<InconsistencyReport> enumSet) {
Report temp = new Report(task.getAssignment().getAircraftNumber(),task.getAssignment().getMntncScheduleDate(),
task.getAssignment().getStationCode(),task.getAssignment().getMntncId());
temp.setCapableStations(InconsistencyReport.getCapableStations(task));
for(InconsistencyReport ir : enumSet)
{
if(ir.compareTo(InconsistencyReport.ROUTED_STN_NEQ_PLANNED_STN)==0)
temp.setRoutedNEQPlannedStn(true);
if(ir.compareTo(InconsistencyReport.ITEM_NT_CAPABLE_AT_PLANNED_STN)==0)
temp.setCapableAtPlannedStn(true);
if(ir.compareTo(InconsistencyReport.NO_ROUTD_STN_ON_A_DATE)==0)
temp.setPlannedOrRoutedStationExists(true);
}
return temp;
}
}
calculateInconsitencyReport() method which will decide whether to create object or not.
public class InconsistencyReportChain {
public enum InconsistencyReport implements InconsistencyReportIface {
ROUTED_STN_NEQ_PLANNED_STN {
#Override
public boolean findInconsistency(Maintenance<?> task ) {
if(!validate(task))
return false;
//some logic
return true;
return false;
}
},
ITEM_NT_CAPABLE_AT_PLANNED_STN {
#Override
public boolean findInconsistency(Maintenance<?> task) {
if(!validate(task))
return false;
//some logic
return true;
return false;
}
},
NO_ROUTD_STN_ON_A_DATE {
#Override
public boolean findInconsistency(Maintenance<?> task) {
if(!validate(task))
return false;
//some logic
return true
return false;
}
};
#Override
public boolean validate(Maintenance<?> task) {
return !(null == task.getAssignment());
}
static Set<String> getCapableStations(Maintenance<?> task)
{
Set<String> capableStations = newHashSet();
if(task.getCapStationList() != null)
{
capableStations.addAll(Arrays.asList(task.getCapStationList().split(StringConstants.COMMA_SPLIT_REGEX)));
}
if(task.getCapStationClassList() != null)
{
Map<String, List<String>> stationClassMap = CacheManager.get(STN_CLASS.name());
List<String> stationClass = Arrays.asList(task.getCapStationClassList().split(StringConstants.COMMA_SPLIT_REGEX));
for(String stnClass : stationClass)
{
capableStations.addAll(stationClassMap.get(stnClass));
}
}
return capableStations;
}
}
public static Report calculateInconsitencyReport(Maintenance<?> task) {
Set<InconsistencyReport> enumSet = null;
for(InconsistencyReport iReport : InconsistencyReport.values())
{
if(iReport.findInconsistency(task))
{
if(null==enumSet)
enumSet = EnumSet.of(iReport);
else
enumSet.add(iReport);
}
}
if(null!= enumSet && enumSet.size() > 0)
return Report.buildReport(task,enumSet);
return null;
}
}
Helper Interface:
public interface InconsistencyReportIface {
public boolean findInconsistency(Maintenance<?> task );
public boolean validate(Maintenance<?> task );
}
Details of class logic is teared off because of security.
What is the problem? Just create your object when one of your booleans is true.
if(bool1 || bool2 || bool3) {
item = new Item(str1, str2, str3, str4, bool1, bool2, bool3);
}
From what I understand of your description:
a) you will have some bools that will determine wether you create a certain object or not.
b) you may have to include some more bools into the "check protocol"
c) you have to do this checking in a loop where
i/ you check for the bool variable
ii/ you check if the object had been created previously
I still don't quite get it yet, but.. that looks pretty straight forward to me. Let's say your bools are stored in a boolean array boolean[] bools and your strings in a string array String[] strings (which, btw, I don't know what they are used for). You are saying to check if every bool is true and then create an object based on that result.
boolean[] bools = new boolean[] { ... };
String[] strings = new String[] { ... };
boolean checks = false;
for(int i = 0; i<bools.length && !checks; i++)
checks = bools[i];
//so far we will have processed if any of the bools was false, which was your condition
if(checks)
Object object = new Object(); //create your desired object
I don't understand why you would need to check if the object has been constructed previously, though, so I didn't include it in my suggestion :P
I need to build a process which will validate a record against ~200 validation rules. A record can be one of ~10 types. There is some segmentation from validation rules to record types but there exists a lot of overlap which prevents me from cleanly binning the validation rules.
During my design I'm considering a chain of responsibility pattern for all of the validation rules. Is this a good idea or is there a better design pattern?
Validation is frequently a Composite pattern. When you break it down, you want to seperate the what you want to from the how you want to do it, you get:
If foo is valid
then do something.
Here we have the abstraction is valid -- Caveat: This code was lifted from currrent, similar examples so you may find missing symbology and such. But this is so you get the picture. In addition, the
Result
Object contains messaging about the failure as well as a simple status (true/false).
This allow you the option of just asking "did it pass?" vs. "If it failed, tell me why"
QuickCollection
and
QuickMap
Are convenience classes for taking any class and quickly turning them into those respected types by merely assigning to a delegate. For this example it means your composite validator is already a collection and can be iterated, for example.
You had a secondary problem in your question: "cleanly binding" as in, "Type A" -> rules{a,b,c}" and "Type B" -> rules{c,e,z}"
This is easily managed with a Map. Not entirely a Command pattern but close
Map<Type,Validator> typeValidators = new HashMap<>();
Setup the validator for each type then create a mapping between types. This is really best done as bean config if you're using Java but Definitely use dependency injection
public interface Validator<T>{
public Result validate(T value);
public static interface Result {
public static final Result OK = new Result() {
#Override
public String getMessage() {
return "OK";
}
#Override
public String toString() {
return "OK";
}
#Override
public boolean isOk() {
return true;
}
};
public boolean isOk();
public String getMessage();
}
}
Now some simple implementations to show the point:
public class MinLengthValidator implements Validator<String> {
private final SimpleResult FAILED;
private Integer minLength;
public MinLengthValidator() {
this(8);
}
public MinLengthValidator(Integer minLength) {
this.minLength = minLength;
FAILED = new SimpleResult("Password must be at least "+minLength+" characters",false);
}
#Override
public Result validate(String newPassword) {
return newPassword.length() >= minLength ? Result.OK : FAILED;
}
#Override
public String toString() {
return this.getClass().getSimpleName();
}
}
Here is another we will combine with
public class NotCurrentValidator implements Validator<String> {
#Autowired
#Qualifier("userPasswordEncoder")
private PasswordEncoder encoder;
private static final SimpleResult FAILED = new SimpleResult("Password cannot be your current password",false);
#Override
public Result validate(String newPassword) {
boolean passed = !encoder.matches(newPassword,user.getPassword());
return (passed ? Result.OK : FAILED);
}
#Override
public String toString() {
return this.getClass().getSimpleName();
}
}
Now here is a composite:
public class CompositePasswordRule extends QuickCollection<Validator> implements Validator<String> {
public CompositeValidator(Collection<Validator> rules) {
super.delegate = rules;
}
public CompositeValidator(Validator<?>... rules) {
super.delegate = Arrays.asList(rules);
}
#Override
public CompositeResult validate(String newPassword) {
CompositeResult result = new CompositeResult(super.delegate.size());
for(Validator rule : super.delegate){
Result temp = rule.validate(newPassword);
if(!temp.isOk())
result.put(rule,temp);
}
return result;
}
public static class CompositeResult extends QuickMap<Validator,Result> implements Result {
private Integer appliedCount;
private CompositeResult(Integer appliedCount) {
super.delegate = VdcCollections.delimitedMap(new HashMap<PasswordRule, Result>(), "-->",", ");
this.appliedCount = appliedCount;
}
#Override
public String getMessage() {
return super.delegate.toString();
}
#Override
public String toString() {
return super.delegate.toString();
}
#Override
public boolean isOk() {
boolean isOk = true;
for (Result r : delegate.values()) {
isOk = r.isOk();
if(!isOk)
break;
}
return isOk;
}
public Integer failCount() {
return this.size();
}
public Integer passCount() {
return appliedCount - this.size();
}
}
}
and now a snippet of use:
private Validator<String> pwRule = new CompositeValidator<String>(new MinLengthValidator(),new NotCurrentValidator());
Validator.Result result = pwRule.validate(newPassword);
if(!result.isOk())
throw new PasswordConstraintException("%s", result.getMessage());
user.obsoleteCurrentPassword();
user.setPassword(passwordEncoder.encode(newPassword));
user.setPwExpDate(DateTime.now().plusDays(passwordDaysToLive).toDate());
userDao.updateUser(user);
Chain of responsibility implies that there is an order in which the validations must take place. I would probably use something similar to the Strategy pattern where you have a Set of validation strategies that are applied to a specific type of record. You could then use a factory to examine the record and apply the correct set of validations.
I am validating the status of a record retrieved from the DB by defining an ENUM as below
public enum RecordStatusEnum {
CREATED("CREATED"),
INSERTED("INSERTED"),
FAILED("FAILED");
private String recordStatusValue;
RecordStatusEnum (String status) {
recordStatusValue= status;
}
public boolean isSuccess() {
return (this.equals(CREATED) || this.equals(INSERTED));
}
}
The method isSuccess() is being used to check the status of the retrieved record ( column status from employee)
if (!(employee.getStatus().isSuccess())) {
// return error
}
As per the new requirement, there are a set of conditions introduced say A,B and C; and for them there is a column in the Employee table 'condition'.
So I need to retrieve the status as well as the condition and see if it belongs to a set which has the combination of both.
For eg : isSuccess() should check if in the following:
CREATED and A
CREATED and B
INSERTED and C
This must be achieved such that it is easy for me to add a new combination say 'INSERTED and B' into the list easily.
What is the best approach for the above problem?
Note : in the actual business scenario there are a whole lot more statuses and checks (eg isFailed() canBeModified() etc) with many different combinations
And any method can be suggested even if it doesn't use ENUMS. I mentioned ENUMS, because I dont want to deviate much from the existing implementation
There are many possibilities, but you could do like this (I removed the String status, which doesn't add any value since it's equal to the name of the enum):
public enum RecordStatusEnum {
CREATED(Condition.A, Condition.B),
INSERTED(Condition.C),
FAILED();
private Set<Condition> successConditions;
RecordStatusEnum(Condition... successConditions) {
this.successConditions = EnumSet.copyOf(Arrays.asList(successConditions));
}
public boolean isSuccess(Condition c) {
return successConditions.contains(c);
}
}
EDIT:
Example with two sets of conditions:
public enum RecordStatusEnum {
CREATED(EnumSet.of(Condition.A, Condition.B),
EnumSet.of(Condition.C)),
INSERTED(EnumSet.of(Condition.C),
EnumSet.of(Condition.B),
FAILED(EnumSet.noneOf(Condition.class),
EnumSet.noneOf(Condition.class));
private Set<Condition> successConditions;
private Set<Condition> modificationConditions;
RecordStatusEnum(Set<Condition> successConditions,
Set<Condition> modificationConditions) {
this.successConditions = successConditions;
this.modificationConditions = modificationConditions;
}
public boolean isSuccess(Condition c) {
return successConditions.contains(c);
}
public boolean canBeModified(Condition c) {
return modificationConditions.contains(c);
}
}
You could also compare the ordinal values, like so:
public enum RecordStatusEnum {
CREATED,
INSERTED,
UPDATED,
NEW,
FAILED,
FAILED_NO_DB,
FAILED_CONSTRAINT_VIOLATION;
public boolean isPersisted(RecordStatusEnum status) {
return status.ordinal < NEW.ordinal;
}
public boolean isError(RecordStatusEnum status){
return status.ordinal >= FAILED.ordinal;
}
}
I'm trying to define a class (or set of classes which implement the same interface) that will behave as a loosely typed object (like JavaScript). They can hold any sort of data and operations on them depend on the underlying type.
I have it working in three different ways but none seem ideal. These test versions only allow strings and integers and the only operation is add. Adding integers results in the sum of the integer values, adding strings concatenates the strings and adding an integer to a string converts the integer to a string and concatenates it with the string. The final version will have more types (Doubles, Arrays, JavaScript-like objects where new properties can be added dynamically) and more operations.
Way 1:
public interface DynObject1 {
#Override public String toString();
public DynObject1 add(DynObject1 d);
public DynObject1 addTo(DynInteger1 d);
public DynObject1 addTo(DynString1 d);
}
public class DynInteger1 implements DynObject1 {
private int value;
public DynInteger1(int v) {
value = v;
}
#Override
public String toString() {
return Integer.toString(value);
}
public DynObject1 add(DynObject1 d) {
return d.addTo(this);
}
public DynObject1 addTo(DynInteger1 d) {
return new DynInteger1(d.value + value);
}
public DynObject1 addTo(DynString1 d)
{
return new DynString1(d.toString()+Integer.toString(value));
}
}
...and similar for DynString1
Way 2:
public interface DynObject2 {
#Override public String toString();
public DynObject2 add(DynObject2 d);
}
public class DynInteger2 implements DynObject2 {
private int value;
public DynInteger2(int v) {
value = v;
}
#Override
public String toString() {
return Integer.toString(value);
}
public DynObject2 add(DynObject2 d) {
Class c = d.getClass();
if(c==DynInteger2.class)
{
return new DynInteger2(value + ((DynInteger2)d).value);
}
else
{
return new DynString2(toString() + d.toString());
}
}
}
...and similar for DynString2
Way 3:
public class DynObject3 {
private enum ObjectType {
Integer,
String
};
Object value;
ObjectType type;
public DynObject3(Integer v) {
value = v;
type = ObjectType.Integer;
}
public DynObject3(String v) {
value = v;
type = ObjectType.String;
}
#Override
public String toString() {
return value.toString();
}
public DynObject3 add(DynObject3 d)
{
if(type==ObjectType.Integer && d.type==ObjectType.Integer)
{
return new DynObject3(Integer.valueOf(((Integer)value).intValue()+((Integer)value).intValue()));
}
else
{
return new DynObject3(value.toString()+d.value.toString());
}
}
}
With the if-else logic I could use value.getClass()==Integer.class instead of storing the type but with more types I'd change this to use a switch statement and Java doesn't allow switch to use Classes.
Anyway... My question is what is the best way to go about something thike this?
What you are trying to do is called double dispatch. You want the method called to depend both on the runtime type of the object it's called on, and on the runtime type of its argument.
Java and other C derivatives support single dispatch only, which is why you need a kludge like the visitor pattern you used in option 1. This is the common way of implementing it. I would prefer this method because it uses no reflection. Furthermore, it allows you to keep each case in its own method, without needing a big "switchboard" method to do the dispatching.
I'd choose the second option, with the third, I'd better be using generics so you don't rely on that Enum. And with the first option you could be implementing methods for the rest of your life. Anyways you could use "instanceof" operator for Class matching.