Estou tentando aprender GRASP e achei isso explicado ( aqui na página 3 ) sobre acoplamento baixo e fiquei muito surpreso quando descobri isso:
Considere o método
addTrack
para umaAlbum
classe, dois métodos possíveis são:
addTrack( Track t )
e
addTrack( int no, String title, double duration )
Qual método reduz o acoplamento? O segundo, porque a classe que usa a classe Album não precisa conhecer uma classe Track. Em geral, os parâmetros dos métodos devem usar tipos de base (int, char ...) e classes dos pacotes java. *.
Eu tendem a discordar disso; Eu acredito que addTrack(Track t)
é melhor do que addTrack(int no, String title, double duration)
devido a várias razões:
É sempre melhor para um método o menor número possível de parâmetros (de acordo com o Código Limpo do tio Bob, nenhum ou um de preferência, 2 em alguns casos e 3 em casos especiais; mais de 3 precisam de refatoração - essas são, obviamente, recomendações, não regras de azevinho) .
Se
addTrack
é o método de uma interface, e os requisitos precisam que aTrack
deva ter mais informações (por exemplo, ano ou gênero), então a interface precisa ser alterada e, portanto, o método deve suportar outro parâmetro.O encapsulamento está quebrado; se
addTrack
estiver em uma interface, ele não deve conhecer os elementos internos do arquivoTrack
.Na verdade, é mais acoplado da segunda maneira, com muitos parâmetros. Suponha que o
no
parâmetro precise ser alterado deint
paralong
porque existem mais deMAX_INT
trilhas (ou por qualquer motivo); entãoTrack
o método e o método precisam ser alterados, enquanto se o método foraddTrack(Track track)
apenas oTrack
seria alterado.
Todos os quatro argumentos estão realmente conectados entre si, e alguns deles são consequências de outros.
Qual abordagem é melhor?
Respostas:
Bem, seus três primeiros pontos são realmente sobre outros princípios além do acoplamento. Você sempre precisa encontrar um equilíbrio entre os princípios de design frequentemente conflitantes.
Seu quarto ponto é sobre o acoplamento, e eu concordo plenamente com você. O acoplamento é sobre o fluxo de dados entre os módulos. O tipo de contêiner no qual os dados fluem é em grande parte irrelevante. Passar uma duração como um duplo em vez de como um campo de a
Track
não evita a necessidade de passá-lo. Os módulos ainda precisam compartilhar a mesma quantidade de dados e ainda têm a mesma quantidade de acoplamento.Ele também está deixando de considerar todo o acoplamento no sistema como um agregado. Embora a introdução de uma
Track
classe acrescente outra dependência entre dois módulos individuais, ela pode reduzir significativamente o acoplamento do sistema , que é a medida importante aqui.Por exemplo, considere um botão "Adicionar à lista de reprodução" e um
Playlist
objeto. A introdução de umTrack
objeto pode ser considerada para aumentar o acoplamento se você considerar apenas esses dois objetos. Agora você tem três classes interdependentes em vez de duas. No entanto, esse não é o todo do seu sistema. Você também precisa importar a faixa, reproduzi-la, exibi-la etc. A adição de mais uma classe a esse mix é insignificante.Agora considere a necessidade de adicionar suporte para reproduzir faixas pela rede, em vez de apenas localmente. Você só precisa criar um
NetworkTrack
objeto que esteja em conformidade com a mesma interface. Sem oTrack
objeto, você teria que criar funções em qualquer lugar, como:Isso dobra seu acoplamento efetivamente, exigindo que mesmo os módulos que não se importam com o material específico da rede continuem acompanhando-o, para poder transmiti-lo.
Seu teste de efeito cascata é bom para determinar sua verdadeira quantidade de acoplamento. O que nos preocupa é limitar os lugares que uma mudança afeta.
fonte
Minha recomendação é:
Usar
mas verifique se
ITrack
é uma interface e não uma classe concreta.O álbum não conhece os elementos internos dos
ITrack
implementadores. É apenas acoplado ao contrato definido peloITrack
.Eu acho que essa é a solução que gera a menor quantidade de acoplamento.
fonte
Track
ser burro ou inteligente.Track
é uma concreção.ITrack
interface é uma abstração. Dessa forma, você poderá ter diferentes tipos de faixas no futuro, desde que cumpramITrack
.Eu argumentaria que o segundo método de exemplo provavelmente aumenta o acoplamento, pois provavelmente instancia um objeto Track e o armazena no objeto Album atual. (Como sugerido no meu comentário acima, eu presumo que seja inerente que uma classe Album tenha o conceito de classe Track em algum lugar dentro dela.)
O primeiro método de exemplo assume que uma faixa está sendo instanciada fora da classe Album, portanto, pelo menos, podemos assumir que a instanciação da classe Track não está acoplada à classe Album.
Se as práticas recomendadas sugerissem que nunca tivéssemos uma referência de classe por segunda, toda a programação orientada a objetos seria lançada pela janela.
fonte
O acoplamento é apenas um dos muitos aspectos a serem obtidos no seu código. Ao reduzir o acoplamento, você não está necessariamente melhorando seu programa. Em geral, essa é uma prática recomendada, mas nesse caso específico, por que não deveria
Track
ser conhecido?Ao usar uma
Track
classe a ser transmitidaAlbum
, você facilita a leitura do código, mas, mais importante, como você mencionou, está transformando uma lista estática de parâmetros em um objeto dinâmico. Em última análise, isso torna sua interface muito mais dinâmica.Você menciona que o encapsulamento está quebrado, mas não está.
Album
deve conhecer os elementos internosTrack
e, se você não usou um objeto,Album
teria que conhecer todas as informações passadas antes de poder fazer uso da mesma maneira. O chamador também deve conhecer os elementos internosTrack
, pois deve construir umTrack
objeto, mas o chamador deve conhecer essas informações da mesma forma, se forem passadas diretamente para o método. Em outras palavras, se a vantagem do encapsulamento não é conhecer o conteúdo de um objeto, ele não pode ser usado nesse caso, poisAlbum
deve fazer uso daTrack
informação da mesma forma.Onde você não gostaria de usar
Track
é seTrack
contém lógica interna à qual você não gostaria que o chamador tivesse acesso. Em outras palavras, seAlbum
fosse uma classe que um programador usando sua biblioteca deveria usar, você não gostaria que ele usasseTrack
se usasse para dizer, chame um método para persistir no banco de dados. O verdadeiro problema disso reside no fato de a interface estar emaranhada com o modelo.Para corrigir o problema, seria necessário separar
Track
os componentes de interface e os componentes lógicos, criando duas classes separadas. Para o chamador,Track
torna-se uma classe leve destinada a reter informações e oferecer pequenas otimizações (dados calculados e / ou valores padrão). Por dentroAlbum
, você usaria uma classe nomeadaTrackDAO
para executar o trabalho pesado associado ao salvamento das informaçõesTrack
no banco de dados.Claro, isso é apenas um exemplo. Tenho certeza de que esse não é o seu caso e, portanto, fique à vontade para usar sem
Track
culpa. Lembre-se de manter o chamador em mente ao criar classes e criar interfaces quando necessário.fonte
Ambos estão corretos
é melhor (como você já argumentou) enquanto
é menos acoplado porque o código que usa
addTrack
não precisa saber que existe umaTrack
classe. A faixa pode ser renomeada, por exemplo, sem a necessidade de atualizar o código de chamada.Enquanto você está falando sobre um código mais legível / de manutenção, o artigo está falando sobre acoplamento . Um código menos acoplado não é necessariamente mais fácil de implementar e entender.
fonte
Baixo acoplamento não significa sem acoplamento. Algo, em algum lugar, precisa saber sobre objetos em outras partes da base de código e, quanto mais você reduz a dependência de objetos "personalizados", mais motivos você dá para o código mudar. O que o autor que você menciona está promovendo com a segunda função é menos associado, mas também menos orientado a objetos, o que contraria toda a idéia do GRASP de ser uma metodologia de design orientada a objetos . O ponto principal é como projetar o sistema como uma coleção de objetos e suas interações; evitá-los é como ensiná-lo a dirigir, dizendo que você deveria andar de bicicleta.
Em vez disso, o caminho certo é reduzir a dependência de objetos concretos , que é a teoria do "acoplamento solto". Quanto menos tipos concretos definidos um método precisar conhecer, melhor. Apenas por essa afirmação, a primeira opção é realmente menos acoplada, porque o segundo método que utiliza os tipos mais simples deve conhecer todos esses tipos mais simples. Claro que eles estão embutidos, e o código dentro do método pode ter que se importar, mas a assinatura do método e os chamadores do método definitivamente não se importam . Alterar um desses parâmetros relacionados a uma faixa de áudio conceitual exigirá mais alterações quando elas estiverem separadas e quando estiverem contidas em um objeto Track (que é o ponto dos objetos; encapsulamento).
Indo um passo adiante, se se esperava que o Track fosse substituído por algo que fizesse o mesmo trabalho melhor, talvez uma interface que definisse a funcionalidade necessária estivesse em ordem, um ITrack. Isso poderia permitir implementações diferentes, como "AnalogTrack", "CdTrack" e "Mp3Track", que forneciam informações adicionais mais específicas a esses formatos, além de fornecer a exposição básica de dados do ITrack que representa conceitualmente uma "faixa"; uma sub-parte finita de áudio. O Track também pode ser uma classe base abstrata, mas isso exige que você sempre deseje usar a implementação inerente ao Track; reimplemente-o como BetterTrack e agora você precisa alterar os parâmetros esperados.
Assim, a regra de ouro; programas e seus componentes de código sempre terão motivos para mudar. Você não pode escrever um programa que nunca exija a edição do código que você já escreveu para adicionar algo novo ou modificar seu comportamento. Seu objetivo, em qualquer metodologia (GRASP, SOLID, qualquer outro acrônimo ou palavra-chave que você possa imaginar) é simplesmente identificar as coisas que terão que mudar ao longo do tempo e projetar o sistema para que essas mudanças sejam o mais fácil possível. (traduzido; tocando no menor número de linhas de código e afetando o menor número possível de outras áreas do sistema além do escopo da alteração pretendida possível). Nesse caso, o que provavelmente mudará é que um Track ganhará mais membros de dados que addTrack () pode ou não se importar, não essa faixa será substituída pelo BetterTrack.
fonte