In this article, we will learn about couplers, their impact, and how to find and fix them. Couplers establish dependencies between components, making our code challenging to understand, modify, and test. The knowledge about how to untangle our code can certainly help with creating better software.

To download the source code for this article, you can visit our GitHub repository.

Let’s start!

How to Refactor Middle Man Class Couplers in C#

In this section, we are going to talk about a class that does little. The middle man’s job is just to delegate the task to another class. There is no additional logic or validation, just a call to a foreign method.

Let’s have a look at a quick example and create a TicketsProvider class:

public class TicketsProvider
{
    private readonly TicketGenerator _ticketGenerator = new();

    public Ticket GetConcertTicket(string band, DateTime date)
    {
        return _ticketGenerator.GenerateConcertTicket(band, date);
    }
}

The responsibility of this class is to return a concert ticket. Since another class (TicketGenerator) does this task, TicketsProvider just delegates it further.

Support Code Maze on Patreon to get rid of ads and get the best discounts on our products!
Become a patron at Patreon!

There are various reasons why we might find such code somewhere. For example, it could be a remnant of a class that did more tasks in the past. Alternatively, it can also happen when developers create unnecessary abstractions that make no sense and introduce new layers that only add complexity to the code.

In any case, such code counts as code smells for two reasons. First, an extra layer is created that is not only unnecessary but also adds to the complexity of the code. Second, if we want to generate a different type of ticket, such as a theater ticket, we will not only need to modify the TicketGenerator, but also the TicketsProvider class. So that’s additional work that does not positively affect the quality of our code.

The solution is simple, in which case it’s worth getting rid of the intermediate class and calling TicketGenerator directly:

var ticketGenerator = new TicketsGenerator();
var ticket = ticketGenerator.GenerateConcertTicket(band, date);

However, it is important to note that not all instances of the Middle Man need to be immediately eliminated. Sometimes we can add this type of code on purpose. One such case is when applying some design patterns, for example, Decorator.

Fixing Feature Envy Couplers in C#

The next code smell we are going to talk about is feature envy. This issue occurs when a method relies more heavily on the public features of a different class than it does on its own.

It is obvious that this will cause strong coupling between classes. Therefore, this code smell often violates the Tell, Don’t Ask principle, because we use many fields of another class to check the status or perform some calculations. Instead, according to the mentioned rule, it is better to tell the class what to do. In one word: encapsulation!

Let’s go straight to an example. Staying on the subject of buying tickets, imagine that on our ticketing platform, a user collects points. If a person has enough points, they can use them to purchase a ticket to an event:

public class Customer
{
    public string Name { get; init; } 
    public int Points { get; set; }
    
    public Customer(string name)
    {
        Name = name;
    }

    public void SubtractPoints(int points)
    {
        Points -= points;
    }

    public void ExchangePointsToTicket(Ticket ticket)
    {
        if (ticket.IsAvailable && ticket.CanAfford(Points))
        {
            ticket.BuyTicket();
            SubtractPoints(ticket.Price);
        }
    }
}

Let’s have a look at the Ticket class as well: 

public class Ticket
{
    public bool IsAvailable { get; private set; } 
    public int Price { get; init; }
    
    public Ticket(bool available, int price)
    {
        IsAvailable = available;
        Price = price;
    }

    public bool CanAfford(int points)
    {
        return points >= Price;
    }

    public void BuyTicket()
    {
        IsAvailable = false;
    }
}

As we can see, the Customer class often accesses fields and methods of the Ticket class (IsAvailable(), CanAfford(), and BuyTicket()).

To address this code smell, let’s refactor the code by calling the ticket-related methods from the Ticket class: 

public class Ticket
{
    public bool IsAvailable { get; private set; } 
    public int Price { get; init; }
    
    public Ticket(bool available, int price)
    {
        IsAvailable = available;
        Price = price;
    }

    private bool CanAfford(int points)
    {
        return points >= Price;
    }

    public int TryToBuyTicket(int points)
    {
        if (!IsAvailable || !CanAfford(points))
        {
            return 0;
        }
        IsAvailable = false;
        
        return Price;
    }
}

This way we can encapsulate the ticket operations within the class responsible for managing the ticket availability and pricing. It helps not only to get rid of the Feature Envy code smell but also alleviates the violation of the Single Responsibility Principle.

Now let’s check the new Customer class:

public class Customer
{
    public string Name { get; init; }
    public int Points { get; set; }
    
    public Customer(string name)
    {
        Name = name;
    }

    public void SubtractPoints(int points)
    {
        Points -= points;
    }

    public void ExchangePointsToTicket(Ticket ticket)
    {
        var usedPoints = ticket.TryToBuyTicket(Points);
        SubtractPoints(usedPoints);
    }
}

