One data abstraction design approach

Our design approach should structured into four stages (typically applied cyclically): definition of the public interface, usage scenarios, test specification, and finally, implementation.

The design approach I describe is highly practical and low-level, involving the creation of model classes and the necessary TDD tests. This work is essential for transitioning smoothly into the development phase.

Public interface

This involves precisely identifying the public operations, understanding what they should expect or assume upon invocation (REQUIRES), identifying what they alter (MODIFIES), and specifying what outcomes they yield (EFFECTS).

For example, imagine we are designing a Microwave object. One of the operations to define in the public interface could be: “setting the cooking time.”

By specifying ‘REQUIRES,’ we define the method’s invariant, indicating the conditions that must be met for the method to function as expected. Any input value that does not fulfill these conditions falls outside our scope of responsibility for the resulting behavior, underscoring the importance of adhering to these preconditions for correct operation.

Flowchart

In the cases where the method has some complicated logic, we can add in the design a flowchart with the expected behavior.

This example is very simple, but it can be all the complex it needs the funcionality, and can have call to another objects or the same object methods.

Usage scenarios

It involves reviewing all the potential uses of your abstraction, which could lead to introducing new operations that need to be outlined.

What does that mean in practice?

Usage scenarios entail building a class with a main method to test the functionality. This hands-on approach helps determine if more methods are needed by directly assessing how well the current setup meets the intended use cases.

Test specification

This means writing a comprehensive and detailed test suite for each operation before implementing it.

Test-driven design involves writing tests after your specifications and implementing your code until the tests pass.

This testing is the best way to ensure that your implementation adheres closely to its intended specifications. The developer should continue refining the implementation until the test passes.

It is prudent to provide the development team with initially failing tests, as it aligns with the Test-Driven Development (TDD) methodology. This practice enhances the efficiency of the development process and ensures the functionality delivered is robust and aligns with the specified requirements.

Calculating Test Cases from Method Effects

We will create a test for each clause of the EFFECTS or we can create a test for each branch of the flowchart if it exists, for example:

Following the EFFECTS:

We will do a test for any branch: sunny and a person in the desert, sunny and a person in the city is not sunny.

We will combine these two branches for each possible input, one value inside ranges (negative, 0), (0, 15), (15, 25), (25, higger).

We will combine these two branches with the boundary limits, too: 0, 15, and 25.

Total: 3 branches x (4 + 3) => 21 cases to test.

Conclusion

To conclude, by providing the development team with a project that includes an abstract object, its public methods outlining the invariants, the object it modifies, and its effects, as well as flowcharts depicting the desired behavior and tests that must be passed before development is considered complete, we ensure that the development is delivered with higher quality and more swiftly.

Nuria

Leave a comment