The Codest
  • Sobre nós
  • Serviços
    • Desenvolvimento de software
      • Desenvolvimento de front-end
      • Desenvolvimento backend
    • Staff Augmentation
      • Programadores Frontend
      • Programadores de back-end
      • Engenheiros de dados
      • Engenheiros de nuvem
      • Engenheiros de GQ
      • Outros
    • Aconselhamento
      • Auditoria e consultoria
  • Indústrias
    • Fintech e Banca
    • E-commerce
    • Adtech
    • Tecnologia da saúde
    • Fabrico
    • Logística
    • Automóvel
    • IOT
  • Valor para
    • CEO
    • CTO
    • Gestor de entregas
  • A nossa equipa
  • Case Studies
  • Saber como
    • Blogue
    • Encontros
    • Webinars
    • Recursos
Carreiras Entrar em contacto
  • Sobre nós
  • Serviços
    • Desenvolvimento de software
      • Desenvolvimento de front-end
      • Desenvolvimento backend
    • Staff Augmentation
      • Programadores Frontend
      • Programadores de back-end
      • Engenheiros de dados
      • Engenheiros de nuvem
      • Engenheiros de GQ
      • Outros
    • Aconselhamento
      • Auditoria e consultoria
  • Valor para
    • CEO
    • CTO
    • Gestor de entregas
  • A nossa equipa
  • Case Studies
  • Saber como
    • Blogue
    • Encontros
    • Webinars
    • Recursos
Carreiras Entrar em contacto
Seta para trás VOLTAR
2019-07-02
Desenvolvimento de software

Princípio aberto-fechado. Alguma vez terei de o usar?

Mateusz Lesniak

A maioria dos programadores já ouviu falar do princípio aberto - fechado - um dos princípios SOLID do Tio Bob. Parece razoável, mas ainda pode ser um pouco confuso até à primeira utilização em código "vivo". O estado completo do princípio é: as entidades de software (classes, módulos, funções, etc.) devem ser abertas para extensão, mas fechadas para modificação.

O que é que isso significa realmente?

Deparámo-nos com um problema de desenvolvimento que mostrou nós o que é, de facto, um princípio aberto-fechado. Numa das nossas aplicações Web, tínhamos um formulário com duas secções (entre outras):

  • canais de procura
  • filtros dinâmicos

Os utilizadores podem adicionar tantos filtros quantos desejarem, mas existem algumas regras - a disponibilidade dos filtros depende dos canais escolhidos.

Canais de procura: ADTROCA, CABEÇALHOLICITAÇÃO, RESERVA, OUTROS Filtros dinâmicos (dimensões): sítio Web, anúnciounidade, geo, criativotamanho, dispositivo

Este artigo é maioritariamente sobre refactorização de código, pelo que haverá muitos excertos de código abaixo. Tentei reduzi-los, mas é necessário algum código para mostrar refacção de código. Não é necessário compreender todas as pequenas partes do código para compreender a ideia principal.

A primeira implementação do problema era simples:

classe 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) {
    // algum dos disablingDemandChannels está selecionado?
  }
}

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

classe ResearchDynamicFilter {
  _setDynamicFilterDisableWebsitesEvent () {
    $(this._getBody()).on('dynamicFilter:disableWebsites', (event, shouldDisableWebsites) => {
      // desativar filtros de sítios Web
    });
  }
}

Como se pode ver, o filtro do sítio Web não deve estar disponível para HEADEROs canais LICITAÇÃO, RESERVA e OUTROS estão disponíveis apenas para os canais ADCanal EXCHANGE.

A última coisa que se pode dizer sobre o código é que ele é permanente ou estático. Assim, temos mais pedidos do nosso cliente, tornando estas classes maiores e mais complexas.

Desenvolvimento de funcionalidades

  • Adicionar outro canal - EBDA (O filtro do sítio Web deve estar indisponível enquanto o EBDA for escolhido):

    • expandir DESACTIVAR_CANAIS_DE_PEDIDO por canal de procura EBDA
    • muitas mudanças de nomes - na primeira implementação, especificámos o sítio Web nos nomes dos métodos e das constantes. Por exemplo:

      • isWebsitesDimensionDisabled para _areFormStateDimensionsDisabled
      • WEBSITE_DISABLING_DEMAND_CHANNELS para DESACTIVAR_CANAIS_DE_PEDIDO

