How to deal with temporal coupling?

2019-05-01 03:36发布

问题:

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.

回答1:

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);
    }
}


回答2:

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.



回答3:

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.



回答4:

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



回答5:

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.