window.pipedriveLeadboosterConfig = { bas: 'leadbooster-chat.pipedrive.com', företagId: 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', version: 2, } ;(funktion () { var w = fönster if (w.LeadBooster) { console.warn('LeadBooster finns redan') } annars { w.LeadBooster = { q: [], on: funktion (n, h) { this.q.push({ t: "o", n: n, h: h }) }, trigger: funktion (n) { this.q.push({ t: 't', n: n }) }, } } })() Principen om öppet och stängt. Kommer jag någonsin att behöva använda den? - The Codest
Codest
  • Om oss
  • Tjänster
    • Utveckling av programvara
      • Frontend-utveckling
      • Backend-utveckling
    • Staff Augmentation
      • Frontend-utvecklare
      • Backend-utvecklare
      • Dataingenjörer
      • Ingenjörer inom molntjänster
      • QA-ingenjörer
      • Övriga
    • Det rådgivande
      • Revision och rådgivning
  • Industrier
    • Fintech & bankverksamhet
    • E-commerce
    • Adtech
    • Hälsoteknik
    • Tillverkning
    • Logistik
    • Fordon
    • IOT
  • Värde för
    • VD OCH KONCERNCHEF
    • CTO
    • Leveranschef
  • Vårt team
  • Fallstudier
  • Vet hur
    • Blogg
    • Möten
    • Webbinarier
    • Resurser
Karriär Ta kontakt med oss
  • Om oss
  • Tjänster
    • Utveckling av programvara
      • Frontend-utveckling
      • Backend-utveckling
    • Staff Augmentation
      • Frontend-utvecklare
      • Backend-utvecklare
      • Dataingenjörer
      • Ingenjörer inom molntjänster
      • QA-ingenjörer
      • Övriga
    • Det rådgivande
      • Revision och rådgivning
  • Värde för
    • VD OCH KONCERNCHEF
    • CTO
    • Leveranschef
  • Vårt team
  • Fallstudier
  • Vet hur
    • Blogg
    • Möten
    • Webbinarier
    • Resurser
Karriär Ta kontakt med oss
Pil tillbaka GÅ TILLBAKA
2019-07-02
Utveckling av programvara

Principen om öppet och stängt. Måste jag någonsin använda den?

Mateusz Lesniak

De flesta utvecklare har hört talas om principen öppen - stängd - en av farbror Bobs SOLID-principer. Det låter rimligt, men det kan fortfarande vara lite suddigt fram till den första användningen på "levande" kod. Principen i sin helhet är: programvaruenheter (klasser, moduler, funktioner etc.) ska vara öppna för utbyggnad, men stängda för modifiering.

Så vad betyder det egentligen?

Vi har stött på ett utvecklingsproblem som har visat oss vad principen om öppet och slutet egentligen handlar om. I en av våra webbapplikationer hade vi ett formulär med två sektioner (bland andra):

  • efterfrågekanaler
  • dynamiska filter

Användare kan lägga till så många filter de vill, men det finns vissa regler - filtertillgänglighet beror på valda kanaler.

Kanaler för efterfrågan: ADUTBYTE, RUBRIKBUDGIVNING, RESERVERING, ÖVRIGT Dynamiska filter(dimensioner): webbplats, annonsenhet, geo, kreativstorlek, enhet

Den här artikeln handlar mest om kodrefaktorering, så det kommer att finnas många kodsnuttar nedan. Jag försökte minska det, men en viss mängd kod är nödvändig för att visa refaktorisering av kod. Du behöver inte förstå varje liten del av koden för att förstå huvudtanken.

Den första implementeringen av problemet var enkel:

klass 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) {
    // är någon av disablingDemandChannels markerad ?
  }
}

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

klass ResearchDynamicFilter {
  _setDynamicFilterDisableWebsitesEvent () {
    $(this._getBody()).on('dynamicFilter:disableWebsites', (event, shouldDisableWebsites) => {
      // inaktivera webbplatsfilter
    });
  }
}

Som du kan se ska webbplatsfiltret inte vara tillgängligt för HEADERBIDDING, RESERVATION och ÖVRIGA kanaler, så den är endast tillgänglig för ADUtbyteskanal.

Det sista man kan säga om kod är att den är permanent eller statisk. Så vi har fler förfrågningar från våra kunder som gör dessa klasser större och mer komplexa.

