DEV Community

mohamed Tayel
mohamed Tayel

Posted on

Flexible C# with OOP Principles: Reducing Complexity in OOP with State-Based Logic

Meta Descripation:Learn how to simplify and maintain object-oriented code by using state-based logic. Discover techniques to manage complexity as requirements grow, using an account management example to illustrate the benefits of state-driven design.

When we start with a new class, it's often small, clear, and focused on a single purpose. However, as requirements evolve, the class can quickly become cluttered, filled with conditions that complicate maintenance. This is where state-based design can help keep our code manageable and object-oriented.

In this article, we’ll explore an example where evolving requirements for an account system lead to complex branching logic and how we can control this complexity.

Example Scenario: Bank Account Requirements

Imagine a client asks us to create an account management system with basic actions:

  1. Deposits: Money can be deposited anytime.
  2. Withdrawals: Money can be withdrawn if the account holder verifies their identity.
  3. Account Closure: The account holder can close the account, after which no transactions are allowed.

Initially, implementing these rules with if statements seems straightforward. For example, we might add conditions to allow withdrawals only if the holder is verified, and to block deposits or withdrawals if the account is closed. But as we add more rules, our class grows more complex.

The Problem with Branching Logic

Each new requirement introduces additional checks that complicate the code. For instance:

  • Withdrawals need to check if the holder is verified.
  • Deposits and Withdrawals must confirm that the account isn't closed.

These conditions make the code harder to read and maintain. Explicit condition checks can lead to scattered logic across methods, reducing readability and increasing the chance of bugs.

Introducing State-Based Logic

A more object-oriented approach is to replace branching conditions with states. We can define distinct account states (e.g., Open, Closed, Verified) and implement each as a separate class that encapsulates the rules specific to that state.

Benefits of State-Based Logic

By creating explicit states, our code becomes more modular and readable, reducing the need for scattered if statements. Each state class handles the actions that are allowed in that state, making the rules easy to follow and maintain.

For example, in the Open state, deposits and withdrawals are allowed (with verification for withdrawals). When the account moves to the Closed state, all actions like deposits and withdrawals can be disabled directly within that state class.

Implementing the Solution

Here’s a brief overview of how this could look:

  1. Define Account States: Create classes for each state (e.g., OpenState, ClosedState).
  2. Encapsulate State-Specific Logic: Move rules for each action (deposit, withdraw, etc.) into the corresponding state class.
  3. Delegate to State Classes: The main Account class now delegates actions to its current state, simplifying the core logic.

Final Thoughts

By transitioning from condition-based checks to state-based design, we make our code easier to understand and extend. Each state manages its own rules, reducing the complexity in our main class and enabling a cleaner, more object-oriented structure.

Top comments (0)