window.pipedriveLeadboosterConfig = { base: 'leadbooster-chat.pipedrive.com', companyId: 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', versión: 2, } ;(function () { var w = window if (w.LeadBooster) { console.warn('LeadBooster ya existe') } 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 abierto-cerrado. ¿Tengo que utilizarlo alguna vez? - The Codest
The Codest
  • Quiénes somos
  • Servicios
    • Desarrollo de software
      • Desarrollo Frontend
      • Desarrollo backend
    • Staff Augmentation
      • Desarrolladores frontales
      • Desarrolladores de backend
      • Ingenieros de datos
      • Ingenieros de la nube
      • Ingenieros de control de calidad
      • Otros
    • Asesoramiento
      • Auditoría y consultoría
  • Industrias
    • Fintech y Banca
    • E-commerce
    • Adtech
    • Tecnología sanitaria
    • Fabricación
    • Logística
    • Automoción
    • IOT
  • Valor para
    • CEO
    • CTO
    • Gestor de entregas
  • Nuestro equipo
  • Case Studies
  • Saber cómo
    • Blog
    • Meetups
    • Seminarios en línea
    • Recursos
Carreras profesionales Póngase en contacto
  • Quiénes somos
  • Servicios
    • Desarrollo de software
      • Desarrollo Frontend
      • Desarrollo backend
    • Staff Augmentation
      • Desarrolladores frontales
      • Desarrolladores de backend
      • Ingenieros de datos
      • Ingenieros de la nube
      • Ingenieros de control de calidad
      • Otros
    • Asesoramiento
      • Auditoría y consultoría
  • Valor para
    • CEO
    • CTO
    • Gestor de entregas
  • Nuestro equipo
  • Case Studies
  • Saber cómo
    • Blog
    • Meetups
    • Seminarios en línea
    • Recursos
Carreras profesionales Póngase en contacto
Flecha atrás VOLVER
2019-07-02
Desarrollo de software

Principio abierto-cerrado. ¿Tengo que usarlo alguna vez?

Mateusz Lesniak

La mayoría de los desarrolladores han oído hablar del principio abierto-cerrado, uno de los principios SOLID del tío Bob. Suena razonable, pero puede ser un poco confuso hasta el primer uso en código "vivo". El estado completo del principio es: las entidades de software (clases, módulos, funciones, etc.) deben estar abiertas a la extensión, pero cerradas a la modificación.

¿Qué significa realmente?

Nos hemos encontrado con un problema de desarrollo que nos ha mostrado en qué consiste realmente el principio de abierto-cerrado. En una de nuestras aplicaciones web teníamos un formulario con dos secciones (entre otras):

  • canales de demanda
  • filtros dinámicos

Los usuarios pueden añadir tantos filtros como deseen, pero hay algunas reglas: la disponibilidad de los filtros depende de los canales elegidos.

Canales de demanda: ADINTERCAMBIO, CABECERAPUJA, RESERVA, OTROS Filtros dinámicos: sitio web, anunciounidad, geo, creativotamaño, dispositivo

Este artículo es sobre todo acerca de refactorización de código, por lo que habrá una gran cantidad de fragmentos de código a continuación. Traté de reducirlo, pero una cierta cantidad de código es necesario para mostrar refactorización del código. No es necesario entender cada pequeña parte del código para captar la idea principal.

La primera aplicación del problema fue sencilla:

clase ResearchFormStateUpdater {
  update () {
    (...)
    this._updateDynamicFilters();
  }

  _actualizarFiltrosDinámicos () {
    $('.dynamic-filter').each((_, filter) => {
      $(filter).trigger('dynamicFilter:disableWebsites', this._shouldDisableWebsitesFields());
    });
  }

  _shouldDisableWebsitesFields () {
    return this._shouldDisableFields(ResearchFormStateUpdater.WEBSITE_DISABLING_DEMAND_CHANNELS);
  }

  _shouldDisableFields (disablingDemandChannels) {
    // ¿se ha marcado disablingDemandChannels ?
  }
}

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

clase ResearchDynamicFilter {
  _setDynamicFilterDisableWebsitesEvent () {
    $(this._getBody()).on('dynamicFilter:disableWebsites', (event, shouldDisableWebsites) => {
      // desactivar filtros de sitios web
    });
  }
}

Como puede ver, se supone que el filtro del sitio web no está disponible para HEADERPUJA, RESERVA y OTROS canales, por lo que sólo está disponible para ADCanal de INTERCAMBIO.

Lo último que se puede decir del código es que es permanente o estático. Así que tenemos más peticiones de nuestro cliente haciendo estas clases más grandes y más complejas.

Desarrollo de funciones

  • Añadir otro canal - EBDA (El filtro del sitio web no debe estar disponible mientras se elija EBDA):

    • ampliar DESACTIVAR_CANALES_DEMANDA por canal de demanda EBDA
    • mucho cambio de nombre - en la primera implementación, especificamos sitio web en los nombres de métodos y constantes. Por ejemplo:

      • isWebsitesDimensionDisabled a _areFormStateDimensionsDisabled
      • DESACTIVAR_CANALES_DEMANDA_WEB a DESACTIVAR_CANALES_DEMANDA

Spoiler alert -> cuando un componente está abierto a cambios, habrá muchos cambios de nombre en el futuro. No prestaremos atención a esto en los próximos pasos.

  • Añadir otro filtro para "Producto (Producto el esquema de disponibilidad del filtro es el mismo que el del sitio web)

    • FiltroDinámicoDeInvestigación la clase tiene que comprobar una dimensión más al desactivar/activar campos
  • Vamos a ir más grande y añadir un poco de conmutador por encima de los canales -> 'Fuente'. Todos los canales de demanda que teníamos hasta ahora están en la fuente Ad Manager. La nueva fuente - SSP - no tiene canales de demanda y el único filtro disponible es sitio web.

    • Reglas:

      • Hay dos estados de fuente: Ad Manager, SSP.
      • Todos nuestros canales de demanda están disponibles sólo para la fuente Ad Manager.
      • No hay canales de demanda para la fuente SSP
      • Sitio web" es el único filtro disponible para la fuente SSP.
    • Implantación:

      • Cuando se elige "SSP":

        • Desactiva los canales de demanda.
        • desencadenar dynamicFilter:disableWebsitesAndProducts' <- activar ambos
        • desencadenar dynamicFilter:disableNonSspOptions
      • Cuando se comprueba el Administrador de anuncios:

        • desencadenar dynamicFilter:disableWebsitesAndProducts' <- comprobar el tiempo activado o desactivado
  • Añadir otro filtro para "Plataforma

    • Reglas:

      • La plataforma sólo está disponible cuando la fuente es SSP
    • Dificultad:

      • Ahora tenemos 'Website', que está disponible para el canal AD_EXCHANGE del Ad Manager y para Ssp y tenemos 'Platform' que está disponible para Ssp pero no para Ad Manager
      • Conmutación el estado del formulario puede ser realmente complicado y confuso

Implementación con nuevas funcionalidades:

Le presento el siguiente fragmento principalmente para mostrar la complejidad del código. Siéntase libre de dejarlo oculto.

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

  _triggerCallbacks () {
    // elegir callbacks dependiendo de la fuente
  }

  _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 comprueba cualquiera de disablingDemandChannels
  }
}

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

