Engineering • 4 min read
By Hamidreza Sahraei
18.10.2024
In Android development, it's common to encounter scenarios where different flows share similar structures but require slight variations in specific business logic. The Strategy Pattern is a nice and useful design pattern in such cases, allowing different algorithms or behaviours to be encapsulated and swapped out based on the flow.
In this article, I’ll share how we applied the Strategy Pattern while refactoring the market order flows in the Bitpanda application. We needed to support two market order flows—buy and sell—with shared UI components and a single ViewModel while maintaining distinct logic for each flow.
Our market order flow was split into two types:
Buy Market Order
Sell Market Order
Both flows used the same amount of screen UI, but the underlying business logic differed. For example, the calculation for estimating the amount the user would receive varied. In a buy order, the calculation divides the amount by the price, whereas in a sell order, the calculation multiplies the amount by the price (just for a sample use case).
Additionally, the price is updated dynamically. We needed a clean solution that allowed us to handle both flows in a unified way while keeping the logic flexible and maintainable.
In this article, we will demonstrate a simplified use case with just an example. This does not reflect the exact implementation in our codebase, but the core idea of utilising the Strategy Pattern to handle varying logic is crucial, especially in a logic with many functions.
The Strategy Pattern is ideal for this problem. It allows us to define separate strategies for buy and sell orders and swap them out depending on the flow. This makes the ViewModel agnostic to specific calculations while keeping the code clean and modular.
Key Benefits:
No repeated 'if/else' or 'when' statements: With this approach, we avoid repeating 'if/else' or 'when' conditions in every method that has different business logic for buy and sell orders.
Improved testability: The buy and sell strategies can be tested independently in a clean and straightforward way, as each strategy is isolated and doesn’t depend on complex conditional logic.
Flexible dependencies: Each strategy can have different dependencies (e.g., services, repositories) based on its logic.
To keep things, simple we've provided fewer examples, but in a production-ready scenario, the strategies might handle more complex interactions while the core idea remains the same.
Here’s how we implemented the Strategy Pattern for this use case.
We start by defining a common interface that both strategies will implement:
Next, we create concrete classes for each strategy: 'BuyMarketOrderStrategy' and 'SellMarketOrderStrategy':
Note: In more complex scenarios, each strategy could inject different dependencies (e.g., a pricing service, external API, or repository) to perform their specific logic.
In the ViewModel, we use a 'StateFlow' to manage the UI state, And the price is updated dynamically. Here’s how the state and strategy integration look:
The 'MarketOrderViewModel' might include additional methods that handle various business logic specific to buy and sell orders. One prominent example is the logic for estimating the received value based on the order type, but other methods might include validation, price format adjustments, and more.
You can decide which strategy to use based on whether it’s a buy or sell order:
In your composable, you observe the 'StateFlow' and update the UI accordingly:
The Strategy Pattern makes it easy to independently test the buy and sell strategies. Here’s an example of how you can write unit tests for each strategy:
Key Points:
Each strategy is isolated, making it easy to test specific business logic.
The tests focus solely on the calculation logic, free from conditional branches like 'if/else' or 'when' statements, leading to more readable and maintainable test code.
The Strategy Pattern allowed us to refactor the market order flow, enabling a single composable and ViewModel to handle both buy and sell orders. The strategy pattern provides flexibility, allowing us to swap logic as needed while maintaining clean and modular code.
This approach eliminates the need for repeating 'if/else' or 'when' statements throughout different methods, making the code more concise and maintainable. Additionally, the strategies can be easily tested in isolation, ensuring robust and accurate business logic.
Although this example focuses on simple cases, in a production-ready environment, each strategy could be more complex with additional dependencies. Nevertheless, the core idea remains the same—encapsulating varying behaviour into strategies and swapping them as needed.
By managing these variations in behaviour using strategies, we achieve a scalable solution that keeps the code maintainable, testable, and adaptable to future changes.
Bitpanda GmbH ve grup şirketleri (Bitpanda) Türk Parasının Kıymetini’nin Korunması Hakkında 32 sayılı Karar’ın 2/b maddesine göre Türkiye’de yerleşik sayılan hiçbir kişiye yönelik olarak 6362 sayılı Sermaye Piyasası Kanunu başta olmak üzere Türkiye Cumhuriyeti Devleti mevzuatı hükümleri gereği Türkiye’de faaliyet izni gerektiren hiçbir sermaye piyasası faaliyetine dair hizmet sunmamaktadır. Şayet Bitpanda’nın yabancı sermaye piyasalarında vermiş olduğu hizmetlerden Türkiye’de yerleşik kişilerin faydalandığı tespit edilecek olursa tüm zararları kullanıcıya ait olmak üzere bu hizmetler ivedilikle sona erdirilecektir.
We use cookies to optimise our services. Learn more
The information we collect is used by us as part of our EU-wide activities. Cookie settings
As the name would suggest, some cookies on our website are essential. They are necessary to remember your settings when using Bitpanda, (such as privacy or language settings), to protect the platform from attacks, or simply to stay logged in after you originally log in. You have the option to refuse, block or delete them, but this will significantly affect your experience using the website and not all our services will be available to you.
We use such cookies and similar technologies to collect information as users browse our website to help us better understand how it is used and then improve our services accordingly. It also helps us measure the overall performance of our website. We receive the date that this generates on an aggregated and anonymous basis. Blocking these cookies and tools does not affect the way our services work, but it does make it much harder for us to improve your experience.
These cookies are used to provide you with adverts relevant to Bitpanda. The tools for this are usually provided by third parties. With the help of these cookies and such third parties, we can ensure for example, that you don’t see the same ad more than once and that the advertisements are tailored to your interests. We can also use these technologies to measure the success of our marketing campaigns. Blocking these cookies and similar technologies does not generally affect the way our services work. Please note, however, that while you’ll still see advertisements about Bitpanda on websites, the adverts will no longer be personalised for you.