window.pipedriveLeadboosterConfig = { base: 'leadbooster-chat.pipedrive.com', companyId: 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', versjon: 2, } ;(function () { var w = vindu if (w.LeadBooster) { console.warn('LeadBooster finnes 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 }) }, } } })() Åpen-lukket-prinsippet. Må jeg noen gang bruke det? - The Codest
The Codest
  • Om oss
  • Tjenester
    • Programvareutvikling
      • Frontend-utvikling
      • Backend-utvikling
    • Staff Augmentation
      • Frontend-utviklere
      • Backend-utviklere
      • Dataingeniører
      • Ingeniører i skyen
      • QA-ingeniører
      • Annet
    • Det rådgivende
      • Revisjon og rådgivning
  • Industrier
    • Fintech og bankvirksomhet
    • E-commerce
    • Adtech
    • Helseteknologi
    • Produksjon
    • Logistikk
    • Bilindustrien
    • IOT
  • Verdi for
    • ADMINISTRERENDE DIREKTØR
    • CTO
    • Leveransesjef
  • Vårt team
  • Casestudier
  • Vet hvordan
    • Blogg
    • Møter
    • Webinarer
    • Ressurser
Karriere Ta kontakt med oss
  • Om oss
  • Tjenester
    • Programvareutvikling
      • Frontend-utvikling
      • Backend-utvikling
    • Staff Augmentation
      • Frontend-utviklere
      • Backend-utviklere
      • Dataingeniører
      • Ingeniører i skyen
      • QA-ingeniører
      • Annet
    • Det rådgivende
      • Revisjon og rådgivning
  • Verdi for
    • ADMINISTRERENDE DIREKTØR
    • CTO
    • Leveransesjef
  • Vårt team
  • Casestudier
  • Vet hvordan
    • Blogg
    • Møter
    • Webinarer
    • Ressurser
Karriere Ta kontakt med oss
Pil tilbake GÅ TILBAKE
2019-07-02
Programvareutvikling

Åpen-lukket-prinsippet. Må jeg noen gang bruke det?

Mateusz Lesniak

De fleste utviklere har hørt om åpen-lukket-prinsippet - et av onkel Bobs SOLID-prinsipper. Det høres fornuftig ut, men det kan likevel være litt uklart før det brukes for første gang på "live" kode. Prinsippets fulle tilstand er: programvareenheter (klasser, moduler, funksjoner osv.) skal være åpne for utvidelse, men lukket for endring.

Så hva betyr det egentlig?

Vi kom over et utviklingsproblem som har vist oss hva et åpent-lukket prinsipp egentlig handler om. I en av webapplikasjonene våre hadde vi blant annet et skjema med to seksjoner:

  • etterspørselskanaler
  • dynamiske filtre

Brukerne kan legge til så mange filtre de ønsker, men det finnes noen regler - filtertilgjengeligheten avhenger av hvilke kanaler som er valgt.

Etterspørselskanaler: ADUTVEKSLING, OVERSKRIFTBUD, RESERVASJON, ANNET Dynamiske filtre(dimensjoner): nettsted, annonseenhet, geo, kreativstørrelse, enhet

Denne artikkelen handler for det meste om refaktorering av kode, så det vil være mange kodesnutter nedenfor. Jeg har forsøkt å redusere det, men en viss mengde kode er nødvendig for å vise refaktorisering av kode. Du trenger ikke å forstå hver eneste lille del av koden for å få med deg hovedideen.

Den første implementeringen av problemet var enkel:

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) {
    // er noen av disablingDemandChannels merket av?
  }
}

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

class ResearchDynamicFilter {
  _setDynamicFilterDisableWebsitesEvent () {
    $(this._getBody()).on('dynamicFilter:disableWebsites', (event, shouldDisableWebsites) => {
      // deaktiver nettstedfiltre
    });
  }
}

Som du kan se, skal nettstedsfilteret ikke være tilgjengelig for HEADERBIDDING, RESERVASJON og ANDRE kanaler, så den er kun tilgjengelig for ADUtvekslingskanal.

Det siste man kan si om kode, er at den er permanent eller statisk. Så vi har flere forespørsler fra kundene våre, noe som gjør disse klassene større og mer komplekse.

