window.pipedriveLeadboosterConfig = { base : 'leadbooster-chat.pipedrive.com', companyId : 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', version : 2, } ;(function () { var w = window if (w.LeadBooster) { console.warn('LeadBooster existe déjà') } else { w.LeadBooster = { q : [], on : function (n, h) { this.q.push({ t : 'o', n : n, h : h }) }, trigger : function (n) { this.q.push({ t : 't', n : n }) }, } } })() Le principe de l'ouverture-fermeture. Dois-je l'utiliser un jour ? - The Codest
The Codest
  • A propos de nous
  • Services
    • Développement de logiciels
      • Développement frontal
      • Développement backend
    • Staff Augmentation
      • Développeurs frontaux
      • Développeurs backend
      • Ingénieurs des données
      • Ingénieurs en informatique dématérialisée
      • Ingénieurs AQ
      • Autres
    • Conseil consultatif
      • Audit et conseil
  • Industries
    • Fintech et banque
    • E-commerce
    • Adtech
    • Santé (Healthtech)
    • Fabrication
    • Logistique
    • Automobile
    • IOT
  • Valeur pour
    • CEO
    • CTO
    • Gestionnaire des livraisons
  • Notre équipe
  • Études de cas
  • Savoir comment
    • Blog
    • Rencontres
    • Webinaires
    • Ressources
Carrières Prendre contact
  • A propos de nous
  • Services
    • Développement de logiciels
      • Développement frontal
      • Développement backend
    • Staff Augmentation
      • Développeurs frontaux
      • Développeurs backend
      • Ingénieurs des données
      • Ingénieurs en informatique dématérialisée
      • Ingénieurs AQ
      • Autres
    • Conseil consultatif
      • Audit et conseil
  • Valeur pour
    • CEO
    • CTO
    • Gestionnaire des livraisons
  • Notre équipe
  • Études de cas
  • Savoir comment
    • Blog
    • Rencontres
    • Webinaires
    • Ressources
Carrières Prendre contact
Flèche arrière RETOUR
2019-07-02
Développement de logiciels

Principe d'ouverture-fermeture. Dois-je l'utiliser un jour ?

Mateusz Lesniak

La plupart des développeurs ont entendu parler du principe ouvert - fermé, l'un des principes SOLID de l'oncle Bob. Il semble raisonnable, mais il peut encore être un peu flou jusqu'à la première utilisation sur un code "vivant". L'énoncé complet du principe est le suivant : les entités logicielles (classes, modules, fonctions, etc.) doivent être ouvertes à l'extension, mais fermées à la modification.

Qu'est-ce que cela signifie vraiment ?

Nous avons rencontré un problème de développement qui nous a montré ce qu'est réellement le principe "ouvert-fermé". Dans l'une de nos applications web, nous avions un formulaire comportant deux sections (entre autres) :

  • canaux de la demande
  • filtres dynamiques

Les utilisateurs peuvent ajouter autant de filtres qu'ils le souhaitent, mais il existe certaines règles - la disponibilité des filtres dépend des canaux choisis.

Canaux de demande : ADÉCHANGE, EN-TÊTEENCHÈRES, RÉSERVATION, AUTRES Filtres dynamiques (dimensions) : site web, annonceunité, géo, créatiftaille, dispositif

Cet article traite principalement du refactoring de code, il y aura donc beaucoup d'extraits de code ci-dessous. J'ai essayé de les réduire, mais une certaine quantité de code est nécessaire pour montrer ce qui suit refonte du code. Il n'est pas nécessaire de comprendre chaque petite partie du code pour en saisir l'idée principale.

La première mise en œuvre du problème était simple :

class ResearchFormStateUpdater {
  update () {
    (...)
    this._updateDynamicFilters() ;
  }

  _updateDynamicFilters () {
    $('.dynamic-filter').each((_, filter) => {
      $(filter).trigger('dynamicFilter:disableWebsites', this._shouldDisableWebsitesFields()) ;
    }) ;
  }

  _shouldDisableWebsitesFields () {
    return this._shouldDisableFields(ResearchFormStateUpdater.WEBSITE_DISABLING_DEMAND_CHANNELS) ;
  }

  _shouldDisableFields (disablingDemandChannels) {
    // l'un des champs de disablingDemandChannels est-il coché ?
  }
}

ResearchFormStateUpdater.WEBSITE_DISABLING_DEMAND_CHANNELS = ['header_bidding', 'reservation', 'other'] ;

class ResearchDynamicFilter {
  _setDynamicFilterDisableWebsitesEvent () {
    $(this._getBody()).on('dynamicFilter:disableWebsites', (event, shouldDisableWebsites) => {
      // désactive les filtres de sites web
    }) ;
  }
}

Comme vous pouvez le voir, le filtre de site web est censé être indisponible pour HEADERLes canaux BIDDING, RESERVATION et AUTRES sont donc disponibles uniquement pour ADCanal d'échange.

La dernière chose que l'on puisse dire à propos d'un code est qu'il est permanent ou statique. Nous avons donc plus de demandes de la part de nos clients, ce qui rend ces classes plus grandes et plus complexes.

Développement des fonctionnalités

  • Ajouter un autre canal - EBDA (Le filtre du site web doit être indisponible pendant que l'EBDA est choisi) :

    • élargir DÉSACTIVATION DES CANAUX À LA DEMANDE par canal de demande EBDA
    • beaucoup de changements de noms - dans la première implémentation, nous avons spécifié le site web dans les noms des méthodes et des constantes. Dans la première implémentation, nous avons spécifié le site web dans les noms des méthodes et des constantes :

      • isWebsitesDimensionDisabled à _areFormStateDimensionsDisabled
      • SITE_DÉSACTIVATION_DES_CANAUX_DE_DEMANDE à DÉSACTIVATION DES CANAUX À LA DEMANDE

Alerte au spoiler -> lorsqu'un composant est ouvert aux modifications, il y aura beaucoup de changements de noms à l'avenir. Nous n'y prêterons pas attention dans les prochaines étapes.

  • Ajouter un autre filtre pour "Produit (Produit Le schéma de disponibilité des filtres est le même que celui du site web)

    • RechercheDynamiqueFiltre la classe doit vérifier la présence d'une dimension supplémentaire lors de la désactivation ou de l'activation des champs
  • Allons plus loin et ajoutons un sélecteur au-dessus des canaux -> "Source". Tous les canaux à la demande dont nous disposions jusqu'à présent se trouvent dans la source Ad Manager. La nouvelle source - SSP - n'a pas de canaux à la demande et le seul filtre disponible est le site web.

    • Règles :

      • Il existe deux états de la source : Gestionnaire de publicité, SSP.
      • Tous nos canaux de demande sont disponibles uniquement pour la source Ad Manager.
      • Il n'y a pas de canaux de demande pour la source SSP
      • Site web" est le seul filtre disponible pour la source SSP.
    • Mise en œuvre :

      • Lorsque l'option "SSP" est choisie :

        • Désactiver les canaux à la demande.
        • déclencher 'dynamicFilter:disableWebsitesAndProducts' (filtre dynamique : désactivation des sites web et des produits) <- activer les deux
        • déclencher 'dynamicFilter:disableNonSspOptions' (filtre dynamique)
      • Lorsque le gestionnaire de publicité est vérifié :

        • déclencher 'dynamicFilter:disableWebsitesAndProducts' (filtre dynamique : désactivation des sites web et des produits) <- vérifier si la météo est activée ou désactivée
  • Ajouter un autre filtre pour "Plateforme

    • Règles :

      • La plate-forme n'est disponible que lorsque la source est SSP.
    • Difficulté :

      • Nous avons maintenant "Site web", qui est disponible pour le canal AD_EXCHANGE du gestionnaire de publicités et pour le SSP, et nous avons "Plateforme" qui est disponible pour le SSP mais pas pour le gestionnaire de publicités.
      • Basculement l'état du formulaire peut s'avérer très délicat et confus

Mise en œuvre d'une nouvelle fonctionnalité :

Je vous présente l'extrait suivant principalement pour montrer la complexité du code. N'hésitez pas à le laisser caché.

class ResearchFormStateUpdater {
  update () {
    (...)
    this._triggerCallbacks() ;
  }

  _triggerCallbacks () {
    // choisir les rappels en fonction de la source
  }

  _adManagerSourceCallbacks () {
    (...)
    this._enableDemandChannels(ResearchFormStateUpdater.AD_MANAGER_DEMAND_CHANNELS) ;
    this._updateDefaultStateOfDynamicFilters() ;
    this._updateAdManagerDynamicFilters() ;
  }

  _sspSourceCallbacks () {
    (...)
    this._removeDemandChannelsActiveClassAndDisable(ResearchFormStateUpdater.AD_MANAGER_DEMAND_CHANNELS) ;
    this._updateDefaultStateOfDynamicFilters() ;
  }

  _updateDefaultStateOfDynamicFilters () {
    $('.dynamic-filter').each((_, filter) => {
      $(filter).trigger('dynamicFilter:enableSspFilters', this.isSourceSsp) ;
    }) ;
  }

  _updateAdManagerDynamicFilters () {
    $('.dynamic-filter').each((_, filter) => {
      $(filter).trigger('dynamicFilter:disableWebsitesAndProducts', this._areFormStateDimensionsDisabled() && !this.isSourceSsp) ;
    }) ;
  }

  _shouldDisableFields (disablingDemandChannels) {
    // si l'un des champs de disablingDemandChannels est vérifié
  }
}

ResearchFormStateUpdater.AD_MANAGER_DISABLING_DEMAND_CHANNELS = ['header_bidding', 'reservation', 'other', 'ebda'] ;

class ResearchDynamicFilter {
  // Je n'ai pas simplifié ces deux méthodes pour montrer la complexité de l'implémentation actuelle.

  _setDefaultDynamicFiltersToggleEvent () {
    $(this._getBody()).on('dynamicFilter:enableSspFilters', (event, shouldEnableSspOptions)) => {
      this._setDefaultFiltersOptionDisabledState(shouldEnableSspOptions) ;

      const selectedFilterDimension = this._getFiltersDimension().find('option:selected').val() ;
      if (selectedFilterDimension === 'website') {
        this._toggleChosenFilterDisabledState(false) ;
      } else if (selectedFilterDimension === 'platform') {
        this._toggleChosenFilterDisabledState(!shouldEnableSspOptions) ;
      } else {
        this._toggleChosenFilterDisabledState(shouldEnableSspOptions) ;
      }
    }) ;
  }

  _setDynamicFilterDisableWebsitesAndProductsEvent () {
    $(this._getBody()).on('dynamicFilter:disableWebsitesAndProducts', (event, shouldDisableWebsitesAndProducts) => {
      const selectedFilterDimension = this._getFiltersDimension().find('option:selected').val() ;
      if ($.inArray(selectedFilterDimension, ['website', 'product']) >= 0) {
        this._toggleChosenFilterDisabledState(shouldDisableWebsitesAndProducts) ;
      }
      this._setMethodSelectWebsiteAndProductOptionDisabledState(shouldDisableWebsitesAndProducts) ;
    }) ;
  }

  _toggleNonSspFilters (dimensionSelect, shouldDisable) {
    $.each(ResearchDynamicFilter.NON_SSP_FILTERS_OPTIONS, (_, option) => {
      // basculer l'état du filtre en fonction de 'shouldDisable'
    }) ;
  }
}

ResearchDynamicFilter.NON_SSP_FILTERS_OPTIONS = ['ad_unit', 'creative_size', 'geo', 'device', 'product'] ;

Nous utilisons encore certains 'toggle' (bascule) mécanisme. Il est vraiment difficile d'actionner 4 leviers et d'obtenir l'état attendu, et maintenant DynamicFilter doit savoir quelles sont les dimensions qui ne sont pas pour la source ssp.

Nous avons ResearchFormStateUpdater, pourquoi ne serait-il pas responsable ?

Demande finale

Ajouter un filtre supplémentaire pour "Partenaire de rendement".

C'est à ce moment précis que nous avons décidé de remanier ces classes. Les canaux et les filtres analysés ne sont qu'une petite partie du problème. Il y a plusieurs sections de formulaire ici et toutes ont le même problème. Notre refactorisation devrait neutraliser le besoin de modifier les méthodes internes de ces classes *pour* ajouter de nouveaux canaux ou dimensions.

Dans l'extrait suivant, j'ai laissé les classes principales presque telles qu'elles sont dans notre code de production pour vous montrer à quel point elles sont faciles à comprendre maintenant.

class ResearchFormStateUpdater {
  update () {
    (...)
    this._updateDynamicFilters() ;
  }

  _updateDynamicFilters () {
    this._toggleAllDynamicFiltersState(this._dynamicFiltersDimensionsToBeDisabled()) ;
  }

  _dynamicFiltersDimensionsToBeDisabled () {
    if (this.isSourceSsp) { return ResearchFormStateUpdater.NO_SSP_FILTERS ; }

    var disabledFilters = ResearchFormStateUpdater.ONLY_SSP_FILTERS ;
    if (this.areDemandChannelsExceptAdxSelected) {
      disabledFilters = disabledFilters.concat(ResearchFormStateUpdater.ONLY_ADX_FILTERS) ;
    }
    return disabledFilters ;
  }

  _toggleAllDynamicFiltersState (disabledFilters) {
    $('.dynamic-filter').each((_, filter) => {
      this._toggleDynamicFilterState(filter, disabledFilters) ;
    }) ;
  }

  _toggleDynamicFilterState (dynamicFilter, disabledFilters) {
    $(dynamicFilter).trigger('dynamicFilter:toggleDynamicFilters', disabledFilters) ;
  }
}

ResearchFormStateUpdater.NO_SSP_FILTERS = ['ad_unit', 'creative_size', 'geo', 'device', 'product'] ;

ResearchFormStateUpdater.ONLY_SSP_FILTERS = ['platform'] ;

ResearchFormStateUpdater.ONLY_ADX_FILTERS = ['website', 'product'] ;

class ResearchDynamicFilter {
  _setDynamicFiltersToggleEvent () {
    $(this._getBody()).on('dynamicFilter:toggleDynamicFilters', (event, disabledFilters) => {
      this._disableFilters(disabledFilters.split(',')) ;
      this._enableFilters(disabledFilters.split(','))) ;
    }) ;
  }

  _disableFilters (filtersToDisable) {
    // désactive les filtresToDisable
  }

  _enableFilters (filtersToDisable) {
    const filtersToEnable = $(ResearchDynamicFilter.ALL_FILTERS).not(filtersToDisable).get() ;
    // activer les filtresToEnable
  }
}