clase ResearchDynamicFilter {
  // No he simplificado el cuerpo de estos dos métodos para mostrar la complejidad de la implementación actual

  _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 === 'plataforma') {
        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) => {
      // cambiar el estado del filtro en función de 'shouldDisable
    });
  }
}

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

Todavía utilizamos algunos alternar mecanismo. Es realmente difícil cambiar 4 palancas y llegar al estado esperado y ahora DynamicFilter tiene que saber, qué dimensiones no son para la fuente ssp.

Tenemos ResearchFormStateUpdater, ¿por qué no debería estar a cargo?

Petición final

Añadir otro filtro para "Socio de rendimiento

Ese es el momento exacto en el que decidimos refactorizar esas clases. Los canales y filtros analizados son sólo una pequeña parte del problema. Hay múltiples secciones de formularios aquí y todas ellas tienen el mismo problema. Nuestra refactorización debería neutralizar la necesidad de cambiar métodos internos de esas clases *para* añadir nuevos canales o dimensiones.

En el siguiente fragmento, he dejado las clases principales casi como están en nuestro código de producción para mostrarte lo fáciles de entender que son ahora.

clase ResearchFormStateUpdater {
  update () {
    (...)
    this._updateDynamicFilters();
  }

  actualizarFiltrosDinámicos () {
    this._toggleAllDynamicFiltersState(this._dynamicFiltersDimensionsToBeDisabled());
  }

  Si se desactiva la función _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(filtro, disabledFilters);
    });
  }

  _toggleDynamicFilterState (dynamicFilter, disabledFilters) {
    $(dynamicFilter).trigger('dynamicFilter:toggleDynamicFilters', disabledFilters);
  }
}

ResearchFormStateUpdater.NO_SSP_FILTERS = ['ad_unit', 'creative_size', 'geo', 'device', 'product'];

ResearchFormStateUpdater.ONLY_SSP_FILTERS = ['plataforma'];

ResearchFormStateUpdater.ONLY_ADX_FILTERS = ['sitio web', 'producto'];

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

  _disableFilters (filtersToDisable) {
    //deshabilitarfiltrosParaDeshabilitar
  }

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

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

