DEV Community

Cover image for Don’t use if-else blocks anymore! Use Strategy and Factory Pattern Together

Don’t use if-else blocks anymore! Use Strategy and Factory Pattern Together

Tamer Ardal on October 18, 2024

As we move forward in a project, lost in if-else blocks, struggling with complex conditions and repetitive code, we look for a solution. But why sh...
Collapse
 
parables profile image
Parables

Great article but the title is a bit of an over-statement....
It sounds like if-else statements are a terrible thing...

This is not a silver bullet and for newbies reading this, don't go refactoring all your if-else blocks with this...

Understanding and mastering Design Patterns is a skill every developer needs to learn but most importantly, knowing when to use the right tool for the right job.

Sometimes, a simple if-else statement is the best solution... Know when to refactor

Collapse
 
nandorholozsnyak profile image
Nándor Holozsnyák

As this is Java, we could be using Java 21's pattern matching with sealed classes.
Current setup does not yield an error if a new type is given at build time, but with sealed classes it would.

I do like this Strategy pattern, but most times with an isActive or whatever how we call method that decides if we have to launch an implementation or not, that is great of multiple impls must be executed in chain.

Collapse
 
lexlohr profile image
Alex Lohr

I know this pattern under the name of branchless programming - the factory pattern is usually an OOP pattern where you define objects through methods, ie. new MyProduct().color(Colors.Blue).size(Size.XL).build().

Collapse
 
schmoris profile image
Boris

That sounds like a version of the builder pattern to me, might be mistaken tho.

Collapse
 
mahmoudalaskalany profile image
Mahmoud Alaskalany

That is a builder pattern

Collapse
 
cicirello profile image
Vincent A. Cicirello • Edited

Your PaymentFactory class isn't a factory. In the factory pattern, a factory method instantiates new object instances. In your example, the getPaymentStrategy method returns one of three pre-existing objects depending on payment type. Call that method again with same payment type and you get same payment strategy instance. This isn't a factory.

Collapse
 
blafasel3 profile image
Maximilian Lenhardt

I like this pattern, much more concise.

I would like to suggest a little improvement: I would use a switch Statement on the payment type instead of the map inside the PaymentFactory. Then you just get a compile error because a case is missing here if you happen to add another payment type. The map will fail at runtime :)

Collapse
 
panditapan profile image
Pandita

Reads the comments

Please don't take them to heart! I've done this for when code becomes really unmanageable and it's great stuff! Sometimes what we're missing is implementing patterns in practice to understand their usefulness.

Thanks for the article!

Collapse
 
tamerardal profile image
Tamer Ardal

Thank you so much for your comment

Collapse
 
efpage profile image
Eckehard

Your titel is too much click bait for me. There is nothing wrong with IfThenElse. Any programming construct will probably be confusing if used in the wrong situation. But this is more of a problem with the programmer than with the method.

Look at this example.

if (x>0) 
   console.log("positive")
else 
   console.log("negative")
Enter fullscreen mode Exit fullscreen mode

Please show me how this could be made simpler with the factory pattern?

You can simplify this with ternary operator like this:

(x>0) ? console.log("positive") : console.log("negative")
Enter fullscreen mode Exit fullscreen mode

But this will be harder to read, as the ternary operator is meant to be used in a different way like this and in this case it will be better readable:

console.log((x>0) ? "positive" : "negative")
Enter fullscreen mode Exit fullscreen mode

There have been multiple posts about escaping the if/else hell like here, but it depends much on your task which one fit´s best.

If you need a "default"-case, you can use this:

function checkStatus(status){
   const statusList = {
      available: 'The user is currently available',
      busy: 'The user is currently busy',
      away: 'The user is currently away from keyboard',
      breaktime: 'The user is currently having a good lunc'
   }

   return statusList[status] ?? 'Option not found' 
}
Enter fullscreen mode Exit fullscreen mode

Another pattern I like much is this a special form to use the ternary operator:

let txt = 
paymentType.equals("CREDIT_CARD")  ? "Processing credit card payment..." :
paymentType.equals("DEBIT_CARD")) ? "Processing debit card payment...") :
paymentType.equals("CRYPTO")) ? "Processing crypto payment...") : false;

if (txt) System.out.println(txt) 
else throw new IllegalArgumentException("Invalid payment type");
Enter fullscreen mode Exit fullscreen mode
Collapse
 
tamerardal profile image
Tamer Ardal • Edited