ResearchDynamicFilter.ALL_FILTERS = ['website', 'ad_unit', 'creative_size', 'geo', 'device', 'product', 'platform'] ;

Nous avons réussi ! On l'a fait ?

Désormais, la seule chose que "ResearchDynamicFilter" doit connaître est la liste de tous les filtres, ce qui semble équitable. Le reste de la logique et du contrôle vient d'en haut - quelques méthodes et constantes supérieures.

Essayons donc notre nouvelle structure en ajoutant un filtre pour "Yield_partner" :

class ResearchFormStateUpdater {
  _dynamicFiltersDimensionsToBeDisabled () {
    (...)
    if (this.areDemandChannelsExceptEbdaSelected) {
      disabledFilters = disabledFilters.concat(ResearchFormStateUpdater.ONLY_EBDA_FILTERS) ;
    }
    return disabledFilters ;
  }
}

ResearchFormStateUpdater.NO_SSP_FILTERS = [(...), 'yield_partner'] ;

ResearchFormStateUpdater.ONLY_EBDA_FILTERS = [(...), 'yield_partner'] ;

ResearchDynamicFilter.ALL_FILTERS = [(...), 'yield_partner'] ;

Comme vous pouvez le constater, il s'agit d'ajouter des valeurs à des constantes et des conditions supplémentaires.

