Qual estratégia você usa para nomeação de pacotes em projetos Java e por quê? [fechadas]

88

Eu pensei sobre isso um pouco atrás e recentemente reapareceu enquanto minha loja estava fazendo seu primeiro aplicativo Java real da web.

Como introdução, vejo duas estratégias principais de nomenclatura de pacotes. (Para ser claro, não estou me referindo a toda a parte 'domain.company.project' disso, estou falando sobre a convenção de pacote abaixo dela.) De qualquer forma, as convenções de nomenclatura de pacote que vejo são as seguintes:

  1. Funcional: Nomear seus pacotes de acordo com sua função arquitetônica, em vez de sua identidade de acordo com o domínio do negócio. Outro termo para isso pode ser nomear de acordo com 'camada'. Portanto, você teria um pacote * .ui e um pacote * .domain e um pacote * .orm. Seus pacotes são fatias horizontais em vez de verticais.

    Isso é muito mais comum do que a nomenclatura lógica. Na verdade, acho que nunca vi ou ouvi falar de um projeto que faça isso. É claro que isso me deixa desconfiado (mais ou menos como pensar que você encontrou uma solução para um problema de NP), pois não sou muito inteligente e presumo que todos devem ter grandes motivos para fazer isso da maneira que fazem. Por outro lado, não me oponho a que as pessoas simplesmente sintam falta do elefante na sala e nunca ouvi um argumento real para nomear pacotes dessa maneira. Parece ser o padrão de fato.

  2. Lógico: Nomear seus pacotes de acordo com sua identidade de domínio de negócios e colocar todas as classes relacionadas a essa fatia vertical de funcionalidade nesse pacote.

    Eu nunca vi ou ouvi isso, como mencionei antes, mas faz muito sentido para mim.

    1. Tenho a tendência de abordar os sistemas verticalmente em vez de horizontalmente. Eu quero desenvolver o sistema de processamento de pedidos, não a camada de acesso a dados. Obviamente, há uma boa chance de tocar na camada de acesso a dados no desenvolvimento desse sistema, mas a questão é que não penso nisso dessa forma. O que isso significa, é claro, é que quando eu receber um pedido de alteração ou quiser implementar algum novo recurso, seria bom não ter que procurar em um monte de pacotes para encontrar todas as classes relacionadas. Em vez disso, eu apenas olho no pacote X porque o que estou fazendo tem a ver com o X.

    2. Do ponto de vista do desenvolvimento, considero uma grande vitória ter seus pacotes documentando seu domínio de negócios em vez de sua arquitetura. Eu sinto que o domínio é quase sempre a parte do sistema que é mais difícil de entender, já que a arquitetura do sistema, especialmente neste ponto, está quase se tornando comum em sua implementação. O fato de eu poder chegar a um sistema com este tipo de convenção de nomenclatura e, instantaneamente, a partir da nomenclatura dos pacotes saber que ele lida com pedidos, clientes, empresas, produtos, etc., parece muito útil.

    3. Parece que isso permitiria que você aproveitasse muito melhor os modificadores de acesso do Java. Isso permite que você defina interfaces de maneira muito mais limpa em subsistemas, em vez de em camadas do sistema. Então, se você tem um subsistema de pedidos que deseja que seja transparentemente persistente, você poderia, em teoria, nunca deixar que ninguém soubesse que é persistente, não tendo que criar interfaces públicas para suas classes de persistência na camada dao e, em vez disso, empacotando a classe dao em com apenas as classes com as quais lida. Obviamente, se você quiser expor essa funcionalidade, poderá fornecer uma interface para ela ou torná-la pública. Parece que você perde muito disso por ter uma fatia vertical dos recursos do seu sistema divididos em vários pacotes.

    4. Suponho que uma desvantagem que posso ver é que isso torna a remoção de camadas um pouco mais difícil. Em vez de apenas excluir ou renomear um pacote e, em seguida, colocar um novo no lugar com uma tecnologia alternativa, você precisa entrar e alterar todas as classes em todos os pacotes. No entanto, não vejo que seja grande coisa. Pode ser por falta de experiência, mas eu tenho que imaginar que a quantidade de vezes que você troca de tecnologia empalidece em comparação com a quantidade de vezes que você entra e edita fatias de recursos verticais em seu sistema.

