How can I use fetchMap() with a RecordMapper? - java

I know I can fetch a map something like this:
this.ctx.select(
shopSubscription.field(SHOP_SUBSCRIPTION.SHOP_ID),
shopSubscription.field(SHOP_SUBSCRIPTION.PAYMENT_GATEWAY_SUBSCRIPTION_ID),
shopSubscription.field(SHOP_SUBSCRIPTION.ADMIN_TOOL_FEATURE_TYPE_ID),
PAYMENT_GATEWAY_SUBSCRIPTION.SUBSCRIPTION_ID_TOKEN
)
.from(PAYMENT_GATEWAY_SUBSCRIPTION)
.join(shopSubscription)
.on(PAYMENT_GATEWAY_SUBSCRIPTION.ID.eq(shopSubscription.field(SHOP_SUBSCRIPTION.PAYMENT_GATEWAY_SUBSCRIPTION_ID))
.and(PAYMENT_GATEWAY_SUBSCRIPTION.PAYMENT_GATEWAY_TYPE_ID.eq(paymentGatewayType)))
.fetchMap(PAYMENT_GATEWAY_SUBSCRIPTION.PAYMENT_GATEWAY_TYPE_ID, ShopSubscriptionDTO.class);
but to detect issues at compile time I'd prefer if I could additionally add a RecordMapper to this query.
So is there a way to call fetchMap() but also provide a RecordMapper?
What I'm thinking of would look something like this:
this.ctx.select(
shopSubscription.field(SHOP_SUBSCRIPTION.SHOP_ID),
shopSubscription.field(SHOP_SUBSCRIPTION.PAYMENT_GATEWAY_SUBSCRIPTION_ID),
shopSubscription.field(SHOP_SUBSCRIPTION.ADMIN_TOOL_FEATURE_TYPE_ID),
PAYMENT_GATEWAY_SUBSCRIPTION.SUBSCRIPTION_ID_TOKEN
)
.from(PAYMENT_GATEWAY_SUBSCRIPTION)
.join(shopSubscription)
.on(PAYMENT_GATEWAY_SUBSCRIPTION.ID.eq(shopSubscription.field(SHOP_SUBSCRIPTION.PAYMENT_GATEWAY_SUBSCRIPTION_ID))
.and(PAYMENT_GATEWAY_SUBSCRIPTION.PAYMENT_GATEWAY_TYPE_ID.eq(paymentGatewayType)))
// For each record apply the map() function
.map(new RecordMapper<Record<?>, ShopSubscriptionDTO>() {
#Override
public ShopSubscriptionDTO map(Record<?> record) {
ShopSubscriptionDTO shopSubscriptionDto = new ShopSubscriptionDTO();
shopSubscriptionDto.setShopId(record.getValue(SHOP_SUBSCRIPTION.SHOP_ID)
// ...
return shopSubscriptionDto;
}
});
// Fetch the result into a map where the key is SHOP_SUBSCRIPTION.ADMIN_TOOL_FEATURE_TYPE_ID
.fetchMap(SHOP_SUBSCRIPTION.ADMIN_TOOL_FEATURE_TYPE_ID);

Since there are quite a lot of different implementations of fetchMap() I didn't see that there is fetchMap(Field<K>, RecordMapper<? super R, R>) too. So just going with that helps solving this issue:
// ...
.fetchMap(ADMIN_TOOL_ADD_ON.ADMIN_TOOL_ADD_ON_TYPE_ID, new RecordMapper<Record, AdminToolAddOnDTO>() {
#Override
public AdminToolAddOnDTO map(Record record) {
AdminToolAddOnDTO dto = new AdminToolAddOnDTO();
dto.setId(record.getValue(ADMIN_TOOL_ADD_ON.ID));
dto.setAdminToolFeatureTypeId(record.getValue(ADMIN_TOOL_ADD_ON.ADMIN_TOOL_FEATURE_TYPE_ID));
dto.setAdminToolAddOnTypeId(record.getValue(ADMIN_TOOL_ADD_ON.ADMIN_TOOL_ADD_ON_TYPE_ID));
dto.setPrice(record.getValue(ADMIN_TOOL_ADD_ON.PRICE));
dto.setCountryId(record.getValue(ADMIN_TOOL_ADD_ON.COUNTRY_ID));
dto.setAddOnIdToken(record.getValue(ADMIN_TOOL_ADD_ON_TYPE.ADD_ON_ID_TOKEN));
return dto;
}
});

java 8 or higher
.fetchMap(CN_TASKS.AGENTID,
r -> new CnTaskMessage(r.getValue(CN_TASKS.CN_TASKID), r.getValue(CN_TASKS.TASK_TYPE),
r.getValue(CN_TASKS.STATUS)));

Related

avoiding if conditions for similar type of checks

Is there anyway to avoid these if conditions? because there may be different type of objects coming in.
if ("OpenOrder".equals(order.getClass().getSimpleName())) {
return OpenOrderBuilder.createOFSMessage((OpenOrder) order); //Returns String
}
if ("ExecutionOrder".equals(order.getClass().getSimpleName())) {
return ExecutionOrderBuilder.createOFSMessage((ExecutionOrder) order); //Returns String
}
You can use a Router pattern to do this. Simple add the computations in a Map like this:
Map<String, Function> router = new HashMap<>();
router.put("OpenOrder", (value) -> OpenOrderBuilder.createOFSMessage((OpenOrder) value));
router.put("ExecutionOrder", (value) -> ExecutionOrderBuilder.createOFSMessage((ExecutionOrder) order));
And you can route the order using the String key. Here is a "OpenOrder" example:
String result = (String) router.get("OpenOrder").apply(order);
There are many ways to do it. Which one to choose, depends on your needs and in this case in particular on how many different types of objects you will have.
I suggest looking at concepts like interfaces and inheritance and on specific design patterns.
One approach I tend to like, although still not perfect, works as follows:
interface Order {
}
interface OrderBuilder<T> {
T forType();
Object createOFSMessage(Order order);
}
class OpenOrderBuilder<OpenOrder> implements OrderBuilder {
#Override
OpenOrder forType() {
return OpenOrder.class;
}
...
}
class ExecutionOrderBuilder<ExecutionOrder> implements OrderBuilder {
#Override
ExecutionOrder forType() {
return ExecutionOrder.class;
}
...
}
class MyProcessor {
Map<Class, OrderBuilder> obs;
public void initialize() {
List<OrderBuilder> builders = new ArrayList<>();
builders.add(new OpenOrderBuilder());
builders.add(new ExecutionOrderBuilder());
obs = new HashMap<Class, OrderBuilder>();
for(OrderBuilder b : builders) {
obs.put(b.forType(), b);
}
}
public Object createOFSMessage(Order order) {
return obs.get(order.getClass()).createOFSMessage(order);
}
}
In the above example, adding a new implementation would just consist of adding an entry to the builders collection. While in the example above it's done manually, normally this is done through Dependency Injection and frameworks like spring (in which case, the initialize method may turn into a constructor with builders as an #Autowired argument).
There are of course other ways, some more simple some more complicated. The best way really depends on what you have to do and one key rule: the less code you have the better.
First one should not forget the switch-on-string:
switch (order.getClass().getSimpleName()) {
case "OpenOrder":
return OpenOrderBuilder.createOFSMessage((OpenOrder) order); //Returns String
case "ExecutionOrder":
return ExecutionOrderBuilder.createOFSMessage((ExecutionOrder) order); //Returns String
}
The code however shows inheritance being used in combination with static child class factories. Evidently a createOFSMessage is not desired in the Order base class.
Then use a non-static "builder" - a factory. Follow the strategy pattern.
If you already know the type when calling the method, this code can help you :
private String CreateOFSMessage(Class<T> classOrder) {
if ("OpenOrder".equals(classOrder.getSimpleName())) {
return OpenOrderBuilder.createOFSMessage((classOrder) order);
}else if ("ExecutionOrder".equals(classOrder.getSimpleName())) {
return ExecutionOrderBuilder.createOFSMessage((classOrder) order);
}
}

logic using functional-style exception handling with java and Vavr

I'm trying to get into basics of functional programming with Java 8 and I have a simple task which is to set a property on the object and then persist it. The database proper type is ltree so it might fail if it contains not allowed characters. I want to process items one-by-one and log exceptions/successes.
I choose to use the Vavr library because Try.of() exception handling and I want to learn to just use it as it seems very helpful.
here is what I came up with but I'm not satisfied enough:
public class PathHandler {
private final DocVersionDAO dao;
public void processWithHandling() {
Try.of(this::process)
.recover(x -> Match(x).of(
Case($(instanceOf(Exception.class)), this::logException)
));
}
private Stream<Try<DocVersion>> logException(Exception e) {
//log exception now but what to return? also I would like to have DocVersion here too..
return null;
}
public Stream<Try<DocVersion>> process() {
return dao.getAllForPathProcessing() //returns Stream<DocVersion>
.map(this::justSetIt)
.map(this::save);
}
public DocVersion justSetIt(DocVersion v) {
String path = Optional.ofNullable(v.getMetadata().getAdditionals().get(Vedantas.PATH))
.orElse(null);
log.info(String.format("document of uuid %s has matadata path %s; setting it", v.getDocument2().getUUID(), path));
v.getDocument2().setPath(path);
return v;
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
public Try<DocVersion> save(DocVersion v) {
return Try.of(() -> dao.save(v));
}
}
the goal is quite simple so could you teach me proper way to do it?
I'm afraid, this will become highly opinionated. Anyway, I try something.
... which happened before I realized, what Vavr actually provides. It attempts to cover everything mentioned here, like immutable data structures and monad syntax sugaring (with the For statement), and goes beyond that by coming up even with pattern matching. It takes a comprehensive set of FP concepts and rebuilds them using Java and it is no surprise Scala comes into one's mind seeing this ("Vavr is greatly inspired by Scala").
Now the foundations of functional programming can't be covered by a single SO post. And it might be problematic to get familiar with them in a language like Java which isn't geared towards it. So perhaps it is better to approach them in their natural habitat like the Scala language, which is still in some proximity to Java, or Haskell, which is not.
Coming back from this detour applying the features of Vavr may be more straight foward for the initiated. But likelely not for the Java developer sitting next to you in the office, who is less willing to go the extra mile and comes up with arguments that can't be just dismissed, like this one: "If we wanted to it that way, we would be a Scala shop". Therefore I'd say, applying Vavr asks for a pragmatic attitute.
To corroborate the Vavra-Scala argument, let's take Vavra's For construct (all Lists mentioned are io.vavr.collection.List), it looks like this:
Iterator<Tuple2<Integer, String>> tuples =
For(List.of(1, 2, 3), i ->
For(List.of(4, 5, 6))
.yield(a -> Tuple.of(i, String.valueOf(a))));
In Scala you'd encounter For and yield this way.
val tuples = for {
i <- 1 to 3
a <- 4 to 6
} yield (i, String.valueOf(a))
All the monad machinery remains under the hood, where Vavra brings more of an approximation, necessarily leaking some internals. For the purpose of learning it might be puzzling to start with Vavra's hybrid creatures.
So what remains of my post is a small time treatment of some FP basics, using the example of the OP, elaborating on immutability and Try on a trench-level, but omitting pattern matching. Here we go:
One of the defining characteristics of FP are functions free of side effects ("pure functions"), which naturally (so to speak) comes along with immutable data structures/objects, which may sound kind of weird. One obvious pay off is, that you don't have to worry, that your operations create unintended changes at some other place. But Java doesn't enforce that in any way, also its immutable collections are only so on a superficial level. From the FP signature characteristics Java only offers higher order functions with java-lambdas.
I used the functional style quite a bit on the job manipulating complicated structures where I stuck to those 2 principles. E.g. load a tree T of objects from a db, do some transformations on it, which meant producing another tree of objects T', sort of one big map operation, place the changes in front of the user to accept or reject them. If accepted, apply the changes to the related JPA entities and persist them. So after the functional transformation two mutations were applied.
I'd propose, to apply FP in this sense and tried to formulate an according version of your code, using an immutable DocVersion class. I chose to simplify the Metadata part for the sake of the example.
I also tried to highlight, how the "exception-free" Try approach (some of it poached from here) could be formulated and utilized some more. Its a small time version of Vavr's Try, hopefully focusing on the essentials. Note its proximity to Java's Optional and the map and flatMap methods in there, which render it an incarnation of the FP concept called monad. It became notorious in a sweep of highly confusing blog posts some years ago usually starting with "What is a monad?" (e.g. this one). They have cost me some weeks of my life, while it is rather easy to get a good intuition of the issue just by using Java streams or Optionals. Miran Lipovaca's "Learn Yourself a Haskell For Great Good" later made good for it to some extent, and Martin Odersky's Scala language.
Boasting with of, map and flatMap, Try would, roughly speaking, qualify for a syntax-sugaring like you find it in C# (linq-expressions) or Scala for-expressions. In Java there is no equivalent, but some attempts to at least compensate a bit are listed here, and Vavr looks like another one. Personally I use the jool library occasionally.
Passing around streams as function results seems not quite canonical to me, since streams are not supposed to get reused. That's also the reason to create a List as an intermediary result in process().
public class PathHandler {
class DocVersionDAO {
public void save(DocVersion v) {
}
public DocVersion validate(DocVersion v) {
return v;
}
public Stream<DocVersion> getAllForPathProcessing() {
return null;
}
}
class Metadata {
#Id
private final Long id;
private final String value;
Metadata() {
this.id = null;
this.value = null;
}
Metadata(Long id, String value) {
this.id = id;
this.value = value;
}
public Optional<String> getValue() {
return Optional.of(value);
}
public Metadata withValue(String value) {
return new Metadata(id, value);
}
}
public #interface Id {
}
class DocVersion {
#Id
private Long id;
private final Metadata metadatata;
public Metadata getMetadatata() {
return metadatata;
}
public DocVersion(Long id) {
this.id = id;
this.metadatata = new Metadata();
}
public DocVersion(Long id, Metadata metadatata) {
this.id = id;
this.metadatata = metadatata;
}
public DocVersion withMetadatata(Metadata metadatata) {
return new DocVersion(id, metadatata);
}
public DocVersion withMetadatata(String metadatata) {
return new DocVersion(id, this.metadatata.withValue(metadatata));
}
}
private DocVersionDAO dao;
public List<DocVersion> process() {
List<Tuple2<DocVersion, Try<DocVersion>>> maybePersisted = dao.getAllForPathProcessing()
.map(d -> augmentMetadata(d, LocalDateTime.now().toString()))
.map(d -> Tuple.of(d, Try.of(() -> dao.validate(d))
.flatMap(this::trySave)))
.peek(i -> i._2.onException(this::logExceptionWithBadPracticeOfUsingPeek))
.collect(Collectors.toList());
maybePersisted.stream()
.filter(i -> i._2.getException().isPresent())
.map(e -> String.format("Item %s caused exception %s", e._1.toString(), fmtException(e._2.getException().get())))
.forEach(this::log);
return maybePersisted.stream()
.filter(i -> !i._2.getException().isPresent())
.map(i -> i._2.get())
.collect(Collectors.toList());
}
private void logExceptionWithBadPracticeOfUsingPeek(Exception exception) {
logException(exception);
}
private String fmtException(Exception e) {
return null;
}
private void logException(Exception e) {
log(fmtException(e));
}
public DocVersion augmentMetadata(DocVersion v, String augment) {
v.getMetadatata().getValue()
.ifPresent(m -> log(String.format("Doc %d has matadata %s, augmenting it with %s", v.id, m, augment)));
return v.withMetadatata(v.metadatata.withValue(v.getMetadatata().value + augment));
}
public Try<DocVersion> trySave(DocVersion v) {
return new Try<>(() -> {
dao.save(v);
return v;
});
}
private void log(String what) {
}
}
Try looks like this
public class Try<T> {
private T result;
private Exception exception;
private Try(T result, Exception exception) {
this.result = result;
this.exception = exception;
}
public static <T> Try<T> of(Supplier<T> f)
{
return new Try<>(f);
}
T get() {
if (result == null) {
throw new IllegalStateException();
}
return result;
}
public void onException(Consumer<Exception> handler)
{
if (exception != null)
{
handler.accept(exception);
}
}
public <U> Try<U> map(Function<T, U> mapper) {
return exception != null ? new Try<>(null, exception) : new Try<>(() -> mapper.apply(result));
}
public <U> Try<U> flatMap(Function<T, Try<U>> mapper) {
return exception != null ? null : mapper.apply(result);
}
public void onError(Consumer<Exception> exceptionHandler) {
if (exception != null) {
exceptionHandler.accept(exception);
}
}
public Optional<Exception> getException() {
return Optional.of(exception);
}
public Try(Supplier<T> r) {
try {
result = r.get();
} catch (Exception e) {
exception = e;
}
}
}

Spring with Query Dsl custom binding and or operation doesn't work

i have following reQuirement Query:
"/article?category=kitchen&category=sports"
This Query is working without custom bindings. It will give me all Kitchen and sports articles as Response. It is doing an OR-Operation somehow.
But i need to customize the binding because i need to ingore the case. Now i am using this customize binding:
#Repository
public interface ArticleRepository extends JpaRepository<Article, Long>,
QueryDslPredicateExecutor<QArticle>, QuerydslBinderCustomizer<QArticle> {
#Override
default public void customize(QuerydslBindings bindings, QArticle article) {
bindings.bind(String.class).first((StringPath path, String value) -> path.containsIgnoreCase(value));
}
}
This only filters the first available value of a attribut. So in this case it only gives articles in category kitchen as Response. The second value of the category (sports) is ignored.
And now my Question: How can i get the articles of both catergories ignoring the case sensetive? What i need to change in my customize binding to achieve that?
Thank you very much
QuerydslBindings.TypeBinder#first uses a single value binding. You need to use QuerydslBindings.TypeBinder#all. This method operates on a
multivalue binding.
I provided several examples of how to customize your bindings in this answer. Something like this should work for you:
#Override
default public void customize(QuerydslBindings bindings, QArticle article) {
// using explicit path bindings
bindings.bind(article.category).all((path, values) -> {
BooleanBuilder predicate = new BooleanBuilder();
// with a for loop
for (String value : values) {
predicate.or(path.containsIgnoreCase(value));
}
});
// using a type binding
bindings.bind(String.class).all((StringPath path, Collection<? extends String> values) -> {
BooleanBuilder predicate = new BooleanBuilder();
// oneliner with Java 8 forEach
values.forEach( value -> predicate.or(path.containsIgnoreCase(value) );
});
}
The correct bindings for your article.category property should be like this:
bindings.bind(article.category).all((path, value) -> {
BooleanBuilder predicate = new BooleanBuilder();
value.forEach(o -> predicate.or(path.equalsIgnoreCase(o)));
return Optional.of(predicate);
});
If you don't need any manipulations of the parameters (like ignoring case) you can simplify it to that:
bindings.bind(article.category).all((path, value) -> Optional.of(path.in(value)));
After a good search on it I found the solution.
bindings.bind(String.class).all(new MultiValueBinding<StringPath, String> () {
#Override
public Predicate bind(StringPath path, Collection<? extends String> values) {
BooleanBuilder predicate = new BooleanBuilder();
values.forEach( value -> predicate.or(path.containsIgnoreCase(value)));
return predicate;
}
});

Consolidate/flatten nested lists in Android with RXJava2

I'm struggling to come up with an RXJava2 Solution to "a simple problem". I am not extremely experienced with RXJava beyond the simple use cases.
Suppose I have a Container that looks like:
class Container {
List<A> listOfA;
}
The rest of the model is a series of nested lists like this model:
class Base {
// irrelevant content
}
class A extends Base {
List<B> listOfB;
}
class B extends Base {
// irrelevant content
}
Somewhere in my code, I obtain a Single<Container> like so:
(note: the code/types/etc have been obfuscated/simplified for an easier reading)
disposables = new CompositeDisposable(); // not important here
disposables.add(
interactor.getTheContainer() // This returns a Single<Container>
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableSingleObserver<Container>() {
// on error ommited for clarity
#Override
public void onSuccess(final Container value) {
process(value);
}
})
);
private void process(final Container container) {
List<Base> items = new ArrayList<>();
List<A> listOfA = container.getListOfA();
for (A a : listOfA) {
items.add(a);
items.addAll(a.getListOfB());
}
// do something with "items" - ommited for clarity
}
I have been unsuccessfully trying to convert the method process(Container) to RXJava (maybe I shouldn't but now I want to know).
I can't even begin to list all the stuff I've experimented with, but I'm really new to RXJava 2 (most usages I've done in the past years with RX were simple Observables from Retrofit and nothing too fancy, or even as an Event Bus to replace Otto/Guava), so I am really not well versed in the arts of making good usage of the RX toolset. I think some sort of map should work, but the whole Java syntax gets confusing really fast for me when it comes to anonymous methods.
The question is:
Where should I read/look for ideas how to perform the same operation of the process method but with RXJava2?
Order is important, the final list looks like this with the current method and I need it this way:
0. A1
1. B1.1
2. B1.2
3. B1.nn…
4. A2
5. B2.1
6. B2.2
7. B2.nn…
8. A3
9. B3.1
…
You get the idea.
Any hints? I do not have Retrolambda or Java 8 (nor can use it, it's not my decision and I can't do anything about it).
You were almost there:
List<Base> process(List<A> list) {
List<Base> result = new ArrayList<>();
for (A a : list) {
result.add(a);
result.addAll(a.getListOfB());
}
return result;
}
interactor.getTheContainer() // This returns a Single<Container>
.subscribeOn(Schedulers.io())
.map(new Function<Container, List<Base>>() {
#Override public List<Base> apply(Container c) {
return process(c.getListOfA());
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableSingleObserver<List<Base>>() {
#Override public void onSuccess(final List<Base> value) {
/* display the list */
}
})
A more "convoluted" solution could replace the map above with some Iterable transformation via IxJava:
.flatMapIterable(new Function<Container, Iterable<A>>() {
#Override public Iterable<A> apply(Container c) {
return c.getListOfA();
}
})
.flatMapIterable(new Function<Iterable<A>, Iterable<Base>>() {
#Override public Iterable<Base> apply(Iterable<A> a) {
return Ix.<Base>just(a).concatWith(a.getListOfB());
}
})
.toList()

