window.pipedriveLeadboosterConfig = { base: 'leadbooster-chat.pipedrive.com', companyId: 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', version: 2, } ;(function () { var w = finestra if (w.LeadBooster) { console.warn('LeadBooster esiste già') } 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 }) }, } } })() Principio di apertura-chiusura. Devo mai usarlo? - The Codest
The Codest
  • Chi siamo
  • Servizi
    • Sviluppo di software
      • Sviluppo Frontend
      • Sviluppo backend
    • Staff Augmentation
      • Sviluppatori Frontend
      • Sviluppatori backend
      • Ingegneri dei dati
      • Ingegneri del cloud
      • Ingegneri QA
      • Altro
    • Consulenza
      • Audit e consulenza
  • Industrie
    • Fintech e banche
    • E-commerce
    • Adtech
    • Tecnologia della salute
    • Produzione
    • Logistica
    • Automotive
    • IOT
  • Valore per
    • CEO
    • CTO
    • Responsabile della consegna
  • Il nostro team
  • Case Studies
  • Sapere come
    • Blog
    • Incontri
    • Webinar
    • Risorse
Carriera Contattate
  • Chi siamo
  • Servizi
    • Sviluppo di software
      • Sviluppo Frontend
      • Sviluppo backend
    • Staff Augmentation
      • Sviluppatori Frontend
      • Sviluppatori backend
      • Ingegneri dei dati
      • Ingegneri del cloud
      • Ingegneri QA
      • Altro
    • Consulenza
      • Audit e consulenza
  • Valore per
    • CEO
    • CTO
    • Responsabile della consegna
  • Il nostro team
  • Case Studies
  • Sapere come
    • Blog
    • Incontri
    • Webinar
    • Risorse
Carriera Contattate
Freccia indietro TORNA INDIETRO
2019-07-02
Sviluppo di software

Principio di apertura-chiusura. Devo mai usarlo?

Mateusz Lesniak

La maggior parte degli sviluppatori ha sentito parlare del principio aperto - chiuso, uno dei principi SOLID dello zio Bob. Sembra ragionevole, ma può essere ancora un po' confuso fino al primo utilizzo su codice "vivo". Lo stato completo del principio è: le entità software (classi, moduli, funzioni, ecc.) dovrebbero essere aperte per l'estensione, ma chiuse per la modifica.

Che cosa significa in realtà?

Ci siamo imbattuti in un problema di sviluppo che ci ha mostrato il vero significato del principio "open-closed". In una delle nostre applicazioni web avevamo un modulo con due sezioni (tra le altre):

  • canali di domanda
  • filtri dinamici

Gli utenti possono aggiungere tutti i filtri che desiderano, ma ci sono alcune regole: la disponibilità dei filtri dipende dai canali scelti.

Canali di domanda: ADSCAMBIO, INTESTAZIONEOFFERTA, PRENOTAZIONE, ALTRO Filtri dinamici (dimensioni): sito web, annunciounità, geo, creativodimensione, dispositivo

Questo articolo riguarda principalmente la rifattorizzazione del codice, quindi ci saranno molti snippet di codice qui sotto. Ho cercato di ridurli, ma una certa quantità di codice è necessaria per mostrare rifattorizzazione del codice. Non è necessario comprendere ogni piccola parte del codice per capire l'idea principale.

La prima implementazione del problema era semplice:

classe 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) {
    // è stato controllato uno dei canali di disabilitazione della domanda?
  }
}

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

class ResearchDynamicFilter {
  _setDynamicFilterDisableWebsitesEvent () {
    $(this._getBody()).on('dynamicFilter:disableWebsites', (event, shouldDisableWebsites) => {
      // disabilita i filtri dei siti web
    });
  }
}

Come si può vedere, si suppone che il filtro del sito web non sia disponibile per HEADEROFFERTE, PRENOTAZIONI e ALTRI canali, quindi è disponibile solo per ADCanale di scambio.

L'ultima cosa che si può dire del codice è che è permanente o statico. Quindi abbiamo più richieste da parte dei nostri clienti, che rendono queste classi più grandi e più complesse.

Sviluppo di funzionalità

  • Aggiungi un altro canale - EBDA (Il filtro del sito web non deve essere disponibile mentre si sceglie EBDA):

    • espandere DISABILITAZIONE DEI CANALI A RICHIESTA per canale di domanda EBDA
    • un sacco di modifiche ai nomi: nella prima implementazione, abbiamo specificato il sito web nei nomi dei metodi e delle costanti. Ad esempio:

      • isWebsitesDimensionDisabled a _areFormStateDimensionsDisabled
      • SITO WEB_DISABILITAZIONE_CANALI_DEMANDATI a DISABILITAZIONE DEI CANALI A RICHIESTA

