Couplage. Les meilleures pratiques

11

Suite à ce fil, j'ai commencé

Le motif singleton

Cela m'a fait réfléchir sur le couplage de mes cours et sur la meilleure façon de réaliser un couplage lâche. Veuillez garder à l'esprit que je suis un nouveau programmeur (4 mois après mon premier emploi) et c'est vraiment la première considération que j'ai donnée à cela, et je suis très désireux de comprendre le concept.

Alors, qu'est-ce qui constitue exactement un couplage lâche vs un couplage lourd? Dans mon projet actuel (et premier), je travaille sur un projet ac # winforms, où la section GUI crée des objets et souscrit à leurs événements, lorsqu'ils sont déclenchés, l'interface graphique crée un autre objet (dans cet exemple, un datagridview (une classe que j'ai créé qui enveloppe une vue de tableau de données standard et ajoute des fonctionnalités supplémentaires) et qui l'attache à l'interface graphique. Est-ce un mauvais couplage ou bon?

Je ne veux vraiment pas prendre de mauvaises habitudes et commencer à mal coder, donc j'apprécierais vos réponses.

Darren Young
la source

Réponses:

11

Pour rendre votre code faiblement couplé, voici quelques points simples à retenir:

Partie 1:

Techniquement connu sous le nom de «séparation des préoccupations». Chaque classe a un rôle spécifique, elle doit gérer la logique métier ou la logique applicative. Essayez d'éviter la classe qui combine les deux responsabilités. Par exemple, une classe qui gère des données (à long terme) est une logique d'application tandis qu'une classe qui utilise des données est une logique métier.

Personnellement, je me réfère à cela (dans mon propre petit monde) comme create it or use it. Une classe doit créer un objet ou utiliser un objet qu'elle ne doit jamais faire les deux.

Partie 2:

Comment mettre en œuvre la séparation des préoccupations.
Comme point de départ, il existe deux techniques simples:

Remarque: les modèles de conception ne sont pas absolus.
Ils sont censés être adaptés à la situation mais ont un thème sous-jacent similaire à toutes les applications. Alors ne regardez pas les exemples ci-dessous et dites que je dois suivre cela de manière rigide; ce ne sont que des exemples (et un peu artificiels à cela).

Injection de dépendance :

C'est là que vous passez un objet qu'une classe utilise. L'objet que vous transmettez basé sur une interface pour que votre classe sache quoi en faire mais n'a pas besoin de connaître l'implémentation réelle.

class Tokenizer
{
    public:
        Tokenizer(std::istream& s)
            : stream(s)
        {}
        std::string nextToken() { std::string token; stream >> token;return token;}
    private:
        std::istream& stream;
};

Ici, nous injectons le flux dans un Tokenizer. Le tokenizer ne sait pas de quel type est le flux tant qu'il implémente l'interface de std :: istream.

Modèle de localisateur de service :

Le modèle de localisateur de service est une légère variation de l'injection de dépendance. Plutôt que de donner un objet qu'il peut utiliser, vous lui passez un objet qui sait localiser (créer) l'objet que vous souhaitez utiliser.

class Application
{
     public:
         Application(Persister& p)
             : persistor(p)
         {}

         void save()
         {
             std::auto_ptr<SaveDialog> saveDialog = persistor.getSaveDialog();
             saveDialog.DoSaveAction();
         }

         void load()
         {
             std::auto_ptr<LoadDialog> loadDialog = persistor.getLoadDialog();
             loadDialog.DoLoadAction();
         }
    private:
        Persister& persistor;
};

Ici, nous passons l'objet d'application un objet persistant. Lorsque vous effectuez une action de sauvegarde / chargement, il utilise la persistance pour créer un objet qui sait réellement comment effectuer l'action. Remarque: Encore une fois, la persistance est une interface et vous pouvez fournir différentes implémentations en fonction de la situation.

Ceci est utile lorsqu'un potentiallyobjet unique est requis chaque fois que vous instanciez une action.

