The Codest
  • O nás
  • Služby
    • Vývoj softwaru
      • Vývoj frontendů
      • Vývoj backendu
    • Staff Augmentation
      • Vývojáři frontendů
      • Vývojáři backendu
      • Datoví inženýři
      • Cloudoví inženýři
      • Inženýři QA
      • Další
    • To Advisory
      • Audit a poradenství
  • Odvětví
    • Fintech a bankovnictví
    • E-commerce
    • Adtech
    • Healthtech
    • Výroba
    • Logistika
    • Automobilový průmysl
    • IOT
  • Hodnota za
    • CEO
    • CTO
    • Manažer dodávek
  • Náš tým
  • Case Studies
  • Vědět jak
    • Blog
    • Setkání
    • Webové semináře
    • Zdroje
Kariéra Spojte se s námi
  • O nás
  • Služby
    • Vývoj softwaru
      • Vývoj frontendů
      • Vývoj backendu
    • Staff Augmentation
      • Vývojáři frontendů
      • Vývojáři backendu
      • Datoví inženýři
      • Cloudoví inženýři
      • Inženýři QA
      • Další
    • To Advisory
      • Audit a poradenství
  • Hodnota za
    • CEO
    • CTO
    • Manažer dodávek
  • Náš tým
  • Case Studies
  • Vědět jak
    • Blog
    • Setkání
    • Webové semináře
    • Zdroje
Kariéra Spojte se s námi
Šipka zpět ZPĚT
2019-07-02
Vývoj softwaru

Princip "otevřeno-uzavřeno". Musím ho někdy použít?

Mateusz Lesniak

Většina vývojářů už slyšela o principu otevřeného a uzavřeného přístupu - jednom z principů SOLID strýčka Boba. Zní to rozumně, ale až do prvního použití v "živém" kódu to může být trochu nejasné. Úplný stav principu zní: softwarové entity (třídy, moduly, funkce atd.) by měly být otevřené pro rozšíření, ale uzavřené pro modifikaci.

Co to vlastně znamená?

Narazili jsme na vývojový problém, který se projevil nás o čem princip otevřeného a uzavřeného systému skutečně je. V jedné z našich webových aplikací jsme měli formulář se dvěma sekcemi (mimo jiné):

  • poptávkové kanály
  • dynamické filtry

Uživatelé mohou přidávat libovolný počet filtrů, ale existují určitá pravidla - dostupnost filtrů závisí na zvolených kanálech.

Poptávkové kanály: ADVÝMĚNA, ZÁHLAVÍNABÍDKA, REZERVACE, OSTATNÍ Dynamické filtry(rozměry): webové stránky, reklamajednotka, geo, kreativnívelikost, zařízení

Tento článek je převážně o refaktorizaci kódu, takže níže bude uvedeno mnoho úryvků kódu. Snažil jsem se je zredukovat, ale určité množství kódu je nutné ukázat. refaktoring kódu. Nemusíte rozumět každé malé části kódu, abyste pochopili hlavní myšlenku.

První implementace problému byla jednoduchá:

třída 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 (vypnutíPoptávkovýchKanálů) {
    // je některý z disablingDemandChannels zaškrtnutý ?
  }
}

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

třída ResearchDynamicFilter {
  _setDynamicFilterDisableWebsitesEvent () {
    $(this._getBody()).on('dynamicFilter:disableWebsites', (event, shouldDisableWebsites) => {
      // vypnout filtry webových stránek
    });
  }
}

Jak vidíte, filtr webových stránek by měl být nedostupný pro HEADER.Nabídky, rezervace a ostatní kanály, takže je k dispozici pouze pro ADKanál EXCHANGE.

Poslední věc, kterou můžete o kódu říci, je, že je stálý nebo statický. Proto máme od klienta další požadavky, díky kterým jsou tyto třídy větší a složitější.

Vývoj funkcí

  • Přidat další kanál - EBDA (Filtr webových stránek by neměl být dostupný, pokud je vybrána služba EBDA):

    • rozšířit DISABLING_DEMAND_CHANNELS podle poptávkového kanálu EBDA
    • hodně změn názvů - v první implementaci jsme v názvech metod a konstant uvedli webové stránky. Například:

      • isWebsitesDimensionDisabled na _areFormStateDimensionsDisabled
      • WEB_DISABLING_DEMAND_CHANNELS na DISABLING_DEMAND_CHANNELS