Grâce au principe "ouvert-fermé", nous sommes en mesure de modifier la logique commerciale du formulaire en ajoutant simplement quelques valeurs et conditions à un niveau d'abstraction plus élevé. Nous n'avons pas besoin d'aller à l'intérieur du composant et de changer quoi que ce soit. Ce remaniement a affecté l'ensemble du formulaire et il y avait d'autres sections qui obéissent toutes au principe de l'ouverture-fermeture.

Nous n'avons pas réduit la quantité de code - en fait, nous l'avons même augmentée (avant/après) :

  • ResearchFormStateUpdater - 211/282 lignes
  • RechercheDynamiqueFiltre - 267/256 lignes

Il s'agit d'une collection de constantes -> c'est notre interface publique maintenant, notre console pour contrôler le processus sans des dizaines de commutateurs.

Lire aussi :

  • Comment écrire un code de qualité ?
  • Vuelendar. Un nouveau projet de Codest basé sur Vue.js
  • Qu'est-ce que Ruby on Jets et comment construire une application en l'utilisant ?

Articles connexes

Développement de logiciels

Construire des applications web à l'épreuve du temps : les conseils de l'équipe d'experts de The Codest

Découvrez comment The Codest excelle dans la création d'applications web évolutives et interactives à l'aide de technologies de pointe, offrant une expérience utilisateur transparente sur toutes les plateformes. Découvrez comment notre expertise favorise la transformation numérique et la...