Então eu acho que a pergunta seria para você, como você nomeia seus pacotes e por quê? Por favor, entenda que não acho necessariamente que tropecei na galinha dos ovos de ouro ou algo assim aqui. Sou muito novo em tudo isso, principalmente com experiência acadêmica. No entanto, não consigo detectar as lacunas em meu raciocínio, então espero que todos vocês possam para que eu possa seguir em frente.

Tim Visher
fonte
O que ouvi até agora parece indicar que é uma espécie de julgamento. No entanto, a maioria das respostas não se concentrou nas considerações práticas. É mais isso que procuro. Obrigado pela ajuda até agora!
Tim Visher
Não acho que seja um julgamento. Dividir primeiro por camada e depois por funcionalidade é definitivamente o caminho a seguir. Estou atualizando minha resposta.
eljenso
@eljenso: diga isso para o pessoal do Eclipse :) No eclipse, a primeira divisão é para "recursos", que são unidades de implantações e também funcionalidade. Dentro do "recurso", existem "plug-ins", que geralmente são divididos por camada - mas os plug-ins dentro do mesmo "recurso" devem sempre ser implantados juntos. Se você tiver apenas uma unidade de implantação, pode fazer sentido dividir primeiro por camada e somente depois por funcionalidade. Com várias unidades de implantação, você não deseja implantar apenas a camada de apresentação - você deseja funcionalidade adicional.
Peter Štibraný

Respostas:

32

Para design de embalagem, primeiro divido por camada e depois por alguma outra funcionalidade.

Existem algumas regras adicionais:

  1. as camadas são empilhadas da mais geral (parte inferior) à mais específica (parte superior)
  2. cada camada tem uma interface pública (abstração)
  3. uma camada pode depender apenas da interface pública de outra camada (encapsulamento)
  4. uma camada só pode depender de camadas mais gerais (dependências de cima para baixo)
  5. uma camada de preferência depende da camada diretamente abaixo dela

Portanto, para um aplicativo da web, por exemplo, você pode ter as seguintes camadas em sua camada de aplicativo (de cima para baixo):

  • camada de apresentação: gera a IU que será mostrada na camada de cliente
  • camada de aplicativo: contém a lógica específica de um aplicativo, com estado
  • camada de serviço: agrupa a funcionalidade por domínio, sem estado
  • camada de integração: fornece acesso à camada de back-end (db, jms, email, ...)

Para o layout do pacote resultante, estas são algumas regras adicionais:

  • a raiz de cada nome de pacote é <prefix.company>.<appname>.<layer>
  • a interface de uma camada é ainda dividida por funcionalidade: <root>.<logic>
  • a implementação privada de uma camada é prefixada com privado: <root>.private

Aqui está um exemplo de layout.

A camada de apresentação é dividida por tecnologia de visualização e, opcionalmente, por (grupos de) aplicativos.

com.company.appname.presentation.internal
com.company.appname.presentation.springmvc.product
com.company.appname.presentation.servlet
...

A camada de aplicativo é dividida em casos de uso.

com.company.appname.application.lookupproduct
com.company.appname.application.internal.lookupproduct
com.company.appname.application.editclient
com.company.appname.application.internal.editclient
...

A camada de serviço é dividida em domínios de negócios, influenciados pela lógica de domínio em uma camada de back-end.

com.company.appname.service.clientservice
com.company.appname.service.internal.jmsclientservice
com.company.appname.service.internal.xmlclientservice
com.company.appname.service.productservice
...

A camada de integração é dividida em 'tecnologias' e objetos de acesso.

com.company.appname.integration.jmsgateway
com.company.appname.integration.internal.mqjmsgateway
com.company.appname.integration.productdao
com.company.appname.integration.internal.dbproductdao
com.company.appname.integration.internal.mockproductdao
...

As vantagens de separar pacotes como esse é que é mais fácil gerenciar a complexidade e aumenta a testabilidade e a reutilização. Embora pareça um monte de sobrecarga, na minha experiência, é muito natural e todos que trabalham nesta estrutura (ou semelhante) pegam em questão de dias.

