What makes good class interface?
Good Class Interface
I. Why we need to create a Good Class Interface?
The first and probably most important step in creating a high-quality class is creating a good interface. This consists of creating a good abstraction for the interface to represent and ensuring that the details remain hidden behind the abstraction.
II. What makes a Good Class Interface?
1. Good Abstraction
- Be sure you understand what abstraction the class is implementing: Some classes are similar enough that you must be careful to understand which abstraction the class interface should capture.
- Provide services in pairs with their opposites: Most operations have corresponding, equal, and opposite operations. For example, if you have an operation that turns a light on, you will probably need one to turn it off.
- Move unrelated information to another class: In some cases, you will find that half a class’s routines work with half the class’s data and half the routines work with the other half of the data. Break them up!
2. Good Encapsulation
- Minimize accessibility of classes and members: If you are wondering whether a specific routine should be public, private or protected, you should favor the strictest level of privacy that’s workable.
- Don’t expose member data in public: Exposing member data is a violation of encapsulation and limits your control over the abstraction.
- Don’t make assumptions about the class’s users: A class should be designed and implemented to adhere to the contract implied by the class interface. It shouldn’s make any assumptions about how that interface will or won’t be used.
Something like this:
- Avoid friend classes: Friend classes are classes have the connection with each other. Friend classes break encapsulation.
- Here are some examples of the ways that a user of a class can break encapsulation semantically:
– Not calling Class A’s InitializeOperations() routine because you know that Class A’s PerformFirstOperation() routine calls it automatically.
– Not calling the database.Connect() routine before you call employee.Retrieve(database) because you know that the employee.Retrieve() function will connect to the database if there isn’t already a connection.
– Not calling Class A’s Terminate() routine because you know that Class A’s PerformFinalOperation() routine has already called it.
The problem with each of these examples is that they make the client code dependent not on the class’s public interface, but on its private implementation.
- Watch for coupling that’s too tight: “Coupling” refers to how tight the connection is between two classes. In general, the looser the connection, the better.
Reference: Code Complete 2 – Steve McConnell