Utveckling av funktioner

  • Lägg till ytterligare en kanal - EBDA (Webbplatsfiltret bör inte vara tillgängligt medan EBDA väljs):

    • expandera INAKTIVERING_AV_KRAV_KANALER genom EBDA:s efterfrågekanal
    • en hel del namnändringar - i den första implementeringen angav vi webbplats i metoder och konstanters namn. Till exempel

      • isWebsitesDimensionDisabled för attareFormStateDimensionsDisabled
      • WEBBPLATS_AVAKTIVERING_AV_EFTERFRÅGEKANALER till INAKTIVERING_AV_KRAV_KANALER

Spoilervarning -> när en komponent är öppen för ändringar kommer det att ske en hel del namnändringar i framtiden. Vi kommer inte att uppmärksamma detta i nästa steg.

  • Lägg till ett nytt filter för "Product (Produkt filtertillgängligheten är densamma som på webbplatsen)

    • ForskningDynamisktFilter klassen måste kontrollera ytterligare en dimension när fält inaktiveras/aktiveras
  • Låt oss gå större och lägga till några växlare ovanför kanaler -> "Källa". Alla efterfrågekanaler som vi har haft fram till nu finns i Ad Manager-källan. Den nya källan - SSP - har inga efterfrågekanaler och det enda tillgängliga filtret är webbplats.

    • Regler:

      • Det finns två tillstånd för källan: Annonshanterare, SSP.
      • Alla våra kanaler för efterfrågan är endast tillgängliga för Ad Manager-källor.
      • Det finns inga kanaler för efterfrågan på SSP-källa
      • "Website" är det enda tillgängliga filtret för SSP-källan.
    • Genomförande:

      • När "SSP" väljs:

        • Avaktivera kanaler för efterfrågan.
        • avtryckare 'dynamisktFilter:inaktiveraWebbplatserOchProdukter' <- aktivera båda
        • avtryckare 'dynamicFilter:disableNonSspOptions'
      • När Ad Manager kontrollerade:

        • avtryckare 'dynamisktFilter:inaktiveraWebbplatserOchProdukter' <- kontrollera väder aktiverat eller inaktiverat
  • Lägg till ett nytt filter för "Plattform

    • Regler:

      • Plattformen är endast tillgänglig när källan är SSP
    • Svårighetsgrad:

      • Nu har vi "Website", som är tillgänglig för AD_EXCHANGE-kanalen från Ad Manager och för Ssp och vi har "Platform" som är tillgänglig för Ssp men inte för Ad Manager
      • Växling formulärets status kan bli riktigt knepig och förvirrande

Implementering med ny funktionalitet:

Jag presenterar nästa kodsnutt främst för att visa kodens komplexitet. Känn dig fri att lämna den dold.

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

  _triggerCallbacks () {
    // välj återuppringningar beroende på källa
  }

  _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) {
    // om något av disablingDemandChannels är markerat
  }
}

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

klass ResearchDynamicFilter {
  // Jag förenklade inte dessa två metoder för att visa den nuvarande implementeringens komplexitet

  _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 === 'plattform') {
        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) => {
      // växla filterstatus beroende på 'shouldDisable'
    });
  }
}

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

Vi använder fortfarande vissa 'växla' mekanism. Det är verkligen svårt att växla 4 spakar och komma till förväntat tillstånd och nu måste DynamicFilter veta vilka dimensioner som inte är för ssp-källa.

Vi har ResearchFormStateUpdater, varför skulle den inte vara ansvarig?

Sista begäran

Lägg till ett nytt filter för 'Yield partner'

Det var precis då vi bestämde oss för att göra om dessa klasser. Kanaler och filter som analyseras är bara en liten del av problemet. Det finns flera formulärsektioner här och alla har samma problem. Vår refaktor bör neutralisera behovet av att ändra metoder inuti dessa klasser * för att * lägga till några nya kanaler eller dimensioner.

I nästa snutt har jag lämnat huvudklasserna nästan som de är i vår produktionskod för att visa hur lättförståeliga de är nu.

klass 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 = ['plattform'];

ResearchFormStateUpdater.ONLY_ADX_FILTERS = ['webbplats', 'produkt'];

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

  _disableFilters (filterToDisable) {
    // inaktivera filterToDisable
  }

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

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

Vi klarade det! Gjorde vi?

Nu är det enda som "ResearchDynamicFilter" behöver veta en lista över alla filter - det verkar rimligt. Resten av logiken och kontrollen kommer från ovan - några högre metoder och konstanter.