Thank you very much for your comment. I think you missed the point a little bit. I did this to keep it a bit simple, but either your or my examples do not represent the real-world situation.

For example, no one logs if the payment type is a credit card. In the real world, we do some service calling, then insert or update data in the database, and finally respond to an API call.

In this scenario, every if-else block has too many code lines. This is very messy and contrary to SOLID principles. And I've seen people doing extremely complex jobs using too much if-else, which makes it very difficult for newcomers to understand the code.

Of course, if you are a beginner or actions are simple, you can use if-else blocks. I also use if-else blocks to do simple checks. Not using them for simple operations would be overengineering.

And I admit the title is a bit click-bait...

In summary, this article was not for beginners, but for those who are already working on a large project and using if-else or switch-case in complex operations.

By doing so, if another method is added in the future, we comply with the Open-Closed Principle by creating new classes without touching the existing code, rather than adding a new else if in the service.

I hope I have explained who this article is more suitable for.

Collapse
 
efpage profile image
Info Comment hidden by post author - thread only accessible via permalink
Eckehard

Dont use cars anymore, I invented a rocket...

I know things can get very messy and it is good to know solutions for that. But this is a very special solution to a very special problem and I suppose, there are many ways to solve this in a structured manner. If you tried to use If/else for that, it shows just your limited experience, it has nothing to do with limitations of the if/else construction.

Maybe you sould be a bit more careful with your titles...

Collapse
 
rgunczer profile image
Robert Gunczer

I would never replace something so simple as if-else with more abstraction and patterns.
The only thing I would do is to use "switch expressions" and that's it.

this is "merchant of complexity" category

Collapse
 
jwp profile image
John Peters

I recall reading something similar to this on CodeProject 15 years ago. Was interested then but never implemented it. Today I find it rare to have huge if then or switch statements. Thanks for reminding me....

Collapse
 
kurealnum profile image
Oscar

Just a heads up that you can add highlighting to the code blocks if you'd like. Just change:

code block with no colors example

... to specify the language:

code block with colors example

More details in our editor guide!

Collapse
 
tamerardal profile image
Tamer Ardal

Wow thanks, I didn't know that

Collapse
 
gregorygaines profile image
Gregory Gaines

I wouldn't say never use if/else. What if inside credit card payments you have chip, tap, swipe, Google Pay, Apple Pay.

Now we have ChipCardPayment, TapCardPayment, SwipeCardPayment, GooglePayCardPayment, ApplePayCardPayment.

If you don't use if/else your're stuck creating a class for every condition. What about non oop languages, they won't be able to fully follow this principle.

Collapse
 
nullablestring profile image
George Abraham

This is a good solution when the if else chain is known to be open ended. You should consider populating the factory from a configuration element so that no code is needed to add elements. This also opens the door to disable forms of payment and track there usage via configuration as well. Thanks for sharing!

Collapse
 
engali_badouin_609c18c444 profile image
EngAli Badouin • Edited

Hello Tamer, thank you for the appreciated tutorial.

I'd need to say more people judge about article without knowledge. some people see if else best and some see more classes wil be created.

You guys should read more about SOLID principles because most of all design patterns are based on them.

Also solid and design pattern benefits won't appear with small business, always appear with big projectsand big business when you need to apply clean Architecture for your project.

We always face big problems with switch cases and else if in projects and in finally we replace it with strategy DP.

Read more and learn more, I believe DP is the best practice to make your code clean, readable, and maintainable.

Thank you all.

Collapse
 
dotnetfullstackdev profile image
DotNet Full Stack Dev

@tamerardal Nice article! Its very pin to the point and great explanation too.
I tried similar article, with little advanced by adding selection of

  • card(bankcard1, bankcard2..etc),

  • UPI (upi1,upi2... etc),

  • online banking (online banking1, onlinebanking2.. etc)

Have a look and add your thoughts and suggestions
Mastering Multiple Payment Gateways with Strategy and Abstract factory Combo

Collapse
 
artsem_miklashevich profile image
Artsem Miklashevich

btw, you are still using if in the PaymentService, also strategy.get probably uses 'if'

Collapse
 
ndrone profile image
Nicholas Drone

PaymentFactory is not following the factory pattern. Factory pattern creates new objects. What you have is just a collection of PaymentStrategy implementations. Using the strategy pattern is great, but really falls short on the factory. Solving the for the if...else I would of just done a switch statement calling the correct PaymentStrategy Implementation. Deleting the PaymentFactory class.

Collapse
 