Por que eu acho que a abordagem vertical não é tão boa?

No modelo em camadas, vários módulos de alto nível diferentes podem usar o mesmo módulo de nível inferior. Por exemplo: você pode construir várias visualizações para o mesmo aplicativo, vários aplicativos podem usar o mesmo serviço, vários serviços podem usar o mesmo gateway. O truque aqui é que, ao passar pelas camadas, o nível de funcionalidade muda. Módulos em camadas mais específicas não mapeiam 1-1 nos módulos da camada mais geral, porque os níveis de funcionalidade que eles expressam não mapeiam 1-1.

Quando você usa a abordagem vertical para design de embalagem, isto é, você divide primeiro por funcionalidade, então você força todos os blocos de construção com diferentes níveis de funcionalidade na mesma 'capa de funcionalidade'. Você pode projetar seus módulos gerais para um mais específico. Mas isso viola o princípio importante de que a camada mais geral não deve saber sobre camadas mais específicas. A camada de serviço, por exemplo, não deve ser modelada a partir de conceitos da camada de aplicativo.

eljenso
fonte
"As vantagens de separar pacotes como este é que é mais fácil gerenciar a complexidade e aumenta a testabilidade e a reutilização." Você realmente não explica as razões por que isso acontece.
mangoDrunk de
1
Você não explica as razões pelas quais não é assim. Mas de qualquer maneira ... as regras de organização são bastante claras e diretas, portanto, reduz a complexidade. É mais testável e reutilizável por causa da própria organização. Todos podem experimentar e, depois, podemos concordar ou discordar. Explico brevemente algumas das desvantagens da abordagem vertical, explicando implicitamente por que acho que a abordagem horizontal é mais fácil.
eljenso
8
Este artigo explica muito bem por que é melhor usar pacote por recurso em vez de pacote por camada .
Tom,
@eljenso "Você não entra nos motivos pelos quais não é assim", tentando deslocar o ônus da prova? Acho que o artigo postado por Tom explica muito bem por que pacote por recurso é melhor e estou tendo dificuldade em entender "Não acho que seja um julgamento. Primeiro dividir por camada, depois por funcionalidade é definitivamente o caminho ir "a sério.
pka
18

Eu me pego aderindo aos princípios de design de embalagem do Tio Bob . Resumindo, as classes que devem ser reutilizadas e alteradas juntas (pela mesma razão, por exemplo, uma mudança de dependência ou uma mudança de framework) devem ser colocadas no mesmo pacote. IMO, a divisão funcional teria melhor chance de atingir esses objetivos do que a divisão vertical / específica do negócio na maioria das aplicações.

Por exemplo, uma fatia horizontal de objetos de domínio pode ser reutilizada por diferentes tipos de front-ends ou mesmo aplicativos e uma fatia horizontal do front-end da web provavelmente mudará junto quando a estrutura da web subjacente precisar ser alterada. Por outro lado, é fácil imaginar o efeito cascata dessas mudanças em muitos pacotes se as classes em diferentes áreas funcionais forem agrupadas nesses pacotes.

Obviamente, nem todos os tipos de software são iguais e a divisão vertical pode fazer sentido (em termos de atingir as metas de reutilização e capacidade de fechamento para alteração) em certos projetos.

Buu Nguyen
fonte
Obrigado, isso é muito claro. Como eu disse na pergunta, sinto que o número de vezes que você trocaria de tecnologia é muito menor do que você trocaria por fatias verticais de funcionalidade. Não foi esta a sua experiência?
Tim Visher
Não se trata apenas de tecnologias. O ponto nº 1 de sua postagem original só faz sentido se as fatias verticais forem aplicativos / serviços independentes que se comunicam entre si por meio de alguma interface (SOA, se preferir). mais abaixo ...
Buu Nguyen
Agora, conforme você avança para os detalhes de cada um desses aplicativos / serviços refinados que têm sua própria interface / negócios / dados, mal posso imaginar que mudanças em uma fatia vertical, seja sobre tecnologia, dependência, regra / fluxo de trabalho, registro , segurança, estilo de IU, podem ser completamente isolados de outras fatias.
Buu Nguyen
Gostaria de receber seus comentários sobre minha resposta
i3ensays
@BuuNguyen O link para os princípios de design de pacote foi quebrado.
johnnieb
5

