It really depends on whether your interface is stateful or stateless. It can be perfectly fine to include pre and/or post conditions for interface methods. In fact, we do this all the time. Any time you create a piece of javadoc (or any other tool), you are creating a contract. Otherwise, how could you test anything? It's important to realize that test-driven-development and design-by-contract have much in common. Defining a contract is essential to proper tdd - you first design an interface and create an informal contract for it (using human-readable language). Then, you write a test to ensure contract is satisfied. If we follow tdd classicists (https://www.thoughtworks.com/insights/blog/mockists-are-dead-long-live-classicists), we always write tests against contracts.
Now, to be more specific. If interface is stateful, we can easily express its invariants according to other methods. Let's take a java List
interface as an example:
If you read the javadoc carefully, you will see there are a lot of invariants. For instance, the add
method has the following contract:
Preconditions: element cannot be null (if list doesn't support it -
it's a design smell btw in my opinion, but let's set it aside for
now)
Postconditions: ordering is preserved, i.e. the ordering of other
elements cannot be changed
Since List
interface is definitely stateful, we can reason about the state of the list using query method, like get
, sublist
etc. Therefore, you can express all the invariants based on interface's methods.
In case of an interface which is stateless, such as Calculator
, we also define a contract, but its invariants do not include any state. So, for example, the sum
method can have the following contract:
int sum(int a, int b)
Preconditions: a and b are integers (which is automatically guaranteed by static type checking in Java)
Postconditions: the result is an integer (again - type safety) which is equal to a + b
Our Calculator
is a stateless interface, therefore we don't include any state in our invariants.
Now, let's get back to your BankAccount
example:
The way you describe it, BankAccount
is definitely a stateful interface. In fact, it's a model example of what we call an Entity
(in terms of domain-driven-design). Therefore, BankAccount
has it's lifecycle, it's state and can (and will) change during its lifetime. Therefore, it's perfectly fine to express your contracts based on the state methods of your class. All you need to do, is to move your amount
, balance
and overdraft
to the top of the interface, either as properties (if your language supports it) or methods - it doesn't really matter. What's important is that amount
, balance
and overdraft
are now part of your interface, and form the ubiquitous language of your interface. These methods/properties are integral part of your entire BankAccount
interface - which means, they can be used as part of your interface's contract.
Some time ago I've implemented a very simple prototype of Java contracts, implemented as set of annotations supported by Aspect Oriented Programming. I tried to achieve similar goal to yours - to integrate contracts with language and make them more formal. It was just a very simple prototype, but I think it expressed the idea quite well. If you are interested - I should probably upload it to the github soon (I've been using bitbucket for most of the time so far).