⬆️ ⬇️

Design Smell: Default Designer

This is the fifth post in the Poka-yoke design series - also known as encapsulation .



The default constructors are “smell” in the code. Exactly. This may sound outrageous. , but we take into account the following: object orientation is the encapsulation of behavior and data into connected pieces of code (classes). Encapsulation means that a class must protect the integrity of the data it encapsulates. When data is necessary, it must be requested through the constructor. In contrast, the default constructor says that no external data is required . This is a rather weak statement regarding class invariants.

Please note that this post describes the " smell ". This means that when a certain idiom or pattern (in this case, the default constructor) is found in the code, this should cause additional research.

As we will further note, there are several scenarios, when everything is fine with default constructors, so the goal of this post is not to destroy the default constructors. The goal is to provide food for thought.


If you read my book , you already know that injection into the constructor is the dominant DI pattern precisely because it statically shows dependencies and protects the integrity of the class, ensuring that the initialized consumer of these dependencies is always in a consistent state. This is a fail-safe design, because the compiler enhances this relationship by providing fast feedback .

This principle extends far beyond DI. In the previous post, I described how a constructor with arguments statically opens the necessary arguments.

public class Fragrance : IFragrance { private readonly string name; public Fragrance(string name) { if (name == null) { throw new ArgumentNullException("name"); } this.name = name; } public string Spread() { return this.name; } } 


The Fragrance class protects the integrity of the name by requiring it through the constructor. Since the class needs a name to implement its own behavior, it is good practice to request it through the constructor. The default constructor would not be fault tolerant, since it would introduce temporal connectivity .

Bear in mind that objects must be containers of behavior and data. While objects contain data, they must be encapsulated. In the (very common) case, when it is impossible to define meaningful defaults, the data must be passed to the object through the constructor. Thus, the presence of default constructors may indicate a violation of encapsulation.



In what cases is the use of default constructors justified?

There are scenarios in which the use of default constructors is completely justified (I am sure that there are more such scenarios than those listed below):



A class that represents pure behavior by implementing an interface is not necessarily a bad thing. This design can be very powerful.

In conclusion, the presence of a default constructor must be a signal in order to stop and think about the invariants of the class in question. Does the default constructor guarantee the integrity of the encapsulated data? If yes, then the default constructor fits; if not, it doesn't. In my experience, default constructors are the exception rather than the rule.


')

Source: https://habr.com/ru/post/205102/



All Articles