Geralmente, há ambos os níveis de divisão presentes. Do topo, existem unidades de implantação. Eles são nomeados 'logicamente' (em seus termos, pense nos recursos do Eclipse). Dentro da unidade de implantação, você tem divisão funcional de pacotes (pense em plug-ins do Eclipse).

Por exemplo, feature is com.feature, e consiste em com.feature.client, com.feature.coree com.feature.uiplugins. Dentro de plugins, tenho muito pouca divisão para outros pacotes, embora isso também não seja incomum.

Atualização: Btw, há uma grande conversa de Juergen Hoeller sobre a organização de código na InfoQ: http://www.infoq.com/presentations/code-organization-large-projects . Juergen é um dos arquitetos da Spring e sabe muito sobre essas coisas.

Peter Štibraný
fonte
Eu não entendo muito bem. Normalmente, você pode ver com.apache.wicket.x onde x é funcional ou lógico. Normalmente não vejo com.x. Acho que você está dizendo que escolheria uma estrutura com.company.project.feature.layer? Você tem motivos?
Tim Visher
A razão é que "com.company.project.feature" é a unidade de implantação. Nesse nível, alguns recursos são opcionais e podem ser ignorados (ou seja, não implantados). No entanto, dentro do recurso, as coisas não são opcionais e você geralmente quer todas elas. Aqui, faz mais sentido dividir por camadas.
Peter Štibraný
4

A maioria dos projetos java em que trabalhei corta os pacotes java funcionalmente primeiro, depois logicamente.

Normalmente as partes são grandes o suficiente para serem divididas em artefatos de construção separados, onde você pode colocar a funcionalidade central em um jar, apis em outro, coisas de front-end da web em um arquivo de guerra, etc.

Ben Hardy
fonte
Como pode ser isso? domain.company.project.function.logicalSlice?
Tim Visher
bastante! egdcpweb.profiles, dcpweb.registration, dcpapis, dcppersistence etc. geralmente funciona muito bem, sua milhagem pode variar. depende também se você estiver usando modelagem de domínio - se você tiver vários domínios, talvez queira dividir por domínio primeiro.
Ben Hardy
Eu pessoalmente prefiro o oposto, lógico do que funcional. apple.rpc, apples.model e depois banana.model, banana.store
mP.
Há mais valor em ser capaz de agrupar todas as coisas da apple do que agrupar todas as coisas da web.
mP.
3

Os pacotes devem ser compilados e distribuídos como uma unidade. Ao considerar quais classes pertencem a um pacote, um dos critérios principais são suas dependências. De quais outros pacotes (incluindo bibliotecas de terceiros) esta classe depende. Um sistema bem organizado agrupará classes com dependências semelhantes em um pacote. Isso limita o impacto de uma mudança em uma biblioteca, uma vez que apenas alguns pacotes bem definidos dependerão dela.

Parece que seu sistema lógico e vertical tende a "manchar" dependências na maioria dos pacotes. Ou seja, se cada recurso for empacotado como uma fatia vertical, cada pacote dependerá de cada biblioteca de terceiros que você usar. Qualquer mudança em uma biblioteca provavelmente afetará todo o sistema.

Erickson
fonte
Ah. Então, uma coisa que fazer fatias horizontais faz é protegê-lo de atualizações reais nas bibliotecas que você está usando. Acho que você está certo ao dizer que as fatias verticais mancham todas as dependências do seu sistema em todos os pacotes. Por quê isso é tão importante?
Tim Visher
Para um "recurso", não é um grande negócio. Eles tendem a ser mais voláteis e ter mais dependências de qualquer maneira. Por outro lado, esse código "cliente" tende a ter baixa reutilização. Para algo que você pretende ser uma biblioteca reutilizável, isolar os clientes de cada pequena mudança é um grande investimento.
Erickson
3

Eu pessoalmente prefiro agrupar classes logicamente, em seguida, dentro de que inclua um subpacote para cada participação funcional.