Personnellement, je trouve cela particulièrement utile pour écrire des tests unitaires.

Remarque sur les modèles:

Les modèles de conception sont un énorme sujet en soi. Il ne s'agit en aucun cas d'une liste exclusive de modèles que vous pouvez utiliser pour faciliter le couplage lâche; ce n'est qu'un point de départ commun.

Avec l'expérience, vous vous rendrez compte que vous utilisez déjà ces modèles, c'est juste que vous n'avez pas utilisé leurs noms formels. En normalisant leurs noms (et en faisant en sorte que tout le monde les apprenne), nous constatons qu'il est facile et plus rapide de communiquer des idées.

Martin York
la source
3
@Darren Young: Merci de l'accepter. Mais votre question n'a que trois heures. Je reviendrais dans un jour ou deux pour vérifier que d'autres n'ont pas fourni de meilleures réponses.
Martin York
Merci pour l'excellente réponse. En ce qui concerne la séparation des préoccupations ... J'ai des classes qui nécessitent des données pour son utilisation, puis je fais quelque chose avec ces données. C'est ainsi que dans le passé, j'ai conçu des cours. Serait-il donc préférable de créer une classe qui obtient les données et les adapte à la forme correcte puis une autre classe pour réellement utiliser les données?
Darren Young
@Darren Young: Comme pour toute programmation, la ligne est grise et floue pas bien définie. Il est difficile de donner une réponse exacte sans lire votre code. Mais quand je parle, managing the dataje me réfère aux variables (pas aux données réelles). Des choses comme les pointeurs doivent donc être gérées afin qu'elles ne fuient pas. Mais les données peuvent être injectées ou la façon dont les données sont récupérées peut être abstraite (afin que votre classe puisse être réutilisée avec différentes méthodes de récupération de données). Je suis désolé de ne pas pouvoir être plus précis.
Martin York
1
@Darren Young: Comme l'a noté @StuperUser dans sa réponse. N'allez pas trop loin (AKA minutae of loose coupling(aimez ce mot minuties)). Le secret de la programmation est d'apprendre quand utiliser les techniques. La surutilisation peut conduire à un enchevêtrement de code.
Martin York
@Martin - merci pour les conseils. Je pense que c'est là que je me bats actuellement ..... J'ai tendance à m'inquiéter constamment de l'architecture de mon code et aussi à essayer d'apprendre le langage spécifique que j'utilise C #. Je suppose que cela viendra avec l'expérience et mon désir d'apprendre ce genre de choses. J'apprécie vos commentaires.
Darren Young
6

Je suis un développeur ASP.NET, donc je ne sais pas grand-chose sur le couplage WinForms, mais je connais un peu les applications Web N-Tier, en supposant une architecture d'application à 3 niveaux UI, Domain, Data Access Layer (DAL).

Le couplage lâche concerne les abstractions.

Comme @MKO l'indique si vous pouvez remplacer un assembly par un autre (par exemple, un nouveau projet d'interface utilisateur qui utilise votre projet de domaine, un nouveau DAL qui enregistre dans une feuille de calcul plutôt qu'une base de données), il y a un couplage lâche. Si votre domaine et votre DAL dépendent de projets plus bas dans la chaîne, le couplage pourrait être plus lâche.

Un aspect de quelque chose étant vaguement couplé est de savoir si vous pouvez remplacer un objet par un autre qui implémente la même interface. Cela ne dépend pas de l'objet réel, mais de la description abstraite de ce qu'il fait (son interface).
Le couplage lâche, les interfaces et les injecteurs de dépendance (DI) et l'inversion de contrôle (IoC) sont utiles pour l'aspect d'isolement de la conception pour la testabilité.

Par exemple, un objet dans le projet UI appelle un objet Repository dans le projet Domain.
Vous pouvez créer un faux objet qui implémente la même interface que le référentiel utilisé par le code sous test, puis écrire un comportement spécial pour les tests ( stubs pour empêcher le code de production qui enregistre / supprime / obtient d'être appelé et des mocks qui agissent comme des stubs et gardent une trace de l'état du faux objet à des fins de test).
Cela signifie que le seul code de production appelé est désormais uniquement dans votre objet d'interface utilisateur, votre test sera uniquement contre cette méthode et tout échec de test isolera le défaut de cette méthode.

