How to deal with temporal coupling?
Asked Answered
D

5

7

I'm struggling because of this:

My classes have some methods that have temporal coupling. This is, some method MethodA has to be invoked first to "initialize" the data that MethodB needs to work properly.

I usually make the temporal coupling explicit by passing the offending dependency to "MethodB" as argument, like in this snippet:

private class SomeClass
{
    private string field;
    private int count;

    public SomeClass()
    {
        MethodA();
        MethodB(field);
    }

    private void MethodA()
    {
        field = "Something";
    }

    private void MethodB(string str)
    {
        count = str.Length;
    }
}

Although it makes things explicit I feel I'm doing something wrong. I end up having method that don't use fields at all (static methods!), so the class starts to seem less cohesive.

Is this the best way to do it? (losing cohesion by passing arguments)

EDIT: Regarding some answers that suggest using field as a parameter in the constructor or using the Builder Pattern to avoid invalid states: I cannot do that, because in my case I'm building a Parser. MethodA reads the input and sets the state depending on it (reading characters from a file) and then, MethodB is invoked. They have to be invoked in the correct order. That is the real problem: one should be invoked before the other.

Donnelldonnelly answered 7/4, 2015 at 19:18 Comment(2)
Are you worried about cohesion in your private methods, or is this a contrived example?Secretarygeneral
@neontapir: higher cohesion doesn't sound like something he should be worried about.Mayamayakovski
C
5

If you follow Anemic Domain Model, you can break your class and make it 2 smaller classes. You become aware of bad design because your current class violates SRP, in short it has 2 responsibility: 1 for handle the input process, 1 for process the input result.

Break it down so that ClassA will handle the input and returning result, then ClassB will take the result from ClassA as parameter, then process it. ex:

public class ClassA
{
    public string MethodA()
    {
        // read the input
        return "Something"; // or return the input
    }
}

public class ClassB
{
    private int count;
    public void MethodB(string str)
    {
        count = str.Length;
    }
}

If you find the use of both class is bothersome, use another aggregate service for that. ex:

public class ClassC
{
    public ClassA ClassA = new ClassA();
    public ClassB ClassB = new ClassB();
    public void Execute(){
        string result = ClassA.MethodA();
        ClassB.MethodB(result);
    }
}
Chancery answered 8/4, 2015 at 2:7 Comment(1)
Perfect solutionImperil
U
4

Fluent API's solve this kind of thing on public interfaces by not exposing dependent methods in the "builder" object until appropriate:

SomeClass someInstance = SomeClassBuilder(x=> { 
     x.MethodA().MethodB("somevalue");
});

This requires alot more plumbling because you need the builder object, as well as builder components such as an object that is returned from MethodA which exposes MethodB. This way the only way to call MethodB is to first call MethodA.

I'm not encouraging you to take this approach. It's probably overkill for many scenarios, but is important to be aware of this option in case you encounter a scenario where it is appropriate.

Usurer answered 7/4, 2015 at 20:9 Comment(1)
I actually think this is a viable approach to this issue.Secretarygeneral
P
3

I guess you need to have a sort of complex initialization, in which some parameters have to be specified before actually initialize the object, and you want a better control on what the class user is doing to avoid invalid states. A good know pattern to solve such situation is the so called "Builder Pattern", very frequently used in OOP. I don't want to point a particular article, you will find yourself a lot of examples by just using the keyword "builder pattern". Just to be complete, the overall idea is to make a fluent sequence of method specifying values of internal fields, and delegate a final method "Build" to create an working object instance, and validate the parameters passed.

Pampas answered 7/4, 2015 at 19:54 Comment(2)
OK, I see your point, but the real problem is that I'm building a parser that reads characters in a sequential way, so MethodA reads from the input, sets some fields to a valid state and the MethodB is invoked. You cannot anticipate the input. Builder is great for some situations, but what about this case?Donnelldonnelly
Why you are reading the file in the Parser class ? I don't understand something or your approach is in conflict with SOLID principles ?Obannon
O
1

I don't know what is your exact goal, but why not put the parameter in the constructor of the class:

private class SomeClass
{
    private string _field;
    private int _count;

    public SomeClass(string field)
    {
        _field = field;
        _count = field.Length;
    }

}

Now you will have something like this

SomeClass sc = new SomeClass("Something");//or whatever you want for field.
Obannon answered 7/4, 2015 at 19:30 Comment(0)
H
0

You can just remove the parameter from MethodB and use the field, in this way you don't lose cohesion

private class SomeClass
{
    private string field;
    private int count;

    public SomeClass()
    {
        MethodA();
        MethodB();
    }

    private void MethodA()
    {
        field = "Something";
    }

    private void MethodB()
    {
        count = field.Length;
    }
}

Notes:

1) The way you describe the problem seems like Template Method design pattern, you should have a look here.

2) Static methods don't belong to that class

Harmattan answered 7/4, 2015 at 19:29 Comment(5)
This is technically wrong. Static methods can only belong to a class. They don't belong to an instance.Effie
What static method are you referring to, @ThorstenDittmar? All these look like instance methods to me.Secretarygeneral
@ThorstenDittmar You are right, static methods must be placed in a class, but not that class.Harmattan
@Secretarygeneral adricadar said that static methods don't belong to a class. That's what I was referring to.Effie
This still does not solve problem of temporal coupling. What if I change the order of invocaiton? It will throw null reference exception. Clearly calling method B requires temporal context and hence its coupled.Impenetrability

© 2022 - 2024 — McMap. All rights reserved.