Objetivos de embalagem

Afinal, os pacotes tratam de agrupar coisas - a ideia de que classes relacionadas vivem próximas umas das outras. Se morarem no mesmo pacote, eles podem aproveitar a vantagem do pacote privado para limitar a visibilidade. O problema é que agrupar todas as suas coisas de visão e persistência em um pacote pode levar a muitas classes sendo misturadas em um único pacote. A próxima coisa sensata a fazer é criar visão, persistência, subpacotes util e classes de refatoração de acordo. O escopo Under infelizmente protegido e o pacote privado não suportam o conceito do pacote e subpacote atuais, pois isso ajudaria na aplicação de tais regras de visibilidade.

Vejo agora o valor na separação por meio da funcionalidade, porque qual valor existe para agrupar todas as coisas relacionadas à visão. As coisas nesta estratégia de nomenclatura se desconectam de algumas classes na visualização, enquanto outras estão em persistência e assim por diante.

Um exemplo da minha estrutura de embalagem lógica

Para fins de ilustração, vamos nomear dois módulos - usarei o módulo de nome como um conceito que agrupa classes em um ramo específico de uma árvore de pacote.

apple.model apple.store banana.model banana.store

Vantagens

Um cliente que usa Banana.store.BananaStore é exposto apenas à funcionalidade que desejamos disponibilizar. A versão de hibernação é um detalhe de implementação que eles não precisam estar cientes e nem devem ver essas classes, pois elas adicionam confusão às operações de armazenamento.

Outras vantagens lógicas v funcionais

Quanto mais em direção à raiz, mais amplo se torna o escopo e as coisas pertencentes a um pacote começam a exibir mais e mais dependências das coisas pertencentes aos outros módulos. Se alguém fosse examinar, por exemplo, o módulo "banana", a maioria das dependências seriam limitadas a esse módulo. Na verdade, a maioria dos ajudantes em "banana" não seriam referenciados fora deste escopo do pacote.

Por que funcionalidade?

Que valor alguém alcança ao agrupar coisas com base na funcionalidade. A maioria das classes em tal caso são independentes umas das outras com pouca ou nenhuma necessidade de tirar vantagem dos métodos ou classes privadas do pacote. Refatorá-los em seus próprios subpacotes ganha pouco, mas ajuda a reduzir a desordem.

Alterações do desenvolvedor no sistema

Quando os desenvolvedores são incumbidos de fazer mudanças um pouco mais do que triviais, parece bobo que potencialmente eles têm mudanças que incluem arquivos de todas as áreas da árvore do pacote. Com a abordagem de estrutura lógica, suas mudanças são mais locais dentro da mesma parte da árvore do pacote, o que parece correto.

mP.
fonte
"Quando os desenvolvedores [...] arquivos de todas as áreas da árvore do pacote." Você está certo quando diz que isso parece bobo. Porque esse é exatamente o ponto da estratificação adequada: as alterações não se propagam por toda a estrutura do aplicativo.
eljenso
Obrigado pelo conselho. Não tenho certeza se estou entendendo você claramente. Eu acredito que você está tentando argumentar por pacotes lógicos, não estou exatamente claro o porquê. Você poderia tentar tornar sua resposta um pouco mais clara, possivelmente reformulando? Obrigado!
Tim Visher
3

Ambas as abordagens funcional (arquitetônica) e lógica (recurso) para embalagem têm seu lugar. Muitos aplicativos de exemplo (aqueles encontrados em livros de texto, etc.) seguem a abordagem funcional de colocar apresentação, serviços de negócios, mapeamento de dados e outras camadas de arquitetura em pacotes separados. Em aplicativos de exemplo, cada pacote geralmente tem apenas algumas ou apenas uma classe.

Essa abordagem inicial é adequada, pois um exemplo inventado muitas vezes serve para: 1) mapear conceitualmente a arquitetura da estrutura que está sendo apresentada, 2) é feito com um único propósito lógico (por exemplo, adicionar / remover / atualizar / excluir animais de estimação de uma clínica) . O problema é que muitos leitores consideram isso um padrão sem limites.