Attenzione: quando un componente è aperto alle modifiche, in futuro ci saranno molti cambi di nome. Non presteremo attenzione a questo nei prossimi passi.

  • Aggiungere un altro filtro per 'Prodotto' (Prodotto Lo schema di disponibilità dei filtri è lo stesso del sito web)

    • RicercaFiltro Dinamico La classe deve verificare la presenza di un'ulteriore dimensione durante la disattivazione/abilitazione dei campi
  • Andiamo più in grande e aggiungiamo uno switcher sopra i canali -> 'Sorgente'. Tutti i canali di richiesta che avevamo finora sono nella fonte Ad Manager. La nuova fonte - SSP - non ha canali di richiesta e l'unico filtro disponibile è quello del sito web.

    • Regole:

      • Esistono due stati di origine: Ad Manager, SSP.
      • Tutti i nostri canali di richiesta sono disponibili solo per la fonte Ad Manager.
      • Non esistono canali di domanda per la fonte SSP
      • 'Sito web' è l'unico filtro disponibile per la sorgente SSP.
    • Implementazione:

      • Quando si sceglie "SSP":

        • Disattivare i canali di richiesta.
        • innesco dynamicFilter:disableWebsitesAndProducts' <- abilita entrambi
        • innesco filtro dinamico:disableNonSspOptions
      • Quando Ad Manager è stato controllato:

        • innesco dynamicFilter:disableWebsitesAndProducts' <- controllare il tempo abilitato o disabilitato
  • Aggiungere un altro filtro per "Piattaforma".

    • Regole:

      • La piattaforma è disponibile solo quando la sorgente è SSP.
    • Difficoltà:

      • Ora abbiamo 'Sito web', che è disponibile per il canale AD_EXCHANGE di Ad Manager e per Ssp e abbiamo 'Piattaforma' che è disponibile per Ssp ma non per Ad Manager.
      • Alterazione lo stato del modulo può diventare davvero complicato e confuso

Implementazione con nuove funzionalità:

Vi presento il prossimo snippet principalmente per mostrare la complessità del codice. Sentitevi liberi di lasciarlo nascosto.

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

  _triggerCallbacks () {
    // sceglie i callback a seconda della sorgente
  }

  _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) {
    // se uno qualsiasi di disablingDemandChannels è controllato
  }
}

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

class ResearchDynamicFilter {
  // Non ho semplificato questi due metodi per mostrare l'attuale complessità dell'implementazione.

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

      const selectedFilterDimension = this._getFiltersDimension().find('option:selected').val();
      se (selectedFilterDimension === 'sito web') {
        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) => {
      // altera lo stato del filtro in base a "shouldDisable".
    });
  }
}

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

Utilizziamo ancora alcuni 'toggle' (alza la levetta) meccanismo. È davvero difficile cambiare 4 leve e raggiungere lo stato previsto e ora DynamicFilter deve sapere quali dimensioni non sono per la sorgente ssp.

Abbiamo ResearchFormStateUpdater, perché non dovrebbe essere responsabile?

Richiesta finale

Aggiungi un altro filtro per "Partner di rendimento".

Questo è il momento esatto in cui abbiamo deciso di rifattorizzare queste classi. I canali e i filtri analizzati sono solo una piccola parte del problema. Ci sono diverse sezioni di form qui e tutte hanno lo stesso problema. Il nostro refactor dovrebbe neutralizzare la necessità di modificare i metodi interni di queste classi *per* aggiungere nuovi canali o dimensioni.

Nel prossimo snippet, ho lasciato le classi principali quasi come sono nel nostro codice di produzione, per mostrare quanto siano facili da capire ora.

classe 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 = ['sito web', 'prodotto'];

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

  _disableFilters (filtersToDisable) {
    // disabilita filtriDaDisabilitare
  }

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

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

Ce l'abbiamo fatta! Ce l'abbiamo fatta?

Ora l'unica cosa che 'ResearchDynamicFilter' deve sapere è l'elenco di tutti i filtri: mi sembra giusto. Il resto della logica e del controllo viene dall'alto: alcuni metodi e costanti superiori.

