window.pipedriveLeadboosterConfig = { base: 'leadbooster-chat.pipedrive.com', companyId: 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', version: 2, } ;(funktion () { var w = vindue if (w.LeadBooster) { console.warn('LeadBooster findes allerede') } 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 }) }, } } })() Åben-lukket-princippet. Får jeg nogensinde brug for det? - The Codest
Codest
  • Om os
  • Serviceydelser
    • Udvikling af software
      • Frontend-udvikling
      • Backend-udvikling
    • Staff Augmentation
      • Frontend-udviklere
      • Backend-udviklere
      • Dataingeniører
      • Cloud-ingeniører
      • QA-ingeniører
      • Andet
    • Det rådgivende
      • Revision og rådgivning
  • Industrier
    • Fintech og bankvirksomhed
    • E-commerce
    • Adtech
    • Sundhedsteknologi
    • Produktion
    • Logistik
    • Biler
    • IOT
  • Værdi for
    • ADMINISTRERENDE DIREKTØR
    • CTO
    • Leder af levering
  • Vores team
  • Casestudier
  • Ved hvordan
    • Blog
    • Møder
    • Webinarer
    • Ressourcer
Karriere Tag kontakt til os
  • Om os
  • Serviceydelser
    • Udvikling af software
      • Frontend-udvikling
      • Backend-udvikling
    • Staff Augmentation
      • Frontend-udviklere
      • Backend-udviklere
      • Dataingeniører
      • Cloud-ingeniører
      • QA-ingeniører
      • Andet
    • Det rådgivende
      • Revision og rådgivning
  • Værdi for
    • ADMINISTRERENDE DIREKTØR
    • CTO
    • Leder af levering
  • Vores team
  • Casestudier
  • Ved hvordan
    • Blog
    • Møder
    • Webinarer
    • Ressourcer
Karriere Tag kontakt til os
Pil tilbage GÅ TILBAGE
2019-07-02
Udvikling af software

Åben-lukket-princippet. Får jeg nogensinde brug for det?

Mateusz Lesniak

De fleste udviklere har hørt om åben-lukket-princippet - et af onkel Bobs SOLID-principper. Det lyder fornuftigt, men det kan stadig være lidt sløret, indtil det første gang bruges på "levende" kode. Princippet er: Softwareenheder (klasser, moduler, funktioner osv.) skal være åbne for udvidelser, men lukkede for ændringer.

Så hvad betyder det egentlig?

Vi er stødt på et udviklingsproblem, som har vist os, hvad et åbent-lukket princip egentlig handler om. I en af vores webapplikationer havde vi en formular med to sektioner (blandt andre):

  • efterspørgselskanaler
  • dynamiske filtre

Brugerne kan tilføje så mange filtre, som de ønsker, men der er nogle regler - tilgængeligheden af filtre afhænger af de valgte kanaler.

Efterspørgselskanaler: ADUDVEKSLING, HEADERBIDDING, RESERVATION, ANDET Dynamiske filtre(dimensioner): hjemmeside, annonceenhed, geo, kreativstørrelse, enhed

Denne artikel handler mest om refaktorering af kode, så der vil være mange kodestykker nedenfor. Jeg har forsøgt at reducere det, men en vis mængde kode er nødvendig for at vise refaktorering af kode. Du behøver ikke at forstå hver eneste lille del af koden for at forstå hovedidéen.

Den første implementering af problemet var enkel:

klasse 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) {
    // er nogen af disablingDemandChannels markeret?
  }
}

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

klasse ResearchDynamicFilter {
  _setDynamicFilterDisableWebsitesEvent () {
    $(this._getBody()).on('dynamicFilter:disableWebsites', (event, shouldDisableWebsites) => {
      // deaktiverer webstedsfiltre
    });
  }
}

Som du kan se, er det meningen, at webstedsfilteret ikke skal være tilgængeligt for HEADERBIDDING, RESERVATION og ANDRE kanaler, så den er kun tilgængelig for ADUdvekslingskanal.

Det sidste, man kan sige om kode, er, at den er permanent eller statisk. Så vi får flere anmodninger fra vores kunder, hvilket gør disse klasser større og mere komplekse.