Conforme um aplicativo de "negócio" se expande para incluir mais e mais recursos, seguir a abordagem funcional torna-se um fardo. Embora eu saiba onde procurar por tipos baseados na camada de arquitetura (por exemplo, controladores da web em um pacote "web" ou "ui", etc.), o desenvolvimento de um único recurso lógico começa a exigir a alternância entre muitos pacotes. Isso é complicado, no mínimo, mas é pior do que isso.

Como os tipos relacionados logicamente não são empacotados juntos, a API é amplamente divulgada; a interação entre os tipos relacionados logicamente é forçada a ser 'pública' para que os tipos possam importar e interagir uns com os outros (a capacidade de minimizar a visibilidade padrão / pacote é perdida).

Se eu estou construindo uma biblioteca de framework, por suposto meus pacotes seguirão uma abordagem de empacotamento funcional / arquitetônico. Meus consumidores de API podem até mesmo apreciar que suas instruções de importação contêm um pacote intuitivo com o nome da arquitetura.

Por outro lado, ao construir um aplicativo de negócios, empacotarei por recurso. Não tenho nenhum problema em colocar Widget, WidgetService e WidgetController no mesmo pacote " com.myorg.widget. " E, em seguida, tirar vantagem da visibilidade padrão (e ter menos instruções de importação, bem como dependências entre pacotes).

Existem, no entanto, casos cruzados. Se meu WidgetService for usado por muitos domínios lógicos (recursos), posso criar um pacote " com.myorg.common.service. " Também existe uma boa chance de eu criar classes com a intenção de serem reutilizáveis ​​em todos os recursos e acabar com pacotes como " com.myorg.common.ui.helpers. " E " com.myorg.common.util. ". Posso até acabar movendo todas essas classes "comuns" posteriores em um projeto separado e incluí-las em meu aplicativo de negócios como uma dependência myorg-commons.jar.

i3ensays
fonte
2

Depende da granularidade de seus processos lógicos?

Se forem autônomos, geralmente você tem um novo projeto para eles no controle de origem, em vez de um novo pacote.

O projeto em que estou no momento está errando em direção à divisão lógica, há um pacote para o aspecto jython, um pacote para um mecanismo de regras, pacotes para foo, bar, binglewozzle, etc. Estou procurando ter os analisadores específicos de XML / Writeers para cada módulo dentro desse pacote, em vez de ter um pacote XML (o que eu fiz anteriormente), embora ainda haja um pacote XML principal onde vai a lógica compartilhada. Uma razão para isso, entretanto, é que ele pode ser extensível (plug-ins) e, portanto, cada plug-in também precisará definir seu código XML (ou banco de dados, etc.), portanto, centralizar isso pode introduzir problemas mais tarde.

No final, parece ser o que parece mais adequado para o projeto específico. No entanto, acho que é fácil empacotar ao longo das linhas do diagrama em camadas de projeto típico. Você acabará com uma mistura de embalagens lógicas e funcionais.

O que é necessário são namespaces marcados. Um analisador XML para alguma funcionalidade Jython pode ser marcado como Jython e XML, em vez de ter que escolher um ou outro.

Ou talvez eu esteja wibbling.

JeeBee
fonte
Eu não entendo totalmente o seu ponto. Eu acho que o que você está dizendo é que você deve apenas fazer o que faz mais sentido para o seu projeto e para você que seja lógico com um pouco de funcionalidade incluída. Existem razões práticas específicas para isso?
Tim Visher
Como eu disse, terei plug-ins para aumentar a funcionalidade embutida. Um plugin terá que ser capaz de analisar e escrever seu XML (e eventualmente no banco de dados), bem como fornecer funcionalidade. Portanto, eu tenho com.xx.feature.xml ou com.xx.xml.feature? O primeiro parece mais puro.
JeeBee
2

Tento projetar estruturas de pacote de forma que, se eu fosse desenhar um gráfico de dependência, seria fácil seguir e usar um padrão consistente, com o mínimo de referências circulares possíveis.

Para mim, isso é muito mais fácil de manter e visualizar em um sistema de nomenclatura vertical em vez de horizontal. se component1.display tiver uma referência a component2.dataaccess, isso emite mais avisos do que se display.component1 tiver uma referência a dataaccess. componente2.

