0

I have an object that based on it's properties, will perform different operations in it's service layer.

Let's say it's a customer with the following states: Registered, RegisteredWithoutContactOnFile, and NoRegistration.

These states are based on properties set on the object. So it would be something like the following:

public class Customer
{
        public Registration Registration {get; set;}
        public string Email {get; set;}
        public int Id {get; set;}
}

Then there would be the CustomerService which does different database operations based on what is entered like such:

public class CustomerService
{
        public void RegisterCustomer(Customer customer)
        {
               if (customer.Registration != null && customer.Id == 0) //Registered without contact -- insert a new contact into the database

               else if (customer.Registration == null && customer.Id == 0) // No registration/contact -- insert both a registration and contact into the database
               
               else if (customer.Registration != null && customer.Id > 0) //Registered and contact on file -- update contact and registration information in the database
}

I basically want to get rid of this if/else statement in the service layer and make it a state in my object so that I can more easily test it. Can anyone assist with how I would go about this or if I'm going about this the wrong way? I will say I am interested in the state pattern as I have been working on Finite State Machines for an AI agent but I feel like it makes sense here as well. Perhaps a strategy pattern would work? In short, I'm just trying to get the logic out of the service layer to make it more testable... any insight would be greatly appreciated!

9
  • If you have a state like that you will have to either limit your set of setters, or customize them. You have to ensure that state invariant for your class is kept intact whatever method you call on it. Also, don't have a setter for your state. Commented Oct 2, 2020 at 16:15
  • Writing a state machine is trivial using pattern matching - it's just a single expression accepting the current state and event, and returning the new state. In this case what is the state machine? What does Registered without contact mean? Commented Oct 2, 2020 at 16:18
  • @PanagiotisKanavos in short, it would mean that they signed up/consented to get emails but never actually put an email on file... it's a little more complicated than that but that's the gist of it. Commented Oct 2, 2020 at 16:19
  • @g_bor I was actually thinking of adding a setter for the state so thank you for clarifying. Does it make sense to have a method such as GetState() that returns a state based on the if/else conditions? Commented Oct 2, 2020 at 16:20
  • @Dan I mean specifically not just the gist. Is it an action to call? A value to return? It's impossible to write an answer with an example without knowing how the transition should be handled, what should happen Commented Oct 2, 2020 at 16:21

2 Answers 2

2

You can't have a state machine without transitions and new states. This looks more like validation checks. It can be implemented used pattern matching and a switch statement, eg :

switch (customer.Registrarion,customer.Id)
{
    case (null,0):
        //No Registration, contact:
        break;
    case (Registration r,0):
        //Registered without contact 
        break;
    case (Registration r, _):
        //Registered with contact 
        break;
    defaut:
        throw new InvalidOperationException("Invalid state!");
}

Pattern matching with switch expressions can be used to implement state machines, like this example from the docs :

var newState = (state, operation, key.IsValid) switch
{
  (State.Opened, Operation.Close, _)      => State.Closed,
  (State.Opened, Operation.Open, _)       => throw new Exception(
    "Can't open an opened door"),
  (State.Opened, Operation.Lock, true)    => State.Locked,
  (State.Locked, Operation.Open, true)    => State.Opened,
  (State.Closed, Operation.Open, false)   => State.Locked,
  (State.Closed, Operation.Lock, true)    => State.Locked,
  (State.Closed, Operation.Close, _)      => throw new Exception(
    "Can't close a closed door"),
  _ => state
};

This expression uses the original state, an operation and a parameter to decide the new state

Sign up to request clarification or add additional context in comments.

Comments

0

ugh...

refactor multi-if or nested-if statement can have many different ways, something like you could use a template pattern, i.e. translate your customer to different type by states, and then have a dictionary of [, ], the core business logic becomes

handlers[customer.GetType()].Handle(customer) 

another way is instead of using if-statement, you could have concrete type (of each state) and then pattern matching.

one more interesting way is employing an actor to handle this. An actor comes with a nature built-in FSM.

What you can do is issuing a message, your handling actor calls Becomes, which enters in a state that will only handle the messages under that state.

2 Comments

Or none of those. A state machine can be created easily inside a function. Simple if/else is good enough but verbose, the actions can be passed as Action or Func parameters to a function and pattern matching can convert the transitions to a single expression.
sure, that's why I ugh at the beginning, coz there is no right or wrong in such a question, just how you want to write and read your code, and you think it's easiest to test your individual logic, completely personal preference.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.