Preferring public method isolation

Things become complicated when public methods of a class call each other. This can be demonstrated fairly easily with some examples:

class UserController {
public onSave() {
this.saveToApi(this.userState);
}
public onBack() {
this.onSave();
this.navigateBack();
}
}

This can also be shown with the following dependency diagram. Which shows that onBack implicitly depends on onSave.

onBack
navigateBack
saveToApi
onSave
UserController

Which means if the onSave method is modified, then the behavior of the onBack method is also affected, which may be unintended

đź”—Implicit interdependence

Like many anti-patterns, it’s more damaging with larger classes. The example below shows how dependency relationships can become more intertwined…

viewUserNotifications
onViewNotifications
resetUserPassword
onResetPassword
navigateToReset
checkHasSaved
onLogout
logoutUser
viewUserDetails
onViewDetails
editUser
onEdit
onDelete
confirmAction
deleteFromApi
onBack
navigateTo
saveToApi
onSave
UserController

In this example, modifying the public method onSave will implicitly also change the behavior of onBack, onLogout… which can cause unintended effects.

đź”—How it appears:

onViewNotifications
onSearch
onResetPassword
onLogout
onViewDetails
onEdit
onDelete
onBack
onSave
UserController

đź”—How it is:

onViewNotifications
onResetPassword
onLogout
onViewDetails
onEdit
onDelete
onBack
onSave
UserController

The root of the problem is that the “public api” is often thought of as a “layer” of the system, where each part of the api is an entry point. When these public methods call each other they break this abstraction, so instead of a layer, it’s an iceberg.

đź”—Solution

The easy rule that I have adopted is simply “Public methods of the same class shouldn’t call each other”. If both public methods need shared functionality, then just use private methods… it will save a lot of headaches…

This isn’t a new idea, it’s basically part of Single Responsibility Principle.