Obviamente, os componentes compartilhados por ambos vêm em seus próprios pacotes.

Levand
fonte
Pelo que entendi, você defenderia a convenção de nomenclatura vertical. Acho que é para isso que serve um pacote * .utils, quando você precisa de uma classe em vários segmentos.
Tim Visher
2

Eu sigo totalmente e proponho a organização lógica ("por característica")! Um pacote deve seguir o conceito de "módulo" o mais próximo possível. A organização funcional pode espalhar um módulo por um projeto, resultando em menos encapsulamento e sujeito a mudanças nos detalhes de implementação.

Vamos pegar um plugin do Eclipse, por exemplo: colocar todas as visualizações ou ações em um pacote seria uma bagunça. Em vez disso, cada componente de um recurso deve ir para o pacote do recurso ou, se houver muitos, para subpacotes (featureA.handlers, featureA.preferences etc.)

Claro, o problema está no sistema de pacotes hierárquicos (que entre outros Java tem), o que torna o tratamento de questões ortogonais impossível ou pelo menos muito difícil - embora eles ocorram em todos os lugares!

thSoft
fonte
1

Eu pessoalmente escolheria uma nomenclatura funcional. O motivo curto: evita a duplicação de código ou pesadelo de dependência.

Deixe-me elaborar um pouco. O que acontece quando você está usando um arquivo jar externo, com sua própria árvore de pacotes? Você está efetivamente importando o código (compilado) para o seu projeto e, com ele, uma árvore de pacotes (funcionalmente separada). Faria sentido usar as duas convenções de nomenclatura ao mesmo tempo? Não, a menos que isso estivesse escondido de você. E é, se o seu projeto for pequeno o suficiente e tiver um único componente. Mas se você tiver várias unidades lógicas, provavelmente não deseja reimplementar, digamos, o módulo de carregamento de arquivo de dados. Você deseja compartilhá-lo entre unidades lógicas, não ter dependências artificiais entre unidades logicamente não relacionadas e não precisa escolher em qual unidade colocar essa ferramenta compartilhada em particular.

Eu acho que é por isso que a nomenclatura funcional é mais usada em projetos que atingem, ou pretendem atingir, um certo tamanho, e a nomenclatura lógica é usada nas convenções de nomenclatura de classe para manter o controle da função específica, se alguma de cada classe em um pacote.

Tentarei responder com mais precisão a cada um de seus pontos sobre a nomenclatura lógica.

  1. Se você tem que ir pescar em classes antigas para modificar funcionalidades quando tem uma mudança de planos, é um sinal de má abstração: você deve construir classes que forneçam uma funcionalidade bem definida, definível em uma frase curta. Apenas algumas classes de nível superior devem reunir tudo isso para refletir sua inteligência de negócios. Dessa forma, você poderá reutilizar mais código, ter uma manutenção mais fácil, documentação mais clara e menos problemas de dependência.

  2. Isso depende principalmente da maneira como você groove seu projeto. Definitivamente, a visão lógica e funcional são ortogonais. Portanto, se você usar uma convenção de nomenclatura, precisará aplicar a outra aos nomes de classe para manter alguma ordem, ou bifurcar de uma convenção de nomenclatura para outra em alguma profundidade.

  3. Os modificadores de acesso são uma boa maneira de permitir que outras classes que entendem seu processamento acessem as entranhas de sua classe. O relacionamento lógico não significa uma compreensão das restrições algorítmicas ou de simultaneidade. Funcional pode, embora não o faça. Estou muito cansado de modificadores de acesso que não sejam públicos e privados, porque muitas vezes eles escondem a falta de arquitetura adequada e abstração de classes.

  4. Em grandes projetos comerciais, a mudança de tecnologias acontece com mais frequência do que você imagina. Por exemplo, já tive que mudar 3 vezes do analisador XML, 2 vezes da tecnologia de cache e 2 vezes do software de geolocalização. Ainda bem que escondi todos os detalhes difíceis em um pacote dedicado ...