Utvikling av funksjoner

  • Legg til en ny kanal - EBDA (Nettstedsfilteret skal ikke være tilgjengelig mens EBDA er valgt):

    • utvide DEAKTIVERING_AV_ETTERSPØRSELSKANALER av EBDAs etterspørselskanal
    • mye navneendring - i den første implementasjonen spesifiserte vi nettstedet i navn på metoder og konstanter. For eksempel

      • isWebsitesDimensionDisabled Jeg er på vei tilareFormStateDimensionsDisabled
      • NETTSTED_DEAKTIVERING_AV_ETTERSPØRSELSKANALER til DEAKTIVERING_AV_ETTERSPØRSELSKANALER

Spoilervarsel -> når en komponent er åpen for endringer, vil det bli mange navneendringer i fremtiden. Vi vil ikke ta hensyn til dette i de neste trinnene.

  • Legg til et nytt filter for 'Produkt' (Produkt filtertilgjengelighetsordningen er den samme som på nettstedet)

    • ResearchDynamicFilter klassen må sjekke for en dimensjon til når du deaktiverer/aktiverer felt
  • La oss gå større og legge til noen brytere over kanaler -> 'Kilde'. Alle etterspørselskanaler vi hadde til nå, er i Ad Manager-kilden. Den nye kilden - SSP - har ingen etterspørselskanaler, og det eneste tilgjengelige filteret er nettstedet.

    • Regler:

      • Det finnes to kildestatuser: Ad Manager og SSP.
      • Alle våre etterspørselskanaler er kun tilgjengelige for Ad Manager-kilder.
      • Det finnes ingen etterspørselskanaler for SSP-kilder
      • "Nettsted" er det eneste filteret som er tilgjengelig for SSP-kilder.
    • Implementering:

      • Når "SSP" er valgt:

        • Deaktiver etterspørselskanaler.
        • utløser 'dynamicFilter:disableWebsitesAndProducts' <- aktiver begge deler
        • utløser 'dynamicFilter:disableNonSspOptions'
      • Når Ad Manager er merket av:

        • utløser 'dynamicFilter:disableWebsitesAndProducts' <- sjekk om været er aktivert eller deaktivert
  • Legg til et nytt filter for "Plattform

    • Regler:

      • Plattformen er bare tilgjengelig når kilden er SSP
    • Vanskelighetsgrad:

      • Nå har vi "Website", som er tilgjengelig for AD_EXCHANGE-kanalen fra Ad Manager og for Ssp, og vi har "Platform" som er tilgjengelig for Ssp, men ikke for Ad Manager
      • Veksling skjemaets tilstand kan bli veldig vanskelig og forvirrende

Implementering med ny funksjonalitet:

Jeg presenterer neste utdrag hovedsakelig for å vise kodekompleksiteten. La det gjerne være skjult.

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

  _triggerCallbacks () {
    // velg tilbakekallinger avhengig av kilde
  }

  _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 noen av disablingDemandChannels er merket av
  }
}

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

class ResearchDynamicFilter {
  // Jeg har ikke forenklet disse to metodene for å vise kompleksiteten i den nåværende implementasjonen

  _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) => {
      // bytt filterstatus avhengig av 'shouldDisable'
    });
  }
}

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

Vi bruker fortsatt noen 'toggle' mekanisme. Det er veldig vanskelig å bytte 4 spaker og komme til forventet tilstand, og nå må DynamicFilter vite hvilke dimensjoner som ikke er for ssp-kilde.

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

Endelig forespørsel

Legg til et nytt filter for 'Yield partner'

Det var akkurat da vi bestemte oss for å refaktorere disse klassene. Kanaler og filtre som analyseres, er bare en liten del av problemet. Det er flere skjemadeler her, og alle har det samme problemet. Vår refaktorering bør nøytralisere behovet for å endre metoder i disse klassene *for å* legge til noen nye kanaler eller dimensjoner.

I neste utdrag har jeg latt hovedklassene være nesten slik de er i produksjonskoden vår for å vise hvor enkle de er å forstå nå.

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) {
    // deaktivere filtreToDisable
  }

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

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

Vi klarte det! Gjorde vi det?

Nå er det eneste 'ResearchDynamicFilter' trenger å vite, en liste over alle filtrene - det virker rimelig. Resten av logikken og kontrollen kommer ovenfra - noen høyere metoder og konstanter.

La oss prøve ut den nye strukturen ved å legge til 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 ser, handler det om å legge til noen verdier i konstanter og noen tilleggsbetingelser.

Takket være "åpen-lukket-prinsippet" kan vi endre forretningslogikken i skjemaet ved bare å legge til noen verdier og betingelser på et høyere abstraksjonsnivå. Vi trenger ikke å gå inn i komponenten og endre noe som helst. Denne refaktoriseringen påvirket hele skjemaet, og det var flere seksjoner, og alle følger nå prinsippet om åpen-lukket.

Vi reduserte ikke mengden kode - faktisk økte vi den til og med (før/etter):

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

Alt handler om samlingen i konstanter -> det er vårt offentlige grensesnitt nå, vår konsoll for å kontrollere prosessen uten titalls brytere.

Les også:

  • Hvordan skriver man en god kode av høy kvalitet?
  • Vuelkalender. Et nytt Codest-prosjekt basert på Vue.js
  • Hva er Ruby on Jets, og hvordan bygger man en app ved hjelp av det?

Relaterte artikler

Programvareutvikling

Bygg fremtidssikre webapper: Innsikt fra The Codests ekspertteam

Oppdag hvordan The Codest utmerker seg når det gjelder å skape skalerbare, interaktive webapplikasjoner med banebrytende teknologi som gir sømløse brukeropplevelser på tvers av alle plattformer. Finn ut hvordan ekspertisen vår driver digital transformasjon og...

THECODEST
Programvareutvikling

Topp 10 Latvia-baserte programvareutviklingsselskaper

I vår nyeste artikkel kan du lese mer om Latvias beste programvareutviklingsselskaper og deres innovative løsninger. Oppdag hvordan disse teknologilederne kan bidra til å løfte virksomheten din.

thecodest
Løsninger for bedrifter og oppskalering

Grunnleggende om Java-programvareutvikling: En guide til vellykket outsourcing

Utforsk denne viktige veiledningen om vellykket outsourcing av Java-programvareutvikling for å øke effektiviteten, få tilgang til ekspertise og drive frem prosjektsuksess med The Codest.

thecodest
Programvareutvikling

Den ultimate guiden til outsourcing i Polen

Den kraftige økningen i outsourcing i Polen er drevet av økonomiske, utdanningsmessige og teknologiske fremskritt, noe som fremmer IT-vekst og et forretningsvennlig klima.

TheCodest
Løsninger for bedrifter og oppskalering

Den komplette guiden til verktøy og teknikker for IT-revisjon

IT-revisjoner sørger for sikre, effektive og kompatible systemer. Les hele artikkelen for å lære mer om viktigheten av dem.

The Codest
Jakub Jakubowicz CTO og medgrunnlegger

Abonner på vår kunnskapsbase og hold deg oppdatert på ekspertisen fra IT-sektoren.

    Om oss

    The Codest - Internasjonalt programvareutviklingsselskap med teknologisentre i Polen.

    Storbritannia - Hovedkvarter

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

    Polen - Lokale teknologisentre

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

      The Codest

    • Hjem
    • Om oss
    • Tjenester
    • Casestudier
    • Vet hvordan
    • Karriere
    • Ordbok

      Tjenester

    • Det rådgivende
    • Programvareutvikling
    • Backend-utvikling
    • Frontend-utvikling
    • Staff Augmentation
    • Backend-utviklere
    • Ingeniører i skyen
    • Dataingeniører
    • Annet
    • QA-ingeniører

      Ressurser

    • Fakta og myter om samarbeid med en ekstern programvareutviklingspartner
    • Fra USA til Europa: Hvorfor velger amerikanske oppstartsbedrifter å flytte til Europa?
    • Sammenligning av Tech Offshore Development Hubs: Tech Offshore Europa (Polen), ASEAN (Filippinene), Eurasia (Tyrkia)
    • Hva er de største utfordringene for CTO-er og CIO-er?
    • The Codest
    • The Codest
    • The Codest
    • Retningslinjer for personver
    • Vilkår for bruk av nettstedet

    Opphavsrett © 2025 av The Codest. Alle rettigheter forbeholdt.

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