Private Methods: What Do You Have To Hide?

We constantly refactoring & improving code bases but, sometimes we can be lukewarm about what we are doing. Enter the private method: Codebase Ruiner.

Private Methods: What Do You Have To Hide?

When you start using Object Oriented Programming (OOP), one of the first things you are introduced to is the concept of public & private methods & properties. Advertised as a way of exercising "black box" class design, using private members can allow your class to do/know things that consumers of that class don't necessarily need to worry about.

A common example of this would be calling some sort of Initialization() method inside of a class constructor to make sure a dependent object is set-up and created correctly.

public class HasPrivateMethod
{
    private readonly MyDependency _dependency;

    public HasPrivateMethod() 
    {
        _dependency = this.ConfigureMyDependency();
    }
    
    public void MethodThatUsesDependency()
    {
        var dependency = this.GetDependency();
        
        // Do something with dependency
        dependency.DoinThings();
    }

    private MyDependency ConfigureMyDependency()
    {
        var dependency = new MyDependency();
        
        // do stuff to prepare MyDependency
        
        return dependency;
    }
}

At first glace, this seems perfectly fine. This object knows how it will be using the dependency so, why wouldn't that class configure it correctly as well? But, that is actually a big part of the problem. This class forces the dependency to behave in a way that supports the consuming class' use case.

The thing is, that this tightly couples the two classes together in a way that if one changes the other must be updated as well. In this structure we have one class, HasPrivateMethod, dictating to it's dependency, MyDependency, how MyDependency should behave when it is a member of HasPrivateMethod.

This behavior is backwards. When bringing in a new dependency, the dependency should tell the dependent class how it will behave. In this specific example, MyDependency should be provided to HasPrivateMethods using the principals outlined by Inversion of Control.


While this is just one example it illustrates the main point:

A class should do one thing.

Private methods, more often than not, are used to hide additional functionality the class is managing that it doesn't want the rest of the world to know about.

A good question to ask yourself when creating a private function is:

If I were to make this method public, would this class still make sense to its consumers?

If it still makes sense to have that method on the class, then make it public. If it doesn't or if it might add complexity to the calling consumers (such as MethodA must always be called before MethodB), look at finding other ways to manage that functionality.

Maybe that is adding a new class to build the dependency for us in our previous example.

public class HadDependencyProvided
{
    private readonly MyDependency _dependency;

    public HasPrivateMethod(MyDependencyFactory dependencyFactory) 
    {
        _dependency = dependencyFactory.GetDependency();
    }
    
    public void MethodThatUsesDependency()
    {        
        // Do something with dependency
        _dependency.DoinThings();
    }
}

Typically a private method indicates too much functionality in a single class. The additional functionality should be moved to it's own class or, if the functionality really does make sense in the current class, it should be a public member.

public class BuildsOwnDependency
{
    public void MethodThatUsesDependency()
    {
        var dependency = this.GetDependency();
        
        // Do something with dependency
        dependency.DoinThings();
    }
    
    public MyDependency GetDependency()
    {
        var dependency = new MyDependency();
        
        // do stuff to prepare MyDependency
        
        return dependency;
    }
}


Code bases get messy as we add more features & fix bug, if we are not careful these "good behaviors" will weaken the codebase overall. Everything should be reviewed in a case by case basis, but it is important that we are always thinking about the decisions we make because the small changes add up. One day our code base can be a massive monument that we built or, an abandoned house that needs far more work than we can provide.