Skip to main content

The simplest solution is to merge Initialize with the constructor. That way, the object will never be available for the client in the the uninitialized state so the error is not possible. In case you cannot do the initialization in the constructor itself, you can create a factory method. For example, if your class requires certain events to be registered, you could require the event listener as a parameter in the constructor or factory method signature.

Compared to runtime exceptions, it is more work to represent states as distinct classes, but it also givegives you compile-time guarantees that you are not calling methods which are invalid for the object state, and it documents the state transitions more clearly in code.

If you have complex temporal coupling (a number of methods must be called in a certain order), one solution could be to use inversion of control, so you create a class which calls the methods in the appropriate order, but uses template methods or events to allow the client to perform custom operations at appropriate stages in the flow. That way, the responsibility for performing operations in the right order is pushed from the client to the class itself.

A simple example: You have a File-object which allows you to read from a file. However, the client needs to call Open before the ReadLine method can be called, and needneeds to remember to always call Close (even if an exception occurs) after which the ReadLine method must not be called anymore. This is temporal coupling. It can be avoided by having a single method which takes a callback or delegate as an argument. The method manages opening the file, calling the callback and then closing the file. It can pass a distinct interface with a Read method to the callback. That way, it is not possible for the the client to forget to call methods in the correct order.

The simplest solution is to merge Initialize with the constructor. That way the object will never be available for the client in the the uninitialized state so the error is not possible. In case you cannot do the initialization in the constructor itself, you can create a factory method. For example, if your class requires certain events to be registered, you could require the event listener as a parameter in the constructor or factory method signature.

Compared to runtime exceptions, it is more work to represent states as distinct classes, but it also give you compile-time guarantees that you are not calling methods which are invalid for the object state, and it documents the state transitions more clearly in code.

If you have complex temporal coupling (a number of methods must be called in a certain order) one solution could be to use inversion of control, so you create a class which calls the methods in the appropriate order, but uses template methods or events to allow the client to perform custom operations at appropriate stages in the flow. That way the responsibility for performing operations in the right order is pushed from the client to the class itself.

A simple example: You have a File-object which allows you to read from a file. However the client needs to call Open before the ReadLine method can be called, and need to remember to always call Close (even if an exception occurs) after which the ReadLine method must not be called anymore. This is temporal coupling. It can be avoided by having a single method which takes a callback or delegate as an argument. The method manages opening the file, calling the callback and then closing. It can pass a distinct interface with a Read method to the callback. That way it is not possible for the the client to forget to call methods in the correct order.

The simplest solution is to merge Initialize with the constructor. That way, the object will never be available for the client in the uninitialized state so the error is not possible. In case you cannot do the initialization in the constructor itself, you can create a factory method. For example, if your class requires certain events to be registered, you could require the event listener as a parameter in the constructor or factory method signature.

Compared to runtime exceptions, it is more work to represent states as distinct classes, but it also gives you compile-time guarantees that you are not calling methods which are invalid for the object state, and it documents the state transitions more clearly in code.

If you have complex temporal coupling (a number of methods must be called in a certain order), one solution could be to use inversion of control, so you create a class which calls the methods in the appropriate order, but uses template methods or events to allow the client to perform custom operations at appropriate stages in the flow. That way, the responsibility for performing operations in the right order is pushed from the client to the class itself.

A simple example: You have a File-object which allows you to read from a file. However, the client needs to call Open before the ReadLine method can be called, and needs to remember to always call Close (even if an exception occurs) after which the ReadLine method must not be called anymore. This is temporal coupling. It can be avoided by having a single method which takes a callback or delegate as an argument. The method manages opening the file, calling the callback and then closing the file. It can pass a distinct interface with a Read method to the callback. That way, it is not possible for the client to forget to call methods in the correct order.

added 450 characters in body
Source Link
JacquesB
  • 62.4k
  • 21
  • 137
  • 190