Alerta de spoiler -> quando um componente está aberto a alterações, haverá muitas mudanças de nome no futuro. Não vamos prestar atenção a isto nos próximos passos.

  • Adicionar outro filtro para 'Produto' (Produto o esquema de disponibilidade do filtro é o mesmo do sítio Web)

    • ResearchDynamicFilter a classe tem de verificar se existe mais uma dimensão ao desativar/desativar campos
  • Vamos aumentar e adicionar um comutador acima dos canais -> 'Source'. Todos os canais de procura que tínhamos até agora estão na fonte do Ad Manager. A nova fonte - SSP - não tem canais de procura e o único filtro disponível é o website.

    • Regras:

      • Existem dois estados de origem: Ad Manager, SSP.
      • Todos os nossos canais de procura estão disponíveis apenas para a fonte Ad Manager.
      • Não existem canais de procura para a fonte SSP
      • 'Website' é o único filtro disponível para a fonte SSP.
    • Implementação:

      • Quando é selecionada a opção "SSP":

        • Desativar os canais de procura.
        • acionador 'dynamicFilter:disableWebsitesAndProducts' <- ativar ambos
        • acionador 'dynamicFilter:disableNonSspOptions'
      • Quando o Gestor de anúncios é verificado:

        • acionador 'dynamicFilter:disableWebsitesAndProducts' <- verificar se o tempo está ativado ou desativado
  • Adicionar outro filtro para "Plataforma

    • Regras:

      • A plataforma só está disponível quando a fonte é a SSP
    • Dificuldade:

      • Agora temos "Website", que está disponível para o canal AD_EXCHANGE do Ad Manager e para o Ssp e temos "Platform" que está disponível para o Ssp mas não para o Ad Manager
      • Comutação o estado do formulário pode ser muito complicado e confuso

Implementação com novas funcionalidades:

Apresento-lhe o próximo excerto principalmente para mostrar a complexidade do código. Pode deixá-lo à vontade para o ocultar.

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

  _triggerCallbacks () {
    // escolher callbacks dependendo da fonte
  }

  _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 algum dos disablingDemandChannels for verificado
  }
}

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

class ResearchDynamicFilter {
  // Não simplifiquei o corpo destes dois métodos para mostrar a complexidade atual da implementação

  _setDefaultDynamicFiltersToggleEvent () {
    $(this._getBody()).on('dynamicFilter:enableSspFilters', (event, shouldEnableSspOptions) => {
      this._setDefaultFiltersOptionDisabledState(shouldEnableSspOptions);

      const selectedFilterDimension = this._getFiltersDimension().find('option:selected').val();
      Se (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();
      se ($.inArray(selectedFilterDimension, ['website', 'product']) >= 0) {
        this._toggleChosenFilterDisabledState(shouldDisableWebsitesAndProducts);
      }
      this._setMethodSelectWebsiteAndProductOptionDisabledState(shouldDisableWebsitesAndProducts);
    });
  }

  _toggleNonSspFilters (dimensionSelect, shouldDisable) {
    $.each(ResearchDynamicFilter.NON_SSP_FILTERS_OPTIONS, (_, option) => {
      // alternar o estado do filtro em função de 'shouldDisable'
    });
  }
}

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

Ainda usamos alguns 'alternar' mecanismo. É realmente difícil mudar 4 alavancas e chegar ao estado esperado e agora o DynamicFilter tem de saber quais as dimensões que não são para a fonte ssp.

Temos o ResearchFormStateUpdater, porque não há-de ser ele o responsável?

Pedido final

Adicionar outro filtro para "Parceiro de rendimento

Foi nesse exato momento que decidimos refactorizar essas classes. Os canais e filtros que estão a ser analisados são apenas uma pequena parte do problema. Existem várias secções de formulários aqui e todas elas têm o mesmo problema. A nossa refacção deve neutralizar a necessidade de alterar os métodos internos dessas classes *para* adicionar alguns novos canais ou dimensões.

No snippet seguinte, deixei as classes principais quase como estão no nosso código de produção para mostrar como são fáceis de entender agora.

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

  _updateDynamicFilters () {
    this._toggleAllDynamicFiltersState(this._dynamicFiltersDimensionsToBeDisabled());
  }

