I have a piece of code where two objects (incoming request object and a profile object) are to be checked for matching conditions.
So the first method is as below where I check whether the profile object will be applied to incoming request :
public Response apply(Request request) {
..// some logic
Profile profile = getProfile();
if( isProfileApplicableOnRequest(profile, request) ) {
}
return null;
}
private boolean isProfileApplicableOnRequest(Profile profile, Request request) {
List<BigInteger> applicability = profile.getApplicability();
return (applicability != null && applicability.contains(BigInteger.ONE))
|| profileMatchesResourceType(profile.getResourceTypes(), request.getResourceType())
|| profileMatchesOperation(profile.getOperations(), request.getOperation())
|| profileMatchesToParameter(profile.getResourceIDs(), request.getTo())
|| profileMatchesReleaseVersion(profile.getReleaseVersions(), request.getReleaseVersionIndicator())
);
}
So here we take values set in request object and match whether its present in the profile object. The profile object can have a Lists in which a parameter from request is checked for.
The functions above in conditional are like:
private boolean profileMatchesResourceType(
List<BigInteger> primitiveProfileResourceTypes,
BigInteger requestResourceType
){
return (true
&& primitiveProfileResourceTypes != null
&& requestResourceType != null
&& primitiveProfileResourceTypes.contains(requestResourceType)
);
}
private boolean profileMatchesToParameter(
List<String> primitiveProfileResourceIds,
String toParameter
){
return (true
&& primitiveProfileResourceIds != null
&& toParameter != null
&& primitiveProfileResourceIds.contains(toParameter)
);
}
private boolean profileMatchesOperation(
List<BigInteger> primitiveProfileOPerations,
BigInteger requestOperation
) {
return (true
&& primitiveProfileOPerations != null
&& requestOperation != null
&& primitiveProfileOPerations.contains(requestOperation)
);
}
Here, since all my match functions are similar I used generics to provide a single method:
private <T> boolean profileMatches(
List<T> primitiveProfileAttributes,
T oneM2MParam
){
return (true
&& primitiveProfileAttributes != null
&& oneM2MParam != null
&& primitiveProfileAttributes.contains(oneM2MParam)
);
}
With this my method looks like:
private boolean isProfileApplicableOnRequest(Profile profile, Request request) {
List<BigInteger> applicability = profile.getApplicability();
return (applicability != null && applicability.contains(BigInteger.ONE))
|| profileMatches(profile.getResourceTypes(), request.getResourceType())
|| profileMatches(profile.getOperations(), request.getOperation())
|| profileMatches(profile.getResourceIDs(), request.getTo())
|| profileMatches(profile.getReleaseVersions(), request.getReleaseVersionIndicator()
);
}
Could I refactor to improve the readability also for this with same profileMatches being called serially ?
EDIT: Data model
Currently in my implementation, we have a standard XSD/JSON Schema defined by the specifications and I generate Java classes from Schema and keeping those in a jar file.
So both Request and Profile are part of the generated Java classes. So for Request if we want more member variables we have created a wrapper around it extending the default generated Request object. So it's treated like a DTO currently. Profile is kept as it was generated which holds standard just getter/setters.
So currently, its mostly suggested in answers to shift logic to Profile class as it seems like a Domain object. But in CRUD request for Profile resource, it's passed in the payload over the wire.
So will it be recommended to create a wrapper for Profile and add domain logic methods to it (keeping transient variables if newly added variables) ??
EDIT: Functionality
Now, the profiling is full functionality being implemented. Like for various different types of Request there can be template method to apply Profile with minor variations inside template.
So in the code above apply() method is the template method and the isProfileApplicableOnRequest will have variations like for Subscription request little different logic.
In the above context of data model and functionality will it make sense to shift responsibility on the Profile object ?
profileMatchesin some kind of loop? If so: loops in Java or C# are a run-time mechanics, generics require the type to be known at compile time. Hence, to make this work in a loop, you could implementprofileMatcheswithout generics with parameters of typeobject. Then, you add a run-time check that the passed parameters have the expected types. Finally, callingprofileMatchesin a loop where the input comes from someList<Tuple<T,T>>should do it. But I think it is debatable if the result will be really more readable.