De plus, dans le menu Analyser de VS (en fonction de la version que vous avez), il existe des outils pour calculer les métriques de code pour votre projet, dont le couplage de classes, vous trouverez plus d'informations à ce sujet dans la documentation MSDN.

Ne vous embourbez PAS TROP dans les minuties du couplage lâche cependant, s'il n'y a AUCUNE chance que les choses soient réutilisées (par exemple, un projet de domaine avec plus d'une interface utilisateur) et que la durée de vie du produit est petite, le couplage lâche devient moins prioritaire (il sera toujours pris en compte), mais ce sera toujours la responsabilité des architectes / responsables techniques qui réviseront votre code.

StuperUser
la source
3

Le couplage fait référence au degré de connaissance directe qu'une classe a d'une autre . Ceci n'est pas censé être interprété comme une encapsulation vs une non-encapsulation. Ce n'est pas une référence à la connaissance d'une classe des attributs ou de l'implémentation d'une autre classe, mais plutôt à la connaissance de cette autre classe elle-même. Un couplage fort se produit lorsqu'une classe dépendante contient un pointeur directement vers une classe concrète qui fournit le comportement requis. La dépendance ne peut pas être substituée, ou sa "signature" modifiée, sans nécessiter une modification de la classe dépendante. Le couplage lâche se produit lorsque la classe dépendante contient un pointeur uniquement vers une interface, qui peut ensuite être implémentée par une ou plusieurs classes concrètes.La dépendance de la classe dépendante est à un "contrat" ​​spécifié par l'interface; une liste définie de méthodes et / ou de propriétés que les classes d'implémentation doivent fournir. Toute classe qui implémente l'interface peut ainsi satisfaire la dépendance d'une classe dépendante sans avoir à changer de classe. Cela permet une extensibilité dans la conception de logiciels; une nouvelle classe implémentant une interface peut être écrite pour remplacer une dépendance actuelle dans certaines ou toutes les situations, sans nécessiter de modification de la classe dépendante; les classes nouvelles et anciennes peuvent être échangées librement. Un couplage fort ne le permet pas. Lien de référence.

Amir Rezaei
la source
3

Jetez un œil aux 5 principes SOLID . En adhérant au SRP, le FAI et le DIP réduiront considérablement le couplage, le DIP étant de loin le plus puissant. C'est le principe fondamental sous le DI déjà mentionné .

En outre, GRASP mérite d'être examiné. C'est un étrange mélange entre des concepts abstraits (vous aurez du mal à mettre en œuvre au début) et des modèles concrets (qui pourraient en fait être utiles), mais la beauté est probablement la moindre de vos préoccupations en ce moment.

Et enfin, vous trouverez peut-être cette section sur l'IoC très utile, comme point d'entrée pour les techniques courantes.

En fait, j'ai trouvé une question sur stackoverflow , où je démontre l'application de SOLID sur un problème concret. Cela pourrait être une lecture intéressante.

back2dos
la source
1

Selon Wikipedia:

Dans le domaine de l'informatique et de la conception de systèmes, un système à couplage lâche est un système dans lequel chacun de ses composants a ou utilise peu ou pas de connaissances des définitions d'autres composants distincts.

Le problème avec un couplage serré rend difficile d'apporter des modifications. (De nombreux auteurs semblent suggérer que cela provoque principalement des problèmes lors de la maintenance, mais d'après mon expérience, cela est également pertinent lors du développement initial.) dans les modules auxquels il est couplé. Plus souvent qu'autrement, cela nécessite plus de changements dans d'autres modules et ainsi de suite.

En revanche, dans un système faiblement couplé, les changements sont relativement isolés. Ils sont donc moins coûteux et peuvent être réalisés avec une plus grande confiance.

