James Course - The Representable/Valid Principle Flashcards
Building something that does the right thing is easy. What is the challenge?
The challenge is to build something that can’t do the wrong thing.
Maintain a one-to-one correspondence between representable and valid states of your program.
One thing is what your program can do, another is what your program should do.
How can we eliminate an entire class of bugs?
By making sure that our program state space can’t represent invalid states and invalid transitions through states can’t happen.
Remember about the Thread.start vs Thread.run example.
How the Representable/Valid Principle changes our view of software?
The principle makes us understand how software is a collection of states, and transitions between theses states.
Thought this lenses of transitions and states we can constrain the state space to make bugs impossible.
Imagine a specific method can’t be called before calling another one. How can you make sure they will never be called in the wrong order?
You can move the second method to another object and make the first method return this object.
task.await(true)
true stands for interruptible tasks, how to prevent users of this API to make a mistake?
Use an enum
task.await(INTERRUPTIBLE)
Rectangle(int x, int y, int width, int height)
How to prevent programmers from passing parameters in the wrong order?
Use keyword arguments, or ask for a point and a dimension object instead of integers. Avoid primitive obsession.
What is the external view?
The external view looks at state transitions and help us to eliminate bad transitions.
What is the internal view?
The internal view look at what possible states could exist given the variables we have and help us to eliminate bad states.
When we create a object usually it has a representation invariant that described what must be true to be valid state representation. What invariant should we strive for?
We should strive for not having a representation invariant, that is, concrete state representations that always satisfies the invariant, that is, all concrete values are valid.
Remember the Oven example:
class Oven bool isOn, isBroil, isBake int bakeTemp
Representation invariant
¬(isBroil & isBake) &
((isBroil | isBake) => isOn) &
(isBake <=> bakeTemp != null)
The mapping between concrete states and abstract states is not 1 to 1.
What tools can we use to remove invalid state representations?
We can use algebraic data type like a sum or a product.
Use precise types to represent your data.
What is the power of design?
Is not in what it can do, but in what it can’t do.
Our goal is to design programs where is easy to do the right thing and impossible to do the wrong thing.
Should we have the possibility of having two concretes states representing the same abstract state?
No, the Representable/Valid Principle says that we should have a 1 to 1 mapping between concrete and abstract representations, that is, an injective function between concrete and abstract representations.
Which techniques were used in the JMock library that prevented library misuse?
The use of interfaces to define the syntax of JMock. This technique restricted the order that methods were allowed to be called.
Implement the syntax interfaces in Builder objects: These builder objects allowed the program to be built in two layers, one for the syntax and another one for the interpreter. The syntax layer was focused on providing methods to describe what we wanted the program to do in a declarative manner. When called these methods would build the object graph (interpretation layer) that executed the computation.
What is the fundamental idea behind returning one token object that is needed to call other methods (that is, enforcing ordering)?
The token represents a proof of work that should happen before calling some other method.
What does the abstract function do?
It removes the details of the implementation and only keeps what is essential to represent the abstract concept. Remember about the ring buffered queue.