Pozor, spoiler -> když je komponenta otevřená pro změny, bude se v budoucnu hodně měnit její název. V dalších krocích tomu nebudeme věnovat pozornost.

  • Přidání dalšího filtru pro položku "Produkt (Produkt schéma dostupnosti filtrů je stejné jako u webových stránek)

    • ResearchDynamicFilter třída musí při vypínání/povolování polí zkontrolovat ještě jeden rozměr.
  • Zvětšeme a přidáme nějaký přepínač nad kanály -> "Zdroj". Všechny kanály poptávky, které jsme dosud měli, jsou ve zdroji Správce reklam. Nový zdroj - SSP - nemá žádné poptávkové kanály a jediný dostupný filtr je webová stránka.

    • Pravidla:

      • Existují dva stavy zdroje: Ad Manager, SSP.
      • Všechny naše poptávkové kanály jsou k dispozici pouze pro zdroj Ad Manager.
      • Neexistují žádné poptávkové kanály pro zdroj SSP
      • "Webová stránka" je jediný filtr dostupný pro zdroj SSP.
    • Provádění:

      • Pokud je vybrána možnost "SSP":

        • Zakázat poptávkové kanály.
        • spouštěč 'dynamicFilter:disableWebsitesAndProducts' <- povolit obojí
        • spouštěč 'dynamicFilter:disableNonSspOptions'
      • Při kontrole Správce reklam:

        • spouštěč 'dynamicFilter:disableWebsitesAndProducts' <- kontrola počasí povolena nebo zakázána
  • Přidání dalšího filtru pro 'Platform'

    • Pravidla:

      • Platforma je k dispozici pouze v případě, že zdrojem je SSP.
    • Obtížnost:

      • Nyní máme "Web", který je k dispozici pro kanál AD_EXCHANGE ze Správce reklam a pro Ssp, a máme "Platform", který je k dispozici pro Ssp, ale ne pro Správce reklam.
      • Přepínání stav formuláře může být opravdu složitý a matoucí.

Implementace s novými funkcemi:

Další ukázku vám předkládám hlavně proto, abych ukázal složitost kódu. Klidně ho nechte skrytý.

třída ResearchFormStateUpdater {
  update () {
    (...)
    this._triggerCallbacks();
  }

  _triggerCallbacks () {
    // zvolte zpětná volání v závislosti na zdroji
  }

  _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) {
    // je zaškrtnuto některé z disablingDemandChannels
  }
}

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

třída ResearchDynamicFilter {
  // Tělo těchto dvou metod jsem nezjednodušil, abych ukázal současnou složitost implementace

  _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) => {
      // přepnutí stavu filtru v závislosti na 'shouldDisable'
    });
  }
}

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

Stále používáme některé 'toggle' mechanismus. Je opravdu těžké přepnout 4 páky a dostat se do očekávaného stavu a nyní musí DynamicFilter vědět, které rozměry nejsou pro zdroj ssp.

Máme ResearchFormStateUpdater, proč by to neměl mít na starosti?

Závěrečná žádost

Přidat další filtr pro "partner pro výnosy

To je přesně ten okamžik, kdy jsme se rozhodli tyto třídy refaktorovat. Analyzované kanály a filtry jsou jen malou částí problému. Je zde více částí formuláře a všechny mají stejný problém. Náš refaktor by měl neutralizovat potřebu měnit metody uvnitř těchto tříd *pro* přidání některých nových kanálů nebo dimenzí.

V dalším úryvku jsem ponechal hlavní třídy téměř tak, jak jsou v našem produkčním kódu, abych vám ukázal, jak jsou nyní snadno pochopitelné.

třída 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'];

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

  _disableFilters (filtersToDisable) {
    // disable filtersToDisable
  }

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

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

Dokázali jsme to! Opravdu?

Nyní musí 'ResearchDynamicFilter' znát pouze seznam všech filtrů - zdá se to být spravedlivé. Zbytek logiky a řízení pochází shora - některé vyšší metody a konstanty.

Vyzkoušejme tedy naši novou strukturu přidáním filtru pro "Yield_partner":

třída 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"];

Jak vidíte, jde o přidání některých hodnot ke konstantám a některých dalších podmínek.

Díky principu "otevřeno-uzavřeno" můžeme měnit obchodní logiku formuláře pouze přidáním některých hodnot a podmínek na vyšší úrovni abstrakce. Nemusíme vstupovat dovnitř komponenty a nic měnit. Tento refaktor se dotkl celého formuláře, přibylo více sekcí a všechny se nyní řídí principem open-closed.

Množství kódu jsme nesnížili - ve skutečnosti jsme ho dokonce zvýšili (před/po):

  • ResearchFormStateUpdater - 211/282 řádků
  • ResearchDynamicFilter - 267/256 řádků

Jde o kolekci v konstantách -> nyní je to naše veřejné rozhraní, naše konzola pro ovládání procesu bez desítek přepínačů.

Přečtěte si také:

  • Jak napsat dobrý a kvalitní kód?
  • Vuelendar. Nový projekt Codestu založený na Vue.js
  • Co je Ruby on Jets a jak pomocí něj vytvořit aplikaci?

Související články

Ilustrace zdravotnické aplikace pro chytré telefony s ikonou srdce a rostoucím zdravotním grafem, označená logem The Codest, která představuje digitální zdraví a řešení HealthTech.
Vývoj softwaru

Softwarové vybavení pro zdravotnictví: a případy použití

Nástroje, na které se dnes zdravotnické organizace spoléhají, se v ničem nepodobají papírovým kartám z doby před desítkami let. zdravotnický software dnes podporuje zdravotnické systémy, péči o pacienty a moderní poskytování zdravotní péče v klinických a...

NEJKRÁSNĚJŠÍ
Abstraktní ilustrace klesajícího sloupcového grafu se stoupající šipkou a zlatou mincí symbolizující efektivitu nákladů nebo úspory. V levém horním rohu se zobrazuje logo The Codest se sloganem "In Code We Trust" na světle šedém pozadí.
Vývoj softwaru

Jak rozšířit tým vývojářů bez ztráty kvality produktu

Zvětšujete svůj vývojový tým? Zjistěte, jak růst, aniž byste museli obětovat kvalitu produktu. Tento průvodce se zabývá příznaky, že je čas na škálování, strukturou týmu, najímáním zaměstnanců, vedením a nástroji - a také tím, jak může The Codest...

NEJKRÁSNĚJŠÍ
Vývoj softwaru

Vytváření webových aplikací odolných vůči budoucnosti: postřehy týmu odborníků The Codest

Zjistěte, jak společnost The Codest vyniká při vytváření škálovatelných, interaktivních webových aplikací pomocí nejmodernějších technologií, které poskytují bezproblémové uživatelské prostředí na všech platformách. Zjistěte, jak naše odborné znalosti podporují digitální transformaci a obchodní...

NEJKRÁSNĚJŠÍ
Vývoj softwaru

10 nejlepších lotyšských společností zabývajících se vývojem softwaru

V našem nejnovějším článku se dozvíte o nejlepších lotyšských společnostech zabývajících se vývojem softwaru a jejich inovativních řešeních. Zjistěte, jak mohou tito technologičtí lídři pomoci pozvednout vaše podnikání.

thecodest
Podniková a škálovací řešení

Základy vývoje softwaru v jazyce Java: A Guide to Outsourcing Successfully

Prozkoumejte tuto základní příručku o úspěšném vývoji softwaru outsourcing Java, abyste zvýšili efektivitu, získali přístup k odborným znalostem a dosáhli úspěchu projektu s The Codest.

thecodest

Přihlaste se k odběru naší znalostní databáze a získejte aktuální informace o odborných znalostech z oblasti IT.

    O nás

    The Codest - Mezinárodní společnost zabývající se vývojem softwaru s technologickými centry v Polsku.

    Spojené království - ústředí

    • Kancelář 303B, 182-184 High Street North E6 2JA
      Londýn, Anglie

    Polsko - Místní technologická centra

    • Kancelářský park Fabryczna, Aleja
      Pokoju 18, 31-564 Krakov
    • Brain Embassy, Konstruktorska
      11, 02-673 Varšava, Polsko

      The Codest

    • Home
    • O nás
    • Služby
    • Case Studies
    • Vědět jak
    • Kariéra
    • Slovník

      Služby

    • To Advisory
    • Vývoj softwaru
    • Vývoj backendu
    • Vývoj frontendů
    • Staff Augmentation
    • Vývojáři backendu
    • Cloudoví inženýři
    • Datoví inženýři
    • Další
    • Inženýři QA

      Zdroje

    • Fakta a mýty o spolupráci s externím partnerem pro vývoj softwaru
    • Z USA do Evropy: Proč se americké startupy rozhodly přesídlit do Evropy?
    • Srovnání technických vývojových center v zahraničí: Tech Offshore Evropa (Polsko), ASEAN (Filipíny), Eurasie (Turecko)
    • Jaké jsou hlavní výzvy CTO a CIO?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • Website terms of use

    Copyright © 2026 by The Codest. Všechna práva vyhrazena.

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