Lo hemos conseguido. ¿Lo logramos?

Ahora lo único que 'ResearchDynamicFilter' tiene que saber es una lista de todos los filtros - parece justo. El resto de la lógica y el control viene de arriba - algunos métodos superiores y constantes.

Probemos nuestra nueva estructura añadiendo un filtro para "Yield_partner":

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

Como puedes ver, se trata de añadir algunos valores a las constantes y algunas condiciones adicionales.

Gracias al "principio abierto-cerrado" podemos cambiar la lógica de negocio del formulario con sólo añadir algunos valores y condiciones en un nivel superior de abstracción. No necesitamos entrar en el componente y cambiar nada. Esta refactorización afectó a todo el formulario y había más secciones y ahora todas obedecen al principio abierto-cerrado.

No hemos reducido la cantidad de código; de hecho, incluso la hemos aumentado (antes/después):

  • ActualizadorInvestigaciónFormStateUpdater - 211/282 líneas
  • FiltroDinámicoDeInvestigación - 267/256 líneas

Se trata de la colección en constantes -> es nuestra interfaz pública ahora, nuestra consola para controlar el proceso sin decenas de conmutadores.

Lea también:

  • ¿Cómo escribir un código bueno y de calidad?
  • Vuelendar. Un nuevo proyecto de Codest basado en Vue.js
  • ¿Qué es Ruby on Jets y cómo crear una aplicación con él?

Artículos relacionados

Desarrollo de software

Crear aplicaciones web preparadas para el futuro: ideas del equipo de expertos de The Codest

Descubra cómo The Codest destaca en la creación de aplicaciones web escalables e interactivas con tecnologías de vanguardia, ofreciendo experiencias de usuario fluidas en todas las plataformas. Descubra cómo nuestra experiencia impulsa la transformación...

EL MEJOR
Desarrollo de software

Las 10 mejores empresas de desarrollo de software de Letonia

Conozca las principales empresas de desarrollo de software de Letonia y sus innovadoras soluciones en nuestro último artículo. Descubra cómo estos líderes tecnológicos pueden ayudarle a mejorar su negocio.

thecodest
Soluciones para empresas y escalas

Fundamentos del desarrollo de software Java: Guía para externalizar con éxito

Explore esta guía esencial sobre el desarrollo de software Java outsourcing con éxito para mejorar la eficiencia, acceder a la experiencia e impulsar el éxito de los proyectos con The Codest.

thecodest
Desarrollo de software

La guía definitiva para subcontratar en Polonia

El auge de las outsourcing en Polonia está impulsado por los avances económicos, educativos y tecnológicos, que fomentan el crecimiento de las TI y un clima favorable a las empresas.

TheCodest
Soluciones para empresas y escalas

Guía completa de herramientas y técnicas de auditoría informática

Las auditorías informáticas garantizan sistemas seguros, eficientes y conformes. Obtenga más información sobre su importancia leyendo el artículo completo.

The Codest
Jakub Jakubowicz CTO y Cofundador

Suscríbase a nuestra base de conocimientos y manténgase al día de la experiencia del sector informático.

    Quiénes somos

    The Codest - Empresa internacional de desarrollo de software con centros tecnológicos en Polonia.

    Reino Unido - Sede central

    • Oficina 303B, 182-184 High Street North E6 2JA
      Londres, Inglaterra

    Polonia - Centros tecnológicos locales

    • Parque de oficinas Fabryczna, Aleja
      Pokoju 18, 31-564 Cracovia
    • Embajada del Cerebro, Konstruktorska
      11, 02-673 Varsovia, Polonia

      The Codest

    • Inicio
    • Quiénes somos
    • Servicios
    • Case Studies
    • Saber cómo
    • Carreras profesionales
    • Diccionario

      Servicios

    • Asesoramiento
    • Desarrollo de software
    • Desarrollo backend
    • Desarrollo Frontend
    • Staff Augmentation
    • Desarrolladores de backend
    • Ingenieros de la nube
    • Ingenieros de datos
    • Otros
    • Ingenieros de control de calidad

      Recursos

    • Hechos y mitos sobre la cooperación con un socio externo de desarrollo de software
    • De EE.UU. a Europa: ¿Por qué las startups estadounidenses deciden trasladarse a Europa?
    • Comparación de los polos de desarrollo de Tech Offshore: Tech Offshore Europa (Polonia), ASEAN (Filipinas), Eurasia (Turquía)
    • ¿Cuáles son los principales retos de los CTO y los CIO?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • Condiciones de uso del sitio web

    Copyright © 2025 por The Codest. Todos los derechos reservados.

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