Imagine que seu cliente deseja ter a possibilidade de adicionar uma nova propriedade (por exemplo, cor) ao produto em sua loja no CMS.
Em vez de ter propriedades como campos:
class Car extends Product {
protected String type;
protected int seats;
}
Você provavelmente acabaria fazendo algo como:
class Product {
protected String productName;
protected Map<String, Property> properties;
}
class Property {
protected String name;
protected String value;
}
Ou seja, criando um sistema de tipo próprio em cima do existente. Parece-me que isso poderia ser visto como a criação de uma linguagem específica de domínio ou não?
Essa abordagem é um padrão de design conhecido? Você resolveria o problema de maneira diferente? Eu sei que existem idiomas nos quais posso adicionar um campo em tempo de execução, mas e o banco de dados? Você prefere adicionar / alterar colunas ou usar algo como mostrado acima?
Obrigado pelo seu tempo :).
Respostas:
Parabéns! Você acabou de circunavegar o globo do sistema de linguagem / tipo de programação, chegando ao outro lado do mundo de onde partiu. Você acabou de desembarcar na fronteira da linguagem dinâmica / do objeto baseado em protótipo!
Muitas linguagens dinâmicas (por exemplo, JavaScript, PHP, Python) permitem estender ou alterar as propriedades do objeto em tempo de execução.
A forma extrema disso é uma linguagem baseada em protótipo como Self ou JavaScript. Eles não têm aulas, estritamente falando. Você pode fazer coisas que se parecem com programação baseada em classe e orientada a objetos com herança, mas as regras são bastante flexíveis em comparação com linguagens baseadas em classes mais definidas, como Java e C #.
Idiomas como PHP e Python vivem no meio do caminho. Eles têm sistemas regulares baseados em classe idiomáticos. Porém, os atributos do objeto podem ser adicionados, alterados ou excluídos no tempo de execução - embora com algumas restrições (como "exceto tipos internos") que você não encontra no JavaScript.
A grande desvantagem desse dinamismo é o desempenho. Esqueça o quão forte ou fracamente digitada é a linguagem ou quão bem ela pode ser compilada no código da máquina. Objetos dinâmicos devem ser representados como mapas / dicionários flexíveis, em vez de estruturas simples. Isso adiciona sobrecarga a todos os acessos a objetos. Alguns programas envidam grandes esforços para reduzir essa sobrecarga (por exemplo, com atribuição de phantom kwarg e classes baseadas em slot no Python), mas a sobrecarga extra geralmente é apenas par para o curso e o preço da entrada.
Voltando ao seu design, você está enxertando a capacidade de ter propriedades dinâmicas em um subconjunto de suas classes. A
Product
pode ter atributos variáveis; presumivelmente umInvoice
ou umOrder
faria e não poderia. Não é um mau caminho a percorrer. Ele oferece a flexibilidade de variar onde você precisar, enquanto permanece em um sistema de idioma e tipo estrito e disciplinado. No lado negativo, você é responsável por gerenciar essas propriedades flexíveis e provavelmente precisará fazê-lo por meio de mecanismos que parecem um pouco diferentes dos atributos mais nativos.p.prop('tensile_strength')
em vez dep.tensile_strength
, por exemplo, e emp.set_prop('tensile_strength', 104.4)
vez dep.tensile_strength = 104.4
. Mas trabalhei e construí muitos programas em Pascal, Ada, C, Java e até mesmo linguagens dinâmicas que usavam exatamente esse acesso getter-setter para tipos de atributos não padrão; a abordagem é claramente viável.A propósito, essa tensão entre tipos estáticos e um mundo altamente variado é extremamente comum. Um problema análogo é frequentemente visto ao projetar o esquema do banco de dados, especialmente para armazenamentos de dados relacionais e pré-relacionais. Às vezes, isso é resolvido através da criação de "super-linhas" que contêm flexibilidade suficiente para conter ou definir a união de todas as variações imaginadas e, em seguida, o preenchimento de todos os dados que aparecem nesses campos. O WordPress
wp_posts
mesa , por exemplo, tem campos comocomment_count
,ping_status
,post_parent
epost_date_gmt
que só são interessantes em algumas circunstâncias, e que, na prática, muitas vezes ficar em branco. Outra abordagem é uma tabela normalizada muito sobressalente,wp_options
semelhante à suaProperty
classe. Embora exija gerenciamento mais explícito, os itens raramente ficam em branco. Os bancos de dados orientados a objetos e de documentos (por exemplo, MongoDB) costumam ter mais facilidade para lidar com as opções de alteração, porque eles podem criar e definir atributos praticamente à vontade.fonte
Eu gosto da pergunta, meus dois centavos:
Suas duas abordagens são radicalmente diferentes:
Em C ++, muitos usariam um std :: map da boost :: variant para obter uma mistura de ambos.
Disgressão: observe que alguns idiomas, como C #, permitem a criação dinâmica de tipos. Qual poderia ser uma boa solução para a questão geral de adicionar membros dinamicamente. No entanto, "modificar / adicionar" tipos após a compilação corrompe o próprio sistema de tipos e torna seus tipos "modificados" quase inúteis (por exemplo, como você acessaria essas propriedades adicionadas, pois você nem sabe que elas existem? A única maneira razoável seria seja uma reflexão sistemática sobre cada objeto ... terminando com uma linguagem dinâmica pura _ você pode consultar a palavra-chave .NET 'dinâmica')
fonte
Criar um tipo em tempo de execução parece muito mais complicado do que criar uma camada de abstração. É muito comum criar uma abstração para desacoplar sistemas.
Deixe-me mostrar um exemplo da minha prática. A bolsa de Moscou tem o núcleo comercial chamado Plaza2 com a API do trader. Os comerciantes escrevem seus programas para trabalhar com dados financeiros. O problema é que esses dados são muito grandes, complexos e altamente expostos a mudanças. Ele pode mudar após a introdução de um novo produto financeiro ou a alteração dos recursos de compensação. A natureza das mudanças futuras não pode ser prevista. Literalmente, isso pode mudar todos os dias e programadores ruins devem editar o código e lançar uma nova versão, e comerciantes irritados devem revisar seus sistemas.
A decisão óbvia é esconder todo o inferno financeiro atrás de uma abstração. Eles usaram a conhecida abstração de tabelas SQL. A maior parte de seu núcleo pode funcionar com qualquer esquema válido, assim como o software do trader pode analisar dinamicamente o esquema e descobrir se ele é compatível com o sistema.
Voltando ao seu exemplo, é normal criar uma linguagem abstrata diante de alguma lógica. Pode ser "propriedade", "tabela", "mensagem" ou até mesmo linguagem humana, mas preste atenção às desvantagens dessa abordagem:
fonte
Em XML e HTML, esses seriam os atributos para um nó / elemento. Eu também os ouvi chamados propriedades estendidas, pares nome / valor e parâmetros.
É assim que eu resolveria o problema, sim.
Um banco de dados seria como Java, em alguns sentidos. No pesudo-sql:
Isso corresponderia ao Java
Observe que não há necessidade de uma classe Property, pois o Mapa armazena o nome como a chave.
Eu tentei a coisa de adicionar / alterar coluna, e foi um pesadelo. Isso pode ser feito, mas as coisas continuaram ficando fora de sincronia e eu nunca consegui que funcionasse bem. A estrutura da tabela que descrevi acima foi muito mais bem-sucedida. Se você precisa fazer pesquisas e ORDER BY de, considerar o uso de uma tabela de atributos para cada tipo de dados (
date_attributes
,currency_attributes
, etc.) ou a adição de algumas das propriedades como boas colunas de banco de dados antigos na tabela de produtos. Os relatórios geralmente são muito mais fáceis de escrever nas colunas do banco de dados do que nas sub-tabelas.fonte