hlapidez profile image
Hank Lapidez

I like the enum approach. Ofen I let the enum implement the interface directly.

Collapse
 
citronbrick profile image
CitronBrick

You can use the below syntax to enable syntax highlighting in Dev.to

triplebacktick java
code
triplebacktick

Collapse
 
hreuven profile image
Reuven Hozias

Great article, thanks!
I think you need to find a spesific use case for such a pattern, don't start refactoring all of your if/else's..

Collapse
 
nsadisha profile image
Sadisha Nimsara

Great article. Clearly explained how we can make use of these concepts. Appreciate it.

Collapse
 
tamerardal profile image
Tamer Ardal

Thanks 🙂

Collapse
 
thethirdrace profile image
TheThirdRace • Edited

Sometimes I'm asking myself if we're trying too much:

  • The original example has 13 lines of code, all collocated in 1 file
  • The new "improved" way has 48 lines of code and you need 4 different files, most likely in different folders

As with any pattern, there's a time for using it and a time not to. I feel the trade-off is not worth it in this particular example. I'm not saying the pattern is bad, I'm simply pointing out the example here is very bad to drive your point.

Collapse
 
tamerardal profile image
Tamer Ardal • Edited

Yes, because the original example is example. Doesn't have any business logic. If you add all payment logic, it will be so complicated. Think like that. And it's not all about shortening the line of code. Long but maintainable is more important.

If you use a lot of if-else in your project and you need it like this, use it. If you are already doing simple operations, you do not need to use it. There is no point in counting the lines of code in this example because this is an example I simplified to explain.

Collapse
 
vilim2000 profile image
Jesse Sargent

Great article!

Collapse
 
jsprenk profile image
Jay
  • Extensibility: A false goal. In practice code is written to accomplish a business function and can't be extended in such a simplistic manner. The requirements change sufficiently it's a redesign. If you extend a program a pub/sub model is far superior. You don't have to change code handling existing functionality at all.

  • Readability: I can right click on a function call in an if statement and choose 'show definition'. Tracking down an implementation through a factory patter is much harder.

  • Maintainability: See previous points.

Collapse
 
skyjur profile image
Ski • Edited

As you can see in this article, using design patterns instead of if-else blocks to manage payment transactions makes the project more developable and improves the readability of the code. Try these patterns in your next project instead of using if-else blocks.

To be fair what I see is that you have replaced 8 simple lines of code with lots of code that doesn't do a thing. This is the opposite of "improves the readability" and the opposite of "more developable". Strategy pattern is overkill in most cases except for rare ones and you didn't provide good examples where strategy pattern would be a good fit except for one example where it's overkill and makes everything more complicated without a reason.

Collapse
 
saugata_guha profile image
Saugata Guha • Edited

Be careful, it might sound good and your ego might get a boost, when no one is understanding you. The code will get thrown out, if the next person do not understand it. You might say, that the guy is a bad programmer, it does not matter, all your work goes down the drain, it will be re-written. Very basic programming fact, keep it simple, otherwise, you yourself will not understand it after a while.

You can use a switch statement instead of a long if then else statement.
I will be very careful about the person who says "Don't use if-else blocks anymore"
Good luck.
These are good for publishing papers.

Collapse
 
jeromevillamor profile image
Jerome Ryan Villamor

Based on your example, I'll go with if-else. Simpler and cleaner. You can refactor it once it becomes bigger.

Collapse
 
timmytune profile image
Adedoyin Timothy yinka

Really? You created a bunch of subclasses just for this? So can you now explain what "strategies.get(paymentType);" is doing that is not an if else?

Collapse
 
hbthepencil profile image
HB_the_Pencil • Edited

This works, but it seems to repeat the same code to get payment every time. Isn't there a better way to do this?

note: I am learning Python, so there are definitely some differences...

Collapse
 
aiosoftware_dev profile image
Andrei AIO Software

Is this only for Java ?

Collapse
 
tamerardal profile image
Tamer Ardal

Of course is not. You can use it in every OOP Language.

Collapse
 
netch80 profile image
Valentin Nechayev

Any Turing complete language may implement the same.
But exact details, will it be that directly apparent or not - will differ.

Collapse
 
designly profile image
Jay @ Designly

Have you done any profiling on speed one method vs the other? Do you think this could be an issue when it comes to software that depends on high-speed transactions?

Collapse
 
johnlkramer profile image
John Kramer

I wrote almost this code a few years ago.

Some comments have been hidden by the post's author - find out more