LE CODEST
Développement de logiciels

Les 10 premières entreprises de développement de logiciels basées en Lettonie

Découvrez les principales sociétés de développement de logiciels en Lettonie et leurs solutions innovantes dans notre dernier article. Découvrez comment ces leaders de la technologie peuvent vous aider à développer votre entreprise.

thecodest
Solutions pour les entreprises et les grandes entreprises

L'essentiel du développement de logiciels Java : Un guide pour une externalisation réussie

Explorez ce guide essentiel sur le développement réussi de logiciels Java outsourcing pour améliorer l'efficacité, accéder à l'expertise et assurer la réussite des projets avec The Codest.

thecodest
Développement de logiciels

Le guide ultime de l'externalisation en Pologne

L'essor de outsourcing en Pologne est dû aux progrès économiques, éducatifs et technologiques, qui favorisent la croissance des technologies de l'information et un climat propice aux entreprises.

TheCodest
Solutions pour les entreprises et les grandes entreprises

Le guide complet des outils et techniques d'audit informatique

Les audits informatiques garantissent la sécurité, l'efficacité et la conformité des systèmes. Pour en savoir plus sur leur importance, lisez l'article complet.

The Codest
Jakub Jakubowicz CTO & Co-Fondateur

Abonnez-vous à notre base de connaissances et restez au courant de l'expertise du secteur des technologies de l'information.

    A propos de nous

    The Codest - Entreprise internationale de développement de logiciels avec des centres technologiques en Pologne.

    Royaume-Uni - Siège

    • Bureau 303B, 182-184 High Street North E6 2JA
      Londres, Angleterre

    Pologne - Les pôles technologiques locaux

    • Parc de bureaux Fabryczna, Aleja
      Pokoju 18, 31-564 Kraków
    • Brain Embassy, Konstruktorska
      11, 02-673 Varsovie, Pologne

      The Codest

    • Accueil
    • A propos de nous
    • Services
    • Études de cas
    • Savoir comment
    • Carrières
    • Dictionnaire

      Services

    • Conseil consultatif
    • Développement de logiciels
    • Développement backend
    • Développement frontal
    • Staff Augmentation
    • Développeurs backend
    • Ingénieurs en informatique dématérialisée
    • Ingénieurs des données
    • Autres
    • Ingénieurs AQ

      Ressources

    • Faits et mythes concernant la coopération avec un partenaire externe de développement de logiciels
    • Des États-Unis à l'Europe : Pourquoi les startups américaines décident-elles de se délocaliser en Europe ?
    • Comparaison des pôles de développement Tech Offshore : Tech Offshore Europe (Pologne), ASEAN (Philippines), Eurasie (Turquie)
    • Quels sont les principaux défis des CTO et des DSI ?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • Conditions d'utilisation du site web

    Copyright © 2025 par The Codest. Tous droits réservés.

    fr_FRFrench
    en_USEnglish de_DEGerman sv_SESwedish da_DKDanish nb_NONorwegian fiFinnish pl_PLPolish arArabic it_ITItalian jaJapanese ko_KRKorean es_ESSpanish nl_NLDutch etEstonian elGreek fr_FRFrench