Udvikling af funktioner

  • Tilføj en anden kanal - EBDA (Hjemmesidefilteret bør ikke være tilgængeligt, mens EBDA vælges):

    • udvide DEAKTIVERING AF EFTERSPØRGSELSKANALER af EBDA-efterspørgselskanal
    • en masse navneændringer - i den første implementering specificerede vi hjemmesiden i navne på metoder og konstanter. For eksempel:

      • isWebsitesDimensionDisabled Til...erFormStateDimensionsDisabled
      • WEBSITE_DEAKTIVERING_AF_EFTERSPØRGSELSKANALER til DEAKTIVERING AF EFTERSPØRGSELSKANALER

Spoiler alert -> når en komponent er åben for ændringer, vil der være mange navneændringer i fremtiden. Vi vil ikke være opmærksomme på dette i de næste trin.

  • Tilføj et nyt filter for 'Produkt' (Produkt filtertilgængelighedsordningen er den samme som på hjemmesiden)

    • ForskningDynamiskFilter Klassen skal tjekke for endnu en dimension, når den deaktiverer/aktiverer felter
  • Lad os gøre det større og tilføje en omskifter over kanaler -> 'Kilde'. Alle de efterspørgselskanaler, vi har haft indtil nu, er i Ad Manager-kilden. Den nye kilde - SSP - har ingen efterspørgselskanaler, og det eneste tilgængelige filter er hjemmeside.

    • Regler:

      • Der er to tilstande for kilden: Ad Manager, SSP.
      • Alle vores efterspørgselskanaler er kun tilgængelige for Ad Manager-kilder.
      • Der er ingen efterspørgselskanaler for SSP-kilder
      • 'Website' er det eneste tilgængelige filter for SSP-kilder.
    • Implementering:

      • Når 'SSP' er valgt:

        • Deaktiver efterspørgselskanaler.
        • udløser 'dynamicFilter:disableWebsitesAndProducts' <- aktiver begge dele
        • udløser 'dynamicFilter:disableNonSspOptions'
      • Når Ad Manager er markeret:

        • udløser 'dynamicFilter:disableWebsitesAndProducts' <- tjek vejret aktiveret eller deaktiveret
  • Tilføj et nyt filter til 'Platform'

    • Regler:

      • Platformen er kun tilgængelig, når kilden er SSP
    • Sværhedsgrad:

      • Nu har vi 'Website', som er tilgængelig for AD_EXCHANGE-kanalen fra Ad Manager og for Ssp, og vi har 'Platform', som er tilgængelig for Ssp, men ikke for Ad Manager.
      • Toggling Formularens tilstand kan blive virkelig vanskelig og forvirrende

Implementering med ny funktionalitet:

Jeg præsenterer det næste uddrag primært for at vise kodens kompleksitet. Du er velkommen til at lade det være skjult.

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

  _triggerCallbacks () {
    // vælg tilbagekald afhængigt af kilden
  }

  _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) {
    // hvis nogen af disablingDemandChannels er markeret
  }
}

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

klasse ResearchDynamicFilter {
  // Jeg har ikke forenklet de to metoder for at vise den nuværende implementeringskompleksitet

  _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) => {
      // skifter filtertilstand afhængigt af 'shouldDisable'
    });
  }
}

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

Vi bruger stadig nogle 'toggle' mekanisme. Det er virkelig svært at skifte 4 håndtag og komme til den forventede tilstand, og nu skal DynamicFilter vide, hvilke dimensioner der ikke er til ssp-kilde.

Vi har ResearchFormStateUpdater, så hvorfor skulle den ikke have ansvaret?

Endelig anmodning

Tilføj et nyt filter til 'Yield partner'

Det var præcis på det tidspunkt, vi besluttede at refaktorere disse klasser. Kanaler og filtre, der analyseres, er kun en lille del af problemet. Der er flere formularsektioner her, og de har alle det samme problem. Vores refaktorering burde neutralisere behovet for at ændre metoder inde i disse klasser *for at* tilføje nogle nye kanaler eller dimensioner.

I det næste uddrag har jeg ladet hovedklasserne være næsten, som de er i vores produktionskode, for at vise dig, hvor lette de er at forstå nu.

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

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

  _disableFilters (filtersToDisable) {
    // deaktiverer filtreToDisable
  }

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

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

Vi gjorde det! Gjorde vi det?

