Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

best practice when order of calls in class are important?

I have one class with two important functions:

public class Foo {
    //plenty of properties here
    void DoSomeThing(){/*code to calculate results*/}
    void SaveSomething(){/* code to save the results in DB*/}

}

SaveSomething() uses the results calculated in DoSomeThing().

the problem is that we must not to call SaveSomething() before DoSomeThing() or if that happens the results are not true results. I mean order of calls are important, this is a problem in maintaining the code.(when new one is added to team).

is there any way to manage this?

I think of 3 methods as below

  1. throwing exception in SaveSomething() if it called before DoSomeThing()
  2. having a bool that are set in DoSomeThing() and SaveSomething() code changes to:

    bool resultsAreCalculated = false;
    void SaveSomething(){
        if (!resultsAreCalculated) {
            DoSomeThing();
            // the resultsAreCalculated = true; is set in DoSomeThing();
            // can we throw some exception?
        }
        /* code to save the results in DB*/
    }
    
  3. implementing it Fluent like :

    Foo x = new Foo();
    x.DoSomeThing().SaveSomething();
    

    in this case, it is important to guarantee that this is not happens:

    x.SaveSomething().DoSomeThing();
    

right now, i use the second method. is there any better way or is that enough?

like image 551
ahmad molaie Avatar asked Oct 20 '11 15:10

ahmad molaie


2 Answers

Ideally methods that need to follow a certain order in execution denote, or imply the need to implement, a workflow of some sort.

There are a couple of design patterns that support enforcing workflow-like linear execution order, such as the Template Method Pattern, or Strategy.

To Take the Template Method approach, your Foo class will have an abstract base that defines the order of executing Do() and Save(), something like:

public abstract class FooBase
{
    protected abstract void DoSomeThing(); 
    protected abstract void SaveSomething();
    public void DoAndSave()
    {
        //Enforce Execution order
        DoSomeThing();
        SaveSomething();
    }

}

public class Foo : FooBase
{
    protected override void DoSomeThing()
    {
        /*code to calculate results*/
    }

    protected override void SaveSomething()
    {
        /* code to save the results in DB*/
    }
}

This way You class consumers will only have access to DoAndSave() and they will not infract the order of execution you intended.

There are another patterns that deals with workflow / state transition type of situations. You can refer to Chain of Command, and State Patterns.

In response to your comment: This follows the same Template idea, you add another step in your template, imagine you want to validate the results before saving, you can extend your template to become:

public abstract class FooBase
{
    protected abstract void DoSomeThing();
    protected abstract void SaveSomething();
    protected abstract bool AreValidResults();
    public void DoAndSave()
    {
        //Enforce Execution order
        DoSomeThing();

        if (AreValidResults())
            SaveSomething();
    }

}

And of course for a more elaborate workflow I referred you to the state pattern at the end of my original answer, you can have a more granular control over the transition condition from one state to another.

like image 70
Anas Karkoukli Avatar answered Sep 28 '22 16:09

Anas Karkoukli


One option to help avoid user error is to make it clear by passing a variable. By doing this, it raises a flag for the user that they need to get the results (i.e. DoSomething()) before calling SaveSomething(...).

results = DoSomething(); // returns the results to be saved
SaveSomething(results);
like image 41
bitsoflogic Avatar answered Sep 28 '22 17:09

bitsoflogic