A simple example: You have a File-object which allows you to read from a file. However the client needs to call Open before the ReadReadLine method can be called, and need to remember to always call Close (even if an exception occurs) after which the ReadReadLine method must not be called anymore. This is temporal coupling. It can be avoided by having a single method which takes a callback or delegate as an argument. The method manages opening the file, calling the callback and then closing. It can pass a distinct interface with a Read method to the callback. That way it is not possible for the the client to forget to call methods in the correct order.

class File {
     /// Must be called on a closed file.
     /// Remember to always call Close() when you are finished
     public void Open();

     /// must be called on on open file
     public string ReadLine();

     /// must be called on an open file
     public void Close();
}
class File {
    /// Opens the file, executes the callback, and closes the file again.
    public void Consume(Action<IOpenFile> callback);
}

interface IOpenFile {
    string ReadLine();
}

A more heavyweight solution would be to define File as an abstract class which requires you to implement a (protected) method which will be executed on the open file. This is called a template method pattern.

abstract class File {
    /// Opens the file, executes ConsumeOpenFile(), and closes the file again.
    public void Consume();

    /// override this
    abstract protected ConsumeOpenFile();

    /// call this from your ConsumeOpenFile() implementation
    protected string ReadLine();
}

The advantage is the same: The client does not have to remember to call methods in a certain order. It is simply not possible to call methods in the wrong order.

A simple example: You have a File-object which allows you to read from a file. However the client needs to call Open before the Read method can be called, and need to remember to always call Close (even if an exception occurs) after which the Read method must not be called anymore. This is temporal coupling. It can be avoided by having a single method which takes a callback or delegate as an argument. The method manages opening the file, calling the callback and then closing. It can pass a distinct interface with a Read method to the callback. That way it is not possible for the the client to forget to call methods in the correct order.

class File {
     /// Must be called on a closed file.
     /// Remember to always call Close() when you are finished
     void Open();

     /// must be called on on open file
     string ReadLine();

     /// must be called on an open file
     void Close();
}
class File {
    // Opens the file, executes the callback, and closes the file again.
    Consume(Action<IOpenFile> callback);
}

interface IOpenFile {
    string ReadLine();
}

A more heavyweight solution would be to define File as an abstract class which requires you to implement a (protected) method which will be executed on the open file. This is called a template method pattern. The advantage is the same: The client does not have to remember to call methods in a certain order.

A simple example: You have a File-object which allows you to read from a file. However the client needs to call Open before the ReadLine method can be called, and need to remember to always call Close (even if an exception occurs) after which the ReadLine method must not be called anymore. This is temporal coupling. It can be avoided by having a single method which takes a callback or delegate as an argument. The method manages opening the file, calling the callback and then closing. It can pass a distinct interface with a Read method to the callback. That way it is not possible for the the client to forget to call methods in the correct order.

class File {
     /// Must be called on a closed file.
     /// Remember to always call Close() when you are finished
     public void Open();

     /// must be called on on open file
     public string ReadLine();

     /// must be called on an open file
     public void Close();
}
class File {
    /// Opens the file, executes the callback, and closes the file again.
    public void Consume(Action<IOpenFile> callback);
}

interface IOpenFile {
    string ReadLine();
}

A more heavyweight solution would be to define File as an abstract class which requires you to implement a (protected) method which will be executed on the open file. This is called a template method pattern.

abstract class File {
    /// Opens the file, executes ConsumeOpenFile(), and closes the file again.
    public void Consume();

    /// override this
    abstract protected ConsumeOpenFile();

    /// call this from your ConsumeOpenFile() implementation
    protected string ReadLine();
}

The advantage is the same: The client does not have to remember to call methods in a certain order. It is simply not possible to call methods in the wrong order.

added 522 characters in body
Source Link
JacquesB
  • 62.4k
  • 21
  • 137
  • 190

The simplest solution is to merge Initialize with the constructor. That way the object will never be available for the client in the the uninitialized objectstate so the error is not possible. In case you cannot do the initialization in the constructor itself, you can create a factory method. For example, if your class requires certain events to be registered, you could require the event listener as a parameter in the constructor or factory method parameterssignature.

