4 principes essentiels pour les programmeurs

Le baiser, le yagni, le sec et le droit des principes de Demeter nous apprennent des personnes expérimentées comment résoudre des problèmes communs. Dans cet article, nous discuterons de chacun d’eux en détail et comprendrons comment ils peuvent nous aider à créer un code durable et professionnel.
Le baiser, le yagni, le sec et la loi des principes demètre, comme d’autres aspects de l’ingénierie logicielle, ne sont pas des règles rigides qui doivent être suivies à tout prix. Au lieu de cela, ils aident les développeurs à réfléchir et à rechercher des alternatives pour créer du code avec un ton plus professionnel.
Dans cet article, nous découvrirons l’histoire de chacun de ces quatre principes. De plus, nous examinerons des exemples pratiques qui peuvent être utilisés pour résoudre les problèmes quotidiens dans l’environnement de développement du système.
Pourquoi est-il important de se renseigner sur les principes?
L’application des principes de développement de logiciels est une caractéristique qui indique la maturité professionnelle d’un développeur. Il ne s’agit pas seulement de savoir comment programmer ou maîtriser une langue spécifique, mais de comprendre comment écrire du code qui sert bien son objectif, qui se développe durable et qui peut être maintenu par d’autres personnes ou même vous-même des mois ou des années plus tard, sans nécessiter beaucoup d’efforts.
Lorsque nous écrivons du code, notre inquiétude est généralement que la logique répond aux exigences fonctionnelles et que le système fonctionne correctement. De plus, lors de l’exécution d’un programme dans des langues telles que C #, le code écrit par le développeur est converti en code binaire (via la compilation IL et JIT), donc pour la machine, quelle que soit la philosophie que vous suivez, à la fin, tout sera transformé en binaire.
Ce que nous devons garder à l’esprit, c’est que le code bien écrit est destiné à bénéficier à d’autres développeurs – après tous, ce sont eux qui le liront, le maintiendront et l’évolueront à l’avenir.
Les principes sont importants car, à travers eux, nous apprenons à réfléchir à ce que nous créons. Ils ont été développés par des personnes qui ont eu une profonde influence sur le monde du développement des systèmes, de sa création à nos jours. Ce sont des gens qui ont rencontré de nombreux problèmes et ont décidé d’écrire ces principes et de les transmettre afin que d’autres puissent en apprendre.
Plus que des règles fixes, ces principes fournissent des moyens de raisonner sur les problèmes. En les apprenant, les programmeurs peuvent développer un état d’esprit critique et raffiné. Ils arrêtent simplement les «tâches de codage» et commencent à concevoir des solutions dans un objectif très important: la création de code qui est maintenable au fil du temps.
Ci-dessous, nous examinerons quatre principes: baiser, yagni, sec et la loi de Demeter. Vous pouvez accéder au code discuté dans le post dans ce référentiel GitHub: Principes de développement.
🧠 baiser
Kiss, un acronyme de «Keep it Simple, Stupid», souligne que la simplicité devrait être l’objectif principal de la conception. L’idée centrale est que les systèmes simples ont tendance à être plus faciles à comprendre, à maintenir et à réparer, ce qui signifie qu’ils sont moins chers.
Le terme est originaire de la marine américaine dans les années 1960 et a été attribué à l’ingénieur Kelly Johnson. Il aurait mis à l’épreuve son équipe de concevoir un nouveau jet afin qu’il puisse être facilement réparé sur le champ de bataille, en utilisant uniquement des outils disponibles sur le champ de bataille. Dans ce contexte, l’utilisation du mot «stupide» n’est pas une insulte, mais un rappel pragmatique: plus le système est simple, plus il sera résilient dans des situations réelles, surtout lorsqu’il y a peu de ressources pour gérer les échecs.
Exemple de baiser pratique
Pour démontrer l’utilisation de KISS, imaginons un scénario hypothétique, où vous devez créer un produit minimum viable (MVP) et l’une des exigences est que le projet a une API Web qui reçoit un emplacement et renvoie une température. Plus tard, cette API fera plus de choses, mais pour l’instant, sa fonction devrait être de simplement recevoir un emplacement et de retourner la température. Donc, si nous ignorons le baiser, nous pourrions avoir ce qui suit:
❌ Mauvais exemple – ne suit pas le baiser
public class WeatherForecastController : ControllerBase
{
private readonly WeatherService _weatherService;
public WeatherForecastController(WeatherService weatherService)
{
_weatherService = weatherService;
}
[HttpGet("weatherforecast/{location}")]
public async Task<string> Get(string location)
{
return await _weatherService.GetWeatherForecastAsync(location);
}
}
public class WeatherService
{
private readonly IWeatherRepository _repository;
private readonly IExternalWeatherApi _api;
public WeatherService(IWeatherRepository repository, IExternalWeatherApi api)
{
_repository = repository;
_api = api;
}
public async Task<string> GetWeatherForecastAsync(string location)
{
if (string.IsNullOrWhiteSpace(location))
throw new ArgumentException("Location is required");
var cached = await _repository.GetForecastAsync(location);
if (cached != null)
return cached;
var external = await _api.FetchForecastAsync(location);
await _repository.SaveForecastAsync(location, external);
return external;
}
}
Bien que ce code fonctionne bien pour les systèmes d’entreprise, pour un scénario simple, dans ce cas un MVP, il est trop complexe. Nous pouvons identifier plusieurs dépendances, telles que la mise en cache, la validation des données, la communication avec les services externes et l’enregistrement de données.
N’oubliez pas que la prémisse était une API qui recevrait un emplacement et renverrait une température, mais le code ci-dessus implémente beaucoup plus que cela, et pour un MVP, peu importe comment ces données sont renvoyées, ni si c’est vrai.
Dans ce cas, pour répondre aux exigences, nous pourrions simplement faire ce qui suit:
✅ bon exemple – Suivre des baisers
app.MapGet("/weatherforecast", (string location) =>
{
return Results.Ok("Sunny with 25°C");
});
Notez que l’exemple ci-dessus est beaucoup plus simple et répond parfaitement aux exigences, sans avoir besoin de plusieurs classes et dépendances (même le contrôleur a été éliminé). Le code n’a qu’un seul point final qui reçoit un emplacement et renvoie la température, c’est-à-dire qu’il est aussi simple que possible.
💡 Utilisez KISS chaque fois que vous avez besoin de mettre en œuvre quelque chose qui n’a besoin d’être fonctionnel et qui n’a pas de complexités comme prémisse. Concentrez-vous sur le minimum nécessaire pour répondre aux exigences et jetez tout le reste.
🙅♀️ Yagni
Yagni, un acronyme pour «vous n’en aurez pas besoin», se concentre sur la mise en œuvre de fonctionnalités qui ne sont pas encore nécessaires (et qui pourraient ne jamais être nécessaires).
Le terme est originaire de pratiques de développement de programmation extrême (XP), une méthodologie agile créée par Kent Beck à la fin des années 1990. À cette époque, de nombreuses équipes logicielles ont perdu du temps à essayer de prédire tout ce dont le système pourrait avoir besoin à l’avenir, ce qui a entraîné un code gonflé, des fonctionnalités inutilisées et de nombreux bogues.
Yagni a émergé comme une réponse directe et efficace à cette surenance: elle encourage la livraison incrémentielle et itérative, guidée par des besoins réels.
Exemple pratique de Yagni
Pour mettre en pratique Yagni, imaginons un scénario où la prémisse est que vous créez une classe de modèle pour représenter une entité utilisateur qui a un ID, un nom, un e-mail et un mot de passe.
Si vous n’avez pas suivi le principe, vous prévoyez déjà que les utilisateurs d’un jour pourraient avoir des profils sociaux, des avatars et des préférences de notification. Vous créez donc tout cela maintenant:
❌ Mauvais exemple – ne suit pas Yagni
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string? AvatarUrl { get; set; }
public SocialProfile? Social { get; set; }
public NotificationPreferences? Preferences { get; set; }
}
public class SocialProfile
{
public string? SocialName { get; set; }
public string? SocialEmail { get; set; }
}
public class NotificationPreferences
{
public bool EmailEnabled { get; set; }
public bool SmsEnabled { get; set; }
}
Ici, le profil social et les cours de notification ont été mis en œuvre même s’ils n’étaient pas inclus dans les exigences initiales.
Bien que cela puisse sembler simple, cet ajout a un code qui ne sera pas utilisé et peut également apporter de futurs bogues, car ORMS tels que l’entité Framework Core Treat les classes comme des entités de base de données et cela peut augmenter la complexité lorsqu’il s’agit de migrations de base de données, par exemple.
✅ bon exemple – Suivre Yagni
Pour suivre Yagni, il vous suffit de faire les bases – suivez simplement les exigences. Dans ce cas, nous pourrions faire ce qui suit:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string Password { get; set; }
}
Utilisez Yagni lorsque vous doutez de savoir si vous devez ou non proposer une implémentation. Si quelque chose n’est pas très pertinent et, plus important encore, n’est pas dans les exigences, cela signifie probablement qu’il ne devrait pas être mis en œuvre maintenant. Une alternative consiste à créer un élément du carnet de commandes qui peut, au bon moment, être ajouté au projet.
Séchage
Le principe sec, un acronyme de «Don’t Repeal Yourself», a été officiellement introduit dans le livre classique Le programmeur pragmatiquepublié en 1999 par les auteurs Andy Hunt et Dave Thomas.
Les défenseurs secs qui en double et logique devraient être extraits pour rendre le code réutilisable. L’application de ce principe évite les incohérences, facilite l’entretien et réduit les erreurs. De plus, le sec n’est pas limité à un simple code répété, mais à toute duplication de connaissances ou de logique dans le système, y compris les règles métier, SQL, validation, configuration, etc.
Exemple sec pratique
Pour implémenter Dry, considérons un exemple courant: gérer la mise en forme des e-mails. Supposons donc que nous ayons une classe de contrôleur qui a deux points de terminaison, un pour enregistrer un e-mail et le second pour la signature. Ces deux points de terminaison ont quelque chose en commun: vérifier les espaces blancs dans l’e-mail et le caractère AT (@) dans l’adresse fournie.
Dans un scénario qui ne suit pas le sec, cela se ferait comme suit:
❌ Mauvais exemple – ne suit pas sec
[HttpPost("register")]
public IActionResult Register([FromBody] string email)
{
if (string.IsNullOrWhiteSpace(email) || !email.Contains("@"))
return BadRequest("Invalid email");
var normalized = email.Trim().ToLower();
return Ok($"Registered: {normalized}");
}
[HttpPost("subscribe")]
public IActionResult Subscribe([FromBody] string email)
{
if (string.IsNullOrWhiteSpace(email) || !email.Contains("@"))
return BadRequest("Invalid email");
var normalized = email.Trim().ToLower();
return Ok($"Subscribed: {normalized}");
}
Notez que les deux points de terminaison ont le même code – ils reçoivent un e-mail, effectuent une validation et renvoient une erreur ou le résultat de l’e-mail normalisé. Ce code est dupliqué, auquel cas nous pouvons appliquer le sec.
✅ bon exemple – suivi sec
public static class EmailHelper
{
public static bool Normalize(string? input, out string normalized)
{
normalized = string.Empty;
if (string.IsNullOrWhiteSpace(input) || !input.Contains("@"))
return false;
normalized = input.Trim().ToLower();
return true;
}
}
[HttpPost("register")]
public IActionResult RegisterDry([FromBody] string email)
{
if (!EmailHelper.Normalize(email, out var normalized))
return BadRequest("Invalid email");
return Ok($"Registered: {normalized}");
}
[HttpPost("subscribe")]
public IActionResult SubscribeDry([FromBody] string email)
{
if (!EmailHelper.Normalize(email, out var normalized))
return BadRequest("Invalid email");
return Ok($"Subscribed: {normalized}");
}
Dans cette nouvelle version, nous avons créé une classe et une méthode statique pour valider et normaliser l’adresse e-mail. La méthode reçoit un texte (entrée) qui peut être nul et renvoie deux résultats, une valeur booléenne (vrai ou false) indiquant si la normalisation a été réussie ou non et une chaîne de sortie (normalisée) qui contiendra l’e-mail traité, si l’entrée est valide.
Enfin, nous appelons cette méthode dans les deux points de terminaison, éliminant ainsi la duplication du code et rendant la logique de normalisation de la messagerie électronique accessible à toute autre partie du code qui a besoin de cette fonctionnalité.
Utilisez sec chaque fois que vous remarquez qu’il y a quelque chose qui peut être réutilisé par d’autres parties du système, visant toujours à éliminer la répétition. Mais ne vous limitez pas au code. N’oubliez pas que le sec peut être utilisé partout où il y a une duplication inutile.
🤝 Loi de Demeter
La loi de Demeter, également connue sous le nom de principe des moindres connaissances, souligne que chaque unité logicielle devrait avoir des connaissances limitées sur d’autres unités avec lesquelles elle est étroitement liée, ou simplement: parler uniquement à vos amis proches.
En termes pratiques, la loi de Demeter détermine qu’une méthode d’un objet a ne doit appeler que les méthodes de:
- Lui-même (ceci)
- Paramètres reçus
- Objets créés par lui
- Ses attributs directs (composants)
La loi de Demeter est née en 1986 et a été proposée par Ian Holland. À l’époque, Holland et ses collègues travaillaient sur un projet appelé Demeter, dont l’objectif principal était d’étudier les moyens de développer des logiciels plus adaptables, en utilisant la programmation orientée objet pour minimiser le couplage entre les composants du système à travers des méthodes plus modulaires.
Ainsi, la loi de Demeter a été formulée comme une ligne directrice pour réduire la dépendance entre les objets et favoriser une plus grande encapsulation.
Exemple pratique de la loi de Demeter
Pour mettre la loi de Demeter en pratique, considérons un exemple courant où nous avons un objet client qui comprend une adresse de rue. Si nous voulions accéder à l’adresse de la rue d’un client, en ignorant la loi de Demeter, nous pourrions faire ce qui suit:
❌ Mauvais exemple – ne suit pas la loi de Demeter
public class Customer
{
public Address Address { get; set; }
public Customer(Address address)
{
Address = address;
}
}
public class Address
{
public Street Street { get; set; }
public Address(Street street)
{
Street = street;
}
}
public class Street
{
public string Name { get; set; }
public Street(string name)
{
Name = name;
}
}
[HttpPost("customer/street-name")]
public ActionResult<string> GetStreetNameViolation([FromBody] Customer customer)
{
if (customer?.Address?.Street == null)
return NotFound("Street not found");
string streetName = customer.Address.Street.Name;
return Ok(streetName);
}
Le code ci-dessus a un point final qui reçoit un client et renvoie le nom de la rue de l’adresse de ce client.
Le problème est que pour obtenir le nom de la rue, il navigue entre les objets qui font partie du client, et c’est exactement là que la loi de Demeter est violée. N’oubliez pas qu’un objet ne devrait accéder qu’à des objets à proximité? En ce sens, nous devons trouver un moyen de raccourcir le chemin et de réduire la dépendance entre le point de terminaison du contrôleur et le nom de la rue.
Ainsi, nous pouvons faire ce qui suit:
✅ Bon exemple – Suivre la loi de Demeter
public class Customer
{
public Address Address { get; set; }
public Customer(Address address)
{
Address = address;
}
public string GetStreetName()
{
return Address?.Street?.Name;
}
}
[HttpPost("customer/street-name")]
public ActionResult<string> GetStreetName([FromBody] Customer customer)
{
var streetName = customer?.GetStreetName();
if (string.IsNullOrEmpty(streetName))
return NotFound("Street not found");
return Ok(streetName);
}
Notez que la classe client a reçu une nouvelle méthode: GetStreetName()
qui accède aux propriétés et aux objets jusqu’à ce qu’il trouve le nom de la rue. De plus, le nouveau contrôleur accéde uniquement à l’objet client. Cela signifie que le contrôleur interagit uniquement avec le client, qui est son collaborateur direct. Le client est celui qui sait accéder Street.Name
. La logique de navigation est encapsulée dans l’objet lui-même, et cela est en harmonie avec la loi de Demeter.
Même si Customer.GetStreetName()
navigation en interne par Address?.Street?.Name
Ceci est contenu dans la classe elle-même, qui a le plein droit de connaître sa structure interne.
Utilisez la loi de Demeter chaque fois que vous voulez ou avez besoin de créer du code couplé vaguement. Ce n’est pas une règle difficile et rapide, mais plutôt une directive de conception pour favoriser l’encapsulation et le couplage lâche entre les objets.
Conclusion
La création de systèmes est complexe et pleine de subtilités, et il est inévitable que des problèmes surviennent au cours de ce processus. La bonne nouvelle est que de nombreuses personnes ont déjà traversé ces problèmes et ont décidé d’aider les autres à les surmonter. C’est pourquoi des principes de conception de logiciels existent – ils nous aident à créer des systèmes plus durables qui sont moins sujets aux erreurs.
Dans cet article, nous avons couvert quatre principes: Kiss, Yagni, Dry et la loi de Demeter. Nous avons également vu comment mettre en œuvre pratiquement chacun d’eux.
J’espère que ce post vous a aidé à comprendre ce que signifie chacun de ces principes et, plus important encore, à vous aider à créer des choses incroyables en réfléchissant à ce qu’ils nous apprennent.
Source link