DEV Community

Cover image for How Object Calisthenics Helped Me Write Better C# Code
Daniel Azevedo
Daniel Azevedo

Posted on

How Object Calisthenics Helped Me Write Better C# Code

Hi devs :)

I’ve been diving into Object Calisthenics recently, and while it seems like a great practice, I know it’s not everyone’s cup of tea. It’s a set of 9 rules aimed at improving object-oriented design, but some developers are skeptical. Despite that, I’ve found these rules really helpful in making my C# code cleaner and more maintainable.

What Are These Rules?

Object Calisthenics encourages you to follow nine rules to enhance the modularity and readability of your code. Here’s a rundown with C# examples:

  1. One Level of Indentation Per Method Rule: Methods should have only one level of indentation. Example: Instead of this:
   public void ProcessOrder(Order order)
   {
       if (order.IsValid())
       {
           if (order.IsPaid())
           {
               SendConfirmation(order);
           }
           else
           {
               HandleUnpaidOrder(order);
           }
       }
   }
Enter fullscreen mode Exit fullscreen mode

Refactor to:

   public void ProcessOrder(Order order)
   {
       if (!order.IsValid())
       {
           HandleInvalidOrder(order);
           return;
       }
       if (order.IsPaid())
       {
           SendConfirmation(order);
       }
       else
       {
           HandleUnpaidOrder(order);
       }
   }
Enter fullscreen mode Exit fullscreen mode

2 . Don’t Use the else Keyword

Rule: Avoid using else by returning early.

Example: Instead of:

   public decimal CalculateDiscount(Customer customer)
   {
       if (customer.IsVIP())
       {
           return 0.2m;
       }
       else
       {
           return 0.1m;
       }
   }
Enter fullscreen mode Exit fullscreen mode

Refactor to:

   public decimal CalculateDiscount(Customer customer)
   {
       if (customer.IsVIP())
       {
           return 0.2m;
       }
       return 0.1m;
   }
Enter fullscreen mode Exit fullscreen mode

3 . Wrap All Primitives and Strings

Rule: Encapsulate primitive data types in their own classes.

Example: Instead of using raw integers for age:

   public void SetAge(int age)
   {
       this.age = age;
   }
Enter fullscreen mode Exit fullscreen mode

Wrap it:

   public class Age
   {
       public int Value { get; private set; }

       public Age(int value)
       {
           Value = value;
       }
   }

   public void SetAge(Age age)
   {
       this.age = age.Value;
   }
Enter fullscreen mode Exit fullscreen mode

4 . First Class Collections

Rule: Collections should be managed by their own classes.

Example: Instead of handling a list of items directly:

   public class Order
   {
       public List<Item> Items { get; private set; } = new List<Item>();
   }
Enter fullscreen mode Exit fullscreen mode

Refactor to:

   public class OrderItems
   {
       private List<Item> items = new List<Item>();

       public void AddItem(Item item)
       {
           items.Add(item);
       }

       public IReadOnlyList<Item> Items => items.AsReadOnly();
   }

   public class Order
   {
       public OrderItems Items { get; } = new OrderItems();
   }
Enter fullscreen mode Exit fullscreen mode

5 . One Dot Per Line

Rule: Limit method chaining to one per line.

Example: Instead of:

   var zipcode = order.Customer.Address.ZipCode;
Enter fullscreen mode Exit fullscreen mode

Refactor to:

   var customer = order.Customer;
   var address = customer.Address;
   var zipcode = address.ZipCode;
Enter fullscreen mode Exit fullscreen mode

6 . No Abbreviations

Rule: Use full, descriptive names.

Example: Instead of CalcDisc, use:

   public decimal CalculateDiscount()
   {
       // Implementation
   }
Enter fullscreen mode Exit fullscreen mode

7 . Keep Entities Small

Rule: Classes should be small and focused.

Example: Instead of a large class with multiple responsibilities:

   public class Customer
   {
       public string Name { get; set; }
       public string Address { get; set; }
       public List<Order> Orders { get; set; }

       public void PlaceOrder(Order order)
       {
           // Implementation
       }

       public void UpdateAddress(string newAddress)
       {
           // Implementation
       }
   }
Enter fullscreen mode Exit fullscreen mode

Refactor into smaller classes:

   public class Customer
   {
       public string Name { get; set; }
       public Address Address { get; set; }
   }

   public class Address
   {
       public string Value { get; set; }

       public void UpdateAddress(string newAddress)
       {
           // Implementation
       }
   }

   public class OrderManager
   {
       public void PlaceOrder(Customer customer, Order order)
       {
           // Implementation
       }
   }
Enter fullscreen mode Exit fullscreen mode

8 . No More Than Two Instance Variables

Rule: Limit classes to two instance variables.

Example: Instead of:

   public class Order
   {
       public List<Item> Items { get; set; }
       public Customer Customer { get; set; }
       public DateTime Date { get; set; }
       public string Status { get; set; }
   }
Enter fullscreen mode Exit fullscreen mode

Refactor to:

   public class Order
   {
       public List<Item> Items { get; set; }
       public Customer Customer { get; set; }
   }

   public class OrderDetails
   {
       public Order Order { get; set; }
       public DateTime Date { get; set; }
       public string Status { get; set; }
   }
Enter fullscreen mode Exit fullscreen mode

9 . No Getters/Setters

Rule: Avoid exposing internal state through getters and setters.

Example: Instead of:

   public class Customer
   {
       public string Name { get; set; }
   }
Enter fullscreen mode Exit fullscreen mode

Refactor to:

   public class Customer
   {
       private string name;

       public void ChangeName(string newName)
       {
           name = newName;
       }
   }
Enter fullscreen mode Exit fullscreen mode

Why I Think It’s Worth Trying

I understand that Object Calisthenics might not be everyone's favorite approach. Some developers might find these rules a bit rigid or impractical. However, I’ve personally found that applying these practices has led to cleaner and more maintainable code. It’s like a workout for your coding habits — challenging but rewarding.

Even if you don’t follow all the rules strictly, experimenting with some of these principles can improve your coding practices. Have you tried Object Calisthenics or have any thoughts on improving object-oriented design? I’d love to hear your experiences!

Top comments (0)