If you for some reason need to be able to access the uninitialized object before initialization, then you could have the two states implemented as separate classes, so you start out with an UnitializedFooManager instance, which have an Initialize(...) method which returns InitializedFooManager. The methods which can only be called in the initialized state only exist on InitializedFooManager. This approach can be extended to multiple states if you need to.

If you have complex temporal coupling (a number of methods must be called in a certain order) one solution could be to use inversion of control, so you create a class which calls the appropriate methods in the appropriate order, but uses template methods or events to allow the client to perform custom operations at appropriate momentsstages in the flow. That way the responsibility for performing operations in the right order is pushed from the client to the class itself.

class File {
    // Opens the file, executes the callback, and closes the file again.
    Consume(Action<IOpenFile> callback);
}

interface IOpenFile {
    string ReadLine();
}

The simplest solution is to merge Initialize with the constructor. That way the object will never be available for the client in the the uninitialized object so the error is not possible. In case you cannot do the initialization in the constructor itself, you can create a factory method. For example, if your class requires certain events to be registered, you could require the event listener in the constructor or factory method parameters.

If you for some reason need to be able to access the uninitialized object before initialization, then you could have the two states implemented as separate classes, so you start out with an UnitializedFooManager instance, which have an Initialize(...) method which returns InitializedFooManager. The methods which can only be called in the initialized state only exist on InitializedFooManager. This approach can be extended to multiple states if you need to.

If you have complex temporal coupling (a number of methods must be called in a certain order) one solution could be to use inversion of control, so you create a class which calls the appropriate methods in the appropriate order, but uses template methods or events to allow the client to perform operations at appropriate moments in the flow. That way the responsibility for performing operations in the right order is pushed from the client to the class itself.

class File {
    Consume(Action<IOpenFile> callback);
}

interface IOpenFile {
    string ReadLine();
}

The simplest solution is to merge Initialize with the constructor. That way the object will never be available for the client in the the uninitialized state so the error is not possible. In case you cannot do the initialization in the constructor itself, you can create a factory method. For example, if your class requires certain events to be registered, you could require the event listener as a parameter in the constructor or factory method signature.

If you need to be able to access the uninitialized object before initialization, then you could have the two states implemented as separate classes, so you start out with an UnitializedFooManager instance, which have an Initialize(...) method which returns InitializedFooManager. The methods which can only be called in the initialized state only exist on InitializedFooManager. This approach can be extended to multiple states if you need to.

If you have complex temporal coupling (a number of methods must be called in a certain order) one solution could be to use inversion of control, so you create a class which calls the methods in the appropriate order, but uses template methods or events to allow the client to perform custom operations at appropriate stages in the flow. That way the responsibility for performing operations in the right order is pushed from the client to the class itself.

class File {
    // Opens the file, executes the callback, and closes the file again.
    Consume(Action<IOpenFile> callback);
}

interface IOpenFile {
    string ReadLine();
}
added 522 characters in body
Source Link
JacquesB
  • 62.4k
  • 21
  • 137
  • 190
Loading
added 866 characters in body
Source Link
JacquesB
  • 62.4k
  • 21
  • 137
  • 190
Loading
added 866 characters in body
Source Link
JacquesB
  • 62.4k
  • 21
  • 137
  • 190
Loading
added 866 characters in body
Source Link
JacquesB
  • 62.4k
  • 21
  • 137
  • 190
Loading
added 866 characters in body
Source Link
JacquesB
  • 62.4k
  • 21
  • 137
  • 190
Loading
added 96 characters in body
Source Link
JacquesB
  • 62.4k
  • 21
  • 137
  • 190
Loading
added 84 characters in body
Source Link
JacquesB
  • 62.4k
  • 21
  • 137
  • 190
Loading
added 84 characters in body
Source Link
JacquesB
  • 62.4k
  • 21
  • 137
  • 190
Loading
added 84 characters in body
Source Link
JacquesB
  • 62.4k
  • 21
  • 137
  • 190
Loading
Source Link
JacquesB
  • 62.4k
  • 21
  • 137
  • 190
Loading