How can I write code that uses a different method for different parameters without using "if-else if"?

To me, if-else if is too long and hard to read.
Is there some table-driven approach I can use to map an input value to call a specific method for that value?
For example:
for(String id:ids){
Test test = new Test();
if(id == "101") {
test.method1();
}else if(id=="102") {
test.method2();
}else if(id=="103"){
test.method3();
...
...
static Map<String,Method> methods = new HashMap<String,Method>();
...
static {
methods.put("101",Test.class.getMethod("method1",null));
methods.put("102",Test.class.getMethod("method2",null));
...
}
...
for( String id : ids ) {
Test test = new Test();
Method m = methods.get(id);
if (m != null) {
m.invoke(test,null);
}
}
Of course, all this really does is trade the pain of the if/else chain for the pain of initializing the hash with all the methods.
You can use reflection to call method. Construct the method name from the string and Use Method Class from java.lang.reflect namespace to invoke the method and pass parameters.
Something like this
Method theMethod = yourClass.getClass().getMethod("methodName", null);
method.invoke(yourClass, );
As it is, your code is not quite correct, as it's using == to check equality of strings.
You should use the equals method:
for(String id: ids){
Test test = new Test();
if(id.equals("101")) {
test.method1();
}else if(id.equals("102")) {
test.method2();
}else if(id.equals("102")){
test.method3();
// etc.
}
}
Other answers have suggested a creating a method map Map<String,Method> and using reflection. This will work and may be perfectly reasonable for your use, but loses some compile-time type safety checks.
You can get those checks back by defining an interface and populating a map with instances of that interface:
interface TestMethod {
void execute(Test test);
}
private static HashMap<String, TestMethod> methodMap = new HashMap<String, TestMethod>();
static {
methodMap.put("101", new TestMethod(){
#Override
public void execute(Test test) {
test.method1();
}
});
methodMap.put("102", new TestMethod(){
#Override
public void execute(Test test) {
test.method2();
}
});
methodMap.put("103", new TestMethod() {
#Override
public void execute(Test test) {
test.method3();
}
});
// etc.
}
and then your loop can be coded as
for (String id: ids) {
Test test = new Test();
methodMap.get(id).execute(test);
}
As it is here, this unfortunately makes the code longer and leaves it at least as difficult to read. But if you're using Java 8, you can use lambda expressions to populate the map, which would make the static initializer block look more like:
static {
methodMap.put("101", t -> t.method1();
methodMap.put("102", t -> t.method2();
methodMap.put("103", t -> t.method3();
// etc.
}
and then it might actually be worth doing.
However, if this sort of conditional based on some kind of string "code" is common in your application, and especially if you have multiple conditionals depending on the same codes, you might want a much broader redesign to encapsulate the different actions done for the different codes in a class hierarchy and use polymorphism instead of either if-else (or switch) or a method map lookup. You're still likely to need some conditionals to build the instances of your classes, but the code might be greatly improved.

Categories