Dans votre exemple particulier, le traitement des événements fournit une certaine séparation entre l'interface graphique et les données sous-jacentes. Cependant, il semble qu'il existe d'autres domaines de séparation que vous pourriez explorer. Sans plus de détails sur votre situation particulière, il est difficile d'être précis. Cependant, vous pouvez commencer par une architecture à 3 niveaux qui se sépare:

  • Logique de stockage et de récupération des données
  • Logique métier
  • Logique requise pour exécuter l'interface utilisateur

Quelque chose à considérer est que pour les petites applications ponctuelles avec un seul développeur, les avantages de l'application d'un couplage lâche à tous les niveaux ne valent pas la peine. D'un autre côté, les applications plus grandes et plus complexes avec plusieurs développeurs sont indispensables. Initialement, l'introduction des abstractions et l'éducation des développeurs qui ne connaissent pas le code concernant son architecture entraînent des coûts. À long terme, cependant, le couplage lâche offre des avantages qui dépassent de loin les coûts.

Si vous envisagez sérieusement de concevoir des systèmes à couplage lâche, lisez les principes SOLIDES et les modèles de conception.

La chose importante à réaliser, cependant, est que ces modèles et principes ne sont que cela - des modèles et des principes. Ce ne sont pas des règles. Cela signifie qu'ils doivent être appliqués de manière pragmatique et intelligente

En ce qui concerne les modèles, il est important de comprendre qu'il n'y a pas de mise en œuvre "correcte" unique des modèles. Ce ne sont pas non plus des modèles de cookies pour concevoir votre propre implémentation. Ils sont là pour vous dire quelle forme une bonne solution pourrait avoir et pour vous fournir un langage commun pour communiquer les décisions de conception avec d'autres développeurs.

Bonne chance.

Kramii
la source
1

Utilisez l'injection de dépendance, les modèles de stratégie et les événements. En général: pour en savoir plus sur les modèles de conception, il s'agit de couplage lâche et de réduction des dépendances. Je dirais que les événements sont à peu près aussi peu couplés que vous le feriez, tandis que l'injection de dépendances et les stratégies de stratégie nécessitent certaines interfaces.

Une bonne astuce consiste à placer les classes dans différentes bibliothèques / assemblages et à les faire dépendre du moins possible d'autres bibliothèques, ce qui vous obligera à refactoriser pour utiliser moins de dépendances

Homde
la source
4
Le préservatif est-il un terme informatique abstrait ou est-ce que je ne reçois tout simplement pas le jeu de mots?
Darren Young
3
C'est un autre nom pour le conteneur d'injection de dépendance.
Mchl
2
C'est également un excellent moyen de prévenir la propagation des virus. Parlons-nous de la même chose?
sova
1
Le couplage lourd est souvent effectué sur le backend
Homde
1
Définir un titre
n'abuse
0

Permettez-moi de fournir une autre vue. Je pense simplement à cela en termes que chaque classe est une bonne API. L'ordre dans lequel les méthodes sont appelées est évident. Ce qu'ils font est évident. Vous avez réduit le nombre de méthodes au minimum nécessaire. Pour des exemples,

init, ouvrir, fermer

contre

setTheFoo, setBar, initX, getConnection, close

Le premier est évident et ressemble à une belle API. Le second peut provoquer des erreurs s'il est appelé dans le mauvais ordre.

Je ne m'inquiète pas trop de devoir modifier et recompiler les appelants. Je maintiens BEAUCOUP de code dont une partie nouvelle et une quinzaine d'années. Je souhaite généralement des erreurs de compilation lorsque j'apporte des modifications. Parfois, je vais délibérément casser une API pour cette raison. Cela me donne une chance de considérer les ramifications sur chaque appelant. Je ne suis pas un grand fan de l'injection de dépendances car je veux pouvoir tracer visuellement mon code sans boîtes noires et je veux que le compilateur capture autant d'erreurs que possible.

Sean McEligot
la source