Proviamo quindi la nostra nuova struttura aggiungendo un filtro per "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'];

Come si può vedere, si tratta di aggiungere alcuni valori alle costanti e alcune condizioni aggiuntive.

Grazie al principio "aperto-chiuso", siamo in grado di modificare la logica di business del modulo aggiungendo solo alcuni valori e condizioni a un livello di astrazione superiore. Non abbiamo bisogno di entrare nel componente e modificare nulla. Questa rifattorizzazione ha interessato l'intero form e ci sono state altre sezioni che ora rispettano tutte il principio di apertura-chiusura.

Non abbiamo ridotto la quantità di codice, anzi l'abbiamo addirittura aumentata (prima/dopo):

  • Aggiornatore dello stato dei moduli di ricerca - 211/282 linee
  • RicercaFiltro Dinamico - 267/256 linee

Si tratta di una collezione di costanti -> è la nostra interfaccia pubblica ora, la nostra console per controllare il processo senza decine di interruttori.

Leggi anche:

  • Come scrivere un codice di qualità?
  • Vuelendar. Un nuovo progetto di Codest basato su Vue.js
  • Che cos'è Ruby on Jets e come si costruisce un'applicazione utilizzandolo?

Articoli correlati

Sviluppo di software

Costruire applicazioni web a prova di futuro: le intuizioni del team di esperti di The Codest

Scoprite come The Codest eccelle nella creazione di applicazioni web scalabili e interattive con tecnologie all'avanguardia, offrendo esperienze utente senza soluzione di continuità su tutte le piattaforme. Scoprite come la nostra esperienza favorisce la trasformazione digitale e il business...

IL CANCRO
Sviluppo di software

Le 10 principali aziende di sviluppo software con sede in Lettonia

Scoprite le migliori aziende di sviluppo software della Lettonia e le loro soluzioni innovative nel nostro ultimo articolo. Scoprite come questi leader tecnologici possono aiutarvi a migliorare la vostra attività.

thecodest
Soluzioni per aziende e scaleup

Essenziali di sviluppo software Java: Guida all'outsourcing di successo

Esplorate questa guida essenziale sullo sviluppo di software Java con successo outsourcing per migliorare l'efficienza, accedere alle competenze e guidare il successo del progetto con The Codest.

thecodest
Sviluppo di software

La guida definitiva all'outsourcing in Polonia

L'aumento di outsourcing in Polonia è guidato dai progressi economici, educativi e tecnologici, che favoriscono la crescita dell'IT e un clima favorevole alle imprese.

IlCodesto
Soluzioni per aziende e scaleup

Guida completa agli strumenti e alle tecniche di audit IT

Gli audit IT garantiscono sistemi sicuri, efficienti e conformi. Per saperne di più sulla loro importanza, leggete l'articolo completo.

The Codest
Jakub Jakubowicz CTO e cofondatore

Iscrivetevi alla nostra knowledge base e rimanete aggiornati sulle competenze del settore IT.

    Chi siamo

    The Codest - Società internazionale di sviluppo software con centri tecnologici in Polonia.

    Regno Unito - Sede centrale

    • Ufficio 303B, 182-184 High Street North E6 2JA
      Londra, Inghilterra

    Polonia - Poli tecnologici locali

    • Parco uffici Fabryczna, Aleja
      Pokoju 18, 31-564 Cracovia
    • Ambasciata del cervello, Konstruktorska
      11, 02-673 Varsavia, Polonia

      The Codest

    • Casa
    • Chi siamo
    • Servizi
    • Case Studies
    • Sapere come
    • Carriera
    • Dizionario

      Servizi

    • Consulenza
    • Sviluppo di software
    • Sviluppo backend
    • Sviluppo Frontend
    • Staff Augmentation
    • Sviluppatori backend
    • Ingegneri del cloud
    • Ingegneri dei dati
    • Altro
    • Ingegneri QA

      Risorse

    • Fatti e miti sulla collaborazione con un partner esterno per lo sviluppo di software
    • Dagli Stati Uniti all'Europa: Perché le startup americane decidono di trasferirsi in Europa
    • Confronto tra gli hub di sviluppo Tech Offshore: Tech Offshore Europa (Polonia), ASEAN (Filippine), Eurasia (Turchia)
    • Quali sono le principali sfide di CTO e CIO?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • Condizioni di utilizzo del sito web

    Copyright © 2025 di The Codest. Tutti i diritti riservati.

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