Varkhan
fonte
1
Perdoe-me se estou errado, mas parece que você está mais falando sobre o desenvolvimento de uma estrutura que deve ser usada por muitas pessoas. Acho que as regras mudam entre esse tipo de desenvolvimento e o desenvolvimento de sistemas de usuário final. Estou errado?
Tim Visher
Eu acho que para classes comuns usadas por muitas das fatias verticais, faz sentido ter algo como um utilspacote. Então, todas as classes que precisarem podem simplesmente depender desse pacote.
Tim Visher
Mais uma vez, o tamanho importa: quando um projeto se torna grande o suficiente, ele precisa ser apoiado por várias pessoas, e algum tipo de especialização ocorrerá. Para facilitar a interação e evitar erros, segmentar o projeto em partes torna-se rapidamente necessário. E o pacote de utilitários logo se tornará gigantesco!
Varkhan
1

É uma experiência interessante não usar pacotes (exceto para o pacote raiz).

A questão que surge então é quando e por que faz sentido introduzir pacotes. Presumivelmente, a resposta será diferente do que você teria respondido no início do projeto.

Presumo que a sua pergunta surja, porque os pacotes são como categorias e às vezes é difícil decidir por uma ou outra. Às vezes, as tags seriam mais úteis para comunicar que uma classe pode ser usada em muitos contextos.

user53682
fonte
0

Do ponto de vista puramente prático, construções de visibilidade do Java permitem classes no mesmo pacote para métodos de acesso e propriedades com protectede defaultvisibilidade, bem como os publicqueridos. Usar métodos não públicos de uma camada completamente diferente do código seria definitivamente um cheiro forte de código. Portanto, tendo a colocar classes da mesma camada no mesmo pacote.

Não costumo usar esses métodos protegidos ou padrão em outros lugares - exceto possivelmente nos testes de unidade para a classe - mas quando faço isso, é sempre de uma classe na mesma camada

Bill Michell
fonte
Não é algo da natureza dos sistemas multicamadas estar sempre dependente da próxima camada abaixo? Por exemplo, a IU depende de sua camada de serviços, que depende de sua camada de domínio, etc. Empacotar fatias verticais juntas parece proteger contra dependências entre pacotes excessivas, não?
Tim Visher
Eu não uso métodos protegidos e padrão quase sempre. 99% são públicos ou privados. Algumas exceções: (1) visibilidade padrão para métodos usados ​​apenas por testes de unidade, (2) método abstrato protegido que é usado apenas a partir da classe base abstrata.
Esko Luontola
1
Tim Visher, dependências entre pacotes não são um problema, contanto que as dependências sempre apontem na mesma direção e não haja ciclos no gráfico de dependências.
Esko Luontola
0

Depende. Na minha linha de trabalho, às vezes dividimos pacotes por funções (acesso a dados, análises) ou por classe de ativos (crédito, ações, taxas de juros). Basta selecionar a estrutura mais conveniente para sua equipe.

quant_dev
fonte
Existe alguma razão para ir de qualquer maneira?
Tim Visher
A divisão por classe de ativo (mais geralmente: por domínio de negócios) torna mais fácil para as novas pessoas encontrarem seu caminho através do código. A divisão por função é boa para encapsulamento. Para mim, o acesso de "pacote" em Java é semelhante a um "amigo" em C ++.
quant_dev
@quant_dev Não concordo com "..por função é boa para encapsulamento". Acho que você quer dizer que o oposto é verdadeiro. Além disso, não escolha apenas por "conveniência". Há um caso para cada um (veja minha resposta).
i3ensays
-3

Da minha experiência, a reutilização cria mais problemas do que solução. Com os processadores e memória mais recentes e baratos, eu preferiria a duplicação de código em vez de uma integração rígida para reutilizar.

Raghu Kasturi
fonte
5
A reutilização de código não tem a ver com o preço do hardware, mas com os custos de manutenção do código.
user2793390
1
Concordo, mas consertar algo em um módulo simples não deve afetar o código inteiro. De quais custos de manutenção você está falando?
Raghu Kasturi
1
Uma correção de bug em um módulo deve afetar todo o aplicativo. Por que você deseja corrigir o mesmo bug várias vezes?
user2793390