It’s worth noting that, as for the Middle Man, not all instances of the Feature Envy code smell require immediate removal. While it typically indicates a mismatch in responsibilities between classes, there are situations where it can be beneficial. For example, when introducing the Strategy design pattern.

How to Avoid Inappropriate Intimacy in C#

The most popular definition says that this code smell occurs when one class uses the private fields and methods of another class. In C#, accessing private fields is not possible, as they are restricted from external access. Great, then we can move to the next code smell, right?

Not really! In general, we speak about inappropriate intimacy when one class knows about the implementation details of a second class.

Examples? When one class needs to perform some actions from a different class in a certain order. Another one? When a class is tightly coupled to the exception-handling mechanisms of another class, catching and handling specific exceptions that should be handled within the class itself.

As an example, we have two classes called Customer and Order. The first class has only one method that checks if the account is active: 

public class Customer
{
    public bool IsActive { get; set; }
    public ICollection<string> FinalizedOperations { get; set; } = new List<string>();

    public void CheckIfActive()
    {
        if (!IsActive)
        {
            throw new InvalidOperationException("Customer is currently inactive.");
        }
    }
}

Now it’s time to see the Order class:

public class Order
{
    public string OrderNumber { get; set; }
    public bool IsFinalized { get; private set; }

    public void ProcessOrder(Customer customer)
    {
        try
        {
            customer.CheckIfActive();
        }
        catch (InvalidOperationException)
        {
            Console.WriteLine("Order can't be processed due to inactive user.");
            
            return;
        }

        customer.FinalizedOperations.Add(OrderNumber);
        IsFinalized = true;
    }
}

What can we see here? The way the Customer class is implemented enforces a specific order of actions in the Order class, as well as the need to handle exceptions. This represents the inappropriate intimacy code smell because one class has intimate knowledge of the inner workings of the second class.

Let’s see how we can improve it by moving the customer-specific code into the Customer class:

public class Customer
{
    public bool IsActive { get; set; }
    public ICollection<string> FinalizedOperations { get; set; } = new List<string>();

    public bool FinalizeOrder(string orderNumber)
    {
        try
        {
            CheckIfActive();
        }
        catch (InvalidOperationException)
        {
            Console.WriteLine("Order can't be processed due to inactive user.");
            
            return false;
        }

        FinalizedOperations.Add(orderNumber);
        
        return true;
    }

    private void CheckIfActive()
    {
        if (!IsActive)
        {
            throw new InvalidOperationException("Customer is currently inactive.");
        }
    }
}

Now let’s adjust Order:

public class Order
{
    public string OrderNumber { get; set; }
    public bool IsFinalized { get; private set; }

    public void ProcessOrder(Customer customer)
    {
        var orderFinalized = customer.FinalizeOrder(OrderNumber);
        IsFinalized = orderFinalized;
    }
}

This way, the Order class doesn’t have to know anything about the given customer. We just pass the data to the FinalizeOrder() method, which handles all the customer actions that should be performed.

Resolving Message Chains Couplers in C#

Let’s start with the answer to the question of what a message chain actually is. This piece of code contains a sequence of linked method calls:

new TicketGenerator()
    .ReserveSeat()
    .ProcessPayment()
    .SendEmailConfirmation()
    .GeneratePDFTicket();

This implementation creates several problems. First of all, the code is tightly coupled. This leads to more work when we want to modify something. Secondly, the code is more error-prone, especially if our message chain is long. Finally, testing can be a bit more complicated and depends on the hierarchy in the chain.

So how can we remedy message chains? The easiest way, which often works, is to hide the delegate. This method consists in hiding our chain in another method. Under the hood, everything works as it did, but outside we only call one method. This has several advantages. First, one public method instead of many is a convenience for the client. Second, we hide implementation details from it. If there are any changes, only our new method will experience the impact.

So, let’s create a GenerateTicket() method in the TicketGenerator class:

public void GenerateTicket() =>
    ReserveSeat().ProcessPayment().SendEmailConfirmation().GeneratePDFTicket();

Now a client using this class doesn’t have to worry about all the steps for ordering a new ticket. Let’s just call our newly created method:

new TicketGenerator().GenerateTicket();

Let’s just add that the use of LINQ or the builder pattern does not cause the discussed code smell. Since in both cases each of the called methods returns an object of the same type, and the called methods themselves are usually not too complicated, we don’t have to worry about it.

Conclusion

In this article, we explored various techniques for refactoring couplers in C#. The elimination of coupler code smells can help improve modularity, code reusability, testability, flexibility, and maintenance ease, making the codebase more effective and manageable.

Liked it? Take a second to support Code Maze on Patreon and get the ad free reading experience!
Become a patron at Patreon!