Så låt oss prova vår nya struktur genom att lägga till ett filter för "Yield_partner":

klass 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'];

Som du kan se handlar det om att lägga till några värden till konstanter och några ytterligare villkor.

Tack vare "open-closed-principen" kan vi ändra formulärets affärslogik genom att bara lägga till några värden och villkor på en högre abstraktionsnivå. Vi behöver inte gå in i komponenten och ändra någonting. Denna refaktor påverkade hela formuläret och det fanns fler sektioner och de följer alla principen om öppen slutenhet nu.

Vi minskade inte mängden kod - faktum är att vi till och med ökade den (före/efter):

  • ForskningFormStateUpdater - 211/282 linjer
  • ForskningDynamisktFilter - 267/256 linjer

Det handlar om samlingen i konstanter -> det är vårt offentliga gränssnitt nu, vår konsol för att styra processen utan tiotals omkopplare.

Läs också:

  • Hur skriver man en bra kod av hög kvalitet?
  • Vuelkalender. Ett nytt Codest-projekt baserat på Vue.js
  • Vad är Ruby on Jets och hur bygger man en app med hjälp av det?

Relaterade artiklar

Utveckling av programvara

Bygg framtidssäkrade webbappar: Insikter från The Codest:s expertteam

Upptäck hur The Codest utmärker sig genom att skapa skalbara, interaktiva webbapplikationer med banbrytande teknik som ger sömlösa användarupplevelser på alla plattformar. Läs om hur vår expertis driver digital omvandling och affärsutveckling...

DEKODEST
Utveckling av programvara

Topp 10 Lettlandsbaserade mjukvaruutvecklingsföretag

Läs mer om Lettlands främsta mjukvaruutvecklingsföretag och deras innovativa lösningar i vår senaste artikel. Upptäck hur dessa teknikledare kan hjälpa till att lyfta ditt företag.

thecodest
Lösningar för företag och uppskalningsföretag

Java Software Development Essentials: En guide till framgångsrik outsourcing

Utforska denna viktiga guide om framgångsrik outsourcing av Java-programvaruutveckling för att förbättra effektiviteten, få tillgång till expertis och driva projektframgång med The Codest.

thecodest
Utveckling av programvara

Den ultimata guiden till outsourcing i Polen

Den kraftiga ökningen av outsourcing i Polen drivs av ekonomiska, utbildningsmässiga och tekniska framsteg, vilket främjar IT-tillväxt och ett företagsvänligt klimat.

TheCodest
Lösningar för företag och uppskalningsföretag

Den kompletta guiden till verktyg och tekniker för IT-revision

IT-revisioner säkerställer säkra, effektiva och kompatibla system. Läs mer om hur viktiga de är genom att läsa hela artikeln.

Codest
Jakub Jakubowicz CTO och medgrundare

Prenumerera på vår kunskapsbas och håll dig uppdaterad om expertisen från IT-sektorn.

    Om oss

    The Codest - Internationellt mjukvaruutvecklingsföretag med teknikhubbar i Polen.

    Förenade kungariket - Huvudkontor

    • Kontor 303B, 182-184 High Street North E6 2JA
      London, England

    Polen - Lokala tekniknav

    • Fabryczna Office Park, Aleja
      Pokoju 18, 31-564 Kraków
    • Brain Embassy, Konstruktorska
      11, 02-673 Warszawa, Polen

      Codest

    • Hem
    • Om oss
    • Tjänster
    • Fallstudier
    • Vet hur
    • Karriär
    • Ordbok

      Tjänster

    • Det rådgivande
    • Utveckling av programvara
    • Backend-utveckling
    • Frontend-utveckling
    • Staff Augmentation
    • Backend-utvecklare
    • Ingenjörer inom molntjänster
    • Dataingenjörer
    • Övriga
    • QA-ingenjörer

      Resurser

    • Fakta och myter om att samarbeta med en extern partner för mjukvaruutveckling
    • Från USA till Europa: Varför väljer amerikanska startup-företag att flytta till Europa?
    • Jämförelse av Tech Offshore Development Hubs: Tech Offshore Europa (Polen), ASEAN (Filippinerna), Eurasien (Turkiet)
    • Vilka är de största utmaningarna för CTO:er och CIO:er?
    • Codest
    • Codest
    • Codest
    • Privacy policy
    • Användarvillkor för webbplatsen

    Copyright © 2025 av The Codest. Alla rättigheter reserverade.

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