Nu er det eneste, 'ResearchDynamicFilter' behøver at vide, en liste over alle filtre - det virker rimeligt. Resten af logikken og kontrollen kommer ovenfra - nogle højere metoder og konstanter.

Så lad os afprøve vores nye struktur ved at tilføje et filter for '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'];

Som du kan se, handler det om at tilføje nogle værdier til konstanter og nogle ekstra betingelser.

Takket være "åben-lukket-princippet" kan vi ændre formularens forretningslogik ved blot at tilføje nogle værdier og betingelser på et højere abstraktionsniveau. Vi behøver ikke at gå ind i komponenten og ændre noget. Denne refaktorering påvirkede hele formularen, og der var flere sektioner, og de overholder alle princippet om åben-lukkethed nu.

Vi reducerede ikke mængden af kode - faktisk øgede vi den endda (før/efter):

  • ResearchFormStateUpdater - 211/282 linjer
  • ForskningDynamiskFilter - 267/256 linjer

Det hele handler om samlingen i konstanter -> det er vores offentlige grænseflade nu, vores konsol til at styre processen uden snesevis af omskiftere.

Læs også her:

  • Hvordan skriver man en god kvalitetskode?
  • Vuelendar. Et nyt Codest-projekt baseret på Vue.js
  • Hvad er Ruby on Jets, og hvordan bygger man en app med det?

Relaterede artikler

Udvikling af software

Byg fremtidssikrede webapps: Indsigt fra The Codest's ekspertteam

Oplev, hvordan The Codest udmærker sig ved at skabe skalerbare, interaktive webapplikationer med banebrydende teknologier, der leverer sømløse brugeroplevelser på tværs af alle platforme. Lær, hvordan vores ekspertise driver digital transformation og...

DENKODEST
Udvikling af software

Top 10 Letlands-baserede softwareudviklingsvirksomheder

Læs om Letlands bedste softwareudviklingsvirksomheder og deres innovative løsninger i vores seneste artikel. Find ud af, hvordan disse teknologiledere kan hjælpe med at løfte din virksomhed.

thecodest
Løsninger til virksomheder og scaleups

Grundlæggende om Java-softwareudvikling: En guide til succesfuld outsourcing

Udforsk denne vigtige guide til vellykket outsourcing af Java-softwareudvikling for at forbedre effektiviteten, få adgang til ekspertise og skabe projektsucces med The Codest.

thecodest
Udvikling af software

Den ultimative guide til outsourcing i Polen

Den voldsomme stigning i outsourcing i Polen er drevet af økonomiske, uddannelsesmæssige og teknologiske fremskridt, der fremmer it-vækst og et erhvervsvenligt klima.

TheCodest
Løsninger til virksomheder og scaleups

Den komplette guide til IT-revisionsværktøjer og -teknikker

IT-revisioner sikrer sikre, effektive og kompatible systemer. Lær mere om deres betydning ved at læse hele artiklen.

Codest
Jakub Jakubowicz CTO og medstifter

Tilmeld dig vores vidensbase, og hold dig opdateret om ekspertisen fra it-sektoren.

    Om os

    The Codest - International softwareudviklingsvirksomhed med tech-hubs i Polen.

    Storbritannien - Hovedkvarter

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

    Polen - Lokale teknologiske knudepunkter

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

      Codest

    • Hjem
    • Om os
    • Serviceydelser
    • Casestudier
    • Ved hvordan
    • Karriere
    • Ordbog

      Serviceydelser

    • Det rådgivende
    • Udvikling af software
    • Backend-udvikling
    • Frontend-udvikling
    • Staff Augmentation
    • Backend-udviklere
    • Cloud-ingeniører
    • Dataingeniører
    • Andet
    • QA-ingeniører

      Ressourcer

    • Fakta og myter om at samarbejde med en ekstern softwareudviklingspartner
    • Fra USA til Europa: Hvorfor beslutter amerikanske startups sig for at flytte til Europa?
    • Sammenligning af Tech Offshore-udviklingsknudepunkter: Tech Offshore Europa (Polen), ASEAN (Filippinerne), Eurasien (Tyrkiet)
    • Hvad er de største udfordringer for CTO'er og CIO'er?
    • Codest
    • Codest
    • Codest
    • Privacy policy
    • Vilkår for brug af hjemmesiden

    Copyright © 2025 af The Codest. Alle rettigheder forbeholdes.

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