  _dynamicFiltersDimensionsToBeDisabled () {
    if (this.isSourceSsp) { return ResearchFormStateUpdater.NO_SSP_FILTERS; }

    var disabledFilters = ResearchFormStateUpdater.ONLY_SSP_FILTERS;
    se (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'];

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

  _disableFilters (filtersToDisable) {
    // desativar filtersToDisable
  }

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

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

Conseguimos! Conseguimos?

Agora, a única coisa que o 'ResearchDynamicFilter' tem de saber é uma lista de todos os filtros - parece-me justo. O resto da lógica e do controlo vem de cima - alguns métodos e constantes superiores.

Vamos então experimentar a nossa nova estrutura adicionando um filtro para "Yield_partner":

classe ResearchFormStateUpdater {
  _dynamicFiltersDimensionsToBeDisabled () {
    (...)
    se (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 pode ver, trata-se apenas de acrescentar alguns valores a constantes e algumas condições adicionais.

Graças ao "princípio aberto-fechado", podemos alterar a lógica comercial do formulário acrescentando apenas alguns valores e condições a um nível de abstração mais elevado. Não precisamos de entrar no componente e alterar nada. Esta refacção afectou todo o formulário e havia mais secções, e todas elas obedecem agora ao princípio aberto-fechado.

Não reduzimos a quantidade de código - de facto, até a aumentámos (antes/depois):

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

É tudo sobre a coleção de constantes -> é a nossa interface pública agora, a nossa consola para controlar o processo sem dezenas de comutadores.

Leia também:

  • Como escrever um código bom e de qualidade?
  • Vuelendar. Um novo projeto da Codest baseado no Vue.js
  • O que é o Ruby on Jets e como criar uma aplicação com ele?

Artigos relacionados

Ilustração de uma aplicação de cuidados de saúde para smartphone com um ícone de coração e um gráfico de saúde em ascensão, com o logótipo The Codest, representando soluções digitais de saúde e HealthTech.
Desenvolvimento de software

Softwares para o setor de saúde: Tipos, casos de uso

As ferramentas em que as organizações de cuidados de saúde confiam atualmente não se assemelham em nada às fichas de papel de há décadas atrás. O software de cuidados de saúde apoia agora os sistemas de saúde, os cuidados aos doentes e a prestação de cuidados de saúde modernos em...

OCODEST
Ilustração abstrata de um gráfico de barras em declínio com uma seta ascendente e uma moeda de ouro que simboliza a eficiência ou a poupança de custos. O logótipo The Codest aparece no canto superior esquerdo com o slogan "In Code We Trust" sobre um fundo cinzento claro
Desenvolvimento de software

Como dimensionar a sua equipa de desenvolvimento sem perder a qualidade do produto

Aumentar a sua equipa de desenvolvimento? Saiba como crescer sem sacrificar a qualidade do produto. Este guia cobre sinais de que é hora de escalar, estrutura da equipe, contratação, liderança e ferramentas - além de como o The Codest pode...

OCODEST
Desenvolvimento de software

Construir aplicações Web preparadas para o futuro: ideias da equipa de especialistas do The Codest

Descubra como o The Codest se destaca na criação de aplicações web escaláveis e interactivas com tecnologias de ponta, proporcionando experiências de utilizador perfeitas em todas as plataformas. Saiba como a nossa experiência impulsiona a transformação digital e o negócio...

OCODEST
Desenvolvimento de software

As 10 principais empresas de desenvolvimento de software sediadas na Letónia

Saiba mais sobre as principais empresas de desenvolvimento de software da Letónia e as suas soluções inovadoras no nosso último artigo. Descubra como estes líderes tecnológicos podem ajudar a elevar o seu negócio.

thecodest
Soluções para empresas e escalas

Fundamentos do desenvolvimento de software Java: Um Guia para Terceirizar com Sucesso

Explore este guia essencial sobre o desenvolvimento de software Java outsourcing com sucesso para aumentar a eficiência, aceder a conhecimentos especializados e impulsionar o sucesso do projeto com The Codest.

thecodest

Subscreva a nossa base de conhecimentos e mantenha-se atualizado sobre os conhecimentos do sector das TI.

    Sobre nós

    The Codest - Empresa internacional de desenvolvimento de software com centros tecnológicos na Polónia.

    Reino Unido - Sede

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

    Polónia - Pólos tecnológicos locais

    • Parque de escritórios Fabryczna, Aleja
      Pokoju 18, 31-564 Cracóvia
    • Embaixada do Cérebro, Konstruktorska
      11, 02-673 Varsóvia, Polónia

      The Codest

    • Início
    • Sobre nós
    • Serviços
    • Case Studies
    • Saber como
    • Carreiras
    • Dicionário

      Serviços

    • Aconselhamento
    • Desenvolvimento de software
    • Desenvolvimento backend
    • Desenvolvimento de front-end
    • Staff Augmentation
    • Programadores de back-end
    • Engenheiros de nuvem
    • Engenheiros de dados
    • Outros
    • Engenheiros de GQ

      Recursos

    • Factos e mitos sobre a cooperação com um parceiro externo de desenvolvimento de software
    • Dos EUA para a Europa: Porque é que as empresas americanas decidem mudar-se para a Europa?
    • Comparação dos centros de desenvolvimento da Tech Offshore: Tech Offshore Europa (Polónia), ASEAN (Filipinas), Eurásia (Turquia)
    • Quais são os principais desafios dos CTOs e dos CIOs?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • Website terms of use

    Direitos de autor © 2026 por The Codest. Todos os direitos reservados.

    pt_PTPortuguese
    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 cs_CZCzech pt_PTPortuguese