Adaptive Programming – Part 1

Índice dos posts

  1. Lei de Demeter – Introdução
  2. Lei de Demeter – Continuação
  3. Lei de Demeter – Continuação
  4. Weaving
  5. Visitor
  6. Walkabout
  7. Hooks
  8. CRUD Generation and Operations
  9. Aplicação de vídeo locadora – Introdução
  10. Value: Terminal, NonTerminal
  11. Edge hooks

Lei de Demeter

Law of Demeter
A style rule for object-oriented design and programming that suggests that a method should make only minimal assumptions about other classes. The rule essentially requires that a method attached to class A should use only the interface of the immediate part classes of A (both computed and stored), and of argument classes of the method, including A. Limits the set of clients of a class, in other words: “a method should not talk to strangers.”

Lei de Demeter estabelece, na sua forma orientada a objetos, que dentro de uma operação/método O de classe C deveríamos chamar apenas as operações das seguintes classes, denominadas classes supridoras preferenciais:

  • As classes das subpartes imediatas (computadas ou armazenadas) do objeto atual
  • As classes dos objetos argumentos de O (incluindo a própria classe C)
  • As classes de objetos criados por O.

The Law of Demeter essentially says that you should only talk to yourself (current class), to close relatives (immediate part classes), and friends who visit you (argument classes). But you never talk to strangers.

(A Lei de Demeter essencialmente diz que você só deve falar consigo mesmo (a classe atual), com os parentes próximos (parte imediatas da classe), e amigos que visitam você (classes dos argumentos). Mas você nunca fala com estranhos.)

Fonte: Adaptive Object-Oriented Software: The Demeter Method


A aplicação da Lei de Demeter pode ser mostrada num exemplo simples. Vamos imaginar um departamento em uma empresa e seus empregados (Veja a figura abaixo).

Queremos implementar um CRUD (Create, Retrieve, Update, Delete) para manter o cadastro de empregados do departamento. Uma forma “ligeira” de implementar a estrutura e o CRUD é criar as classes Departamento, Empregado e usar uma collection para agrupar os empregados do departamento. Para manter os empregados usaremos a própria interface da implementação da collection escolhida na biblioteca disponível da linguagem de implementação. Usaremos Smalltalk a seguir para exemplificar o código.

No código acima as mensagens

departamento empregados add: (Empregado matricula: Empregado proximaMatricula nome: 'Carlos' salario: 3500).

emp := (departamento empregados select: [:empregado| empregado matricula = 3]) first.

departamento empregados remove: emp.

pressupõem um conhecimento da estrutura interna do objeto da classe Departamento. O código cliente usa o conhecimento de que a lista de empregados é uma OrderedCollection. São usadas as mensagens #add:, #select: e #remove:. Isso cria uma dependência que complica manutenções futuras.

Usamos uma OrderedCollection para agrupar os empregados mas queremos mudar para o uso de um Dictionary. O código no cliente teria que ser alterado como abaixo:

As linhas que executam o CRUD tiveram que ser alteradas para levar em conta o novo protocolo de mensagens aceitas pelo objeto da classe Dictionary. Isso teria que ser feito em TODOS os clientes, o que representa um esforço de manutenção inaceitável dado os riscos de inconsistência implícitos.

As linhas modificadas estão repetidas abaixo:


departamento empregados
at: Empregado proximaMatricula
put: (Empregado matricula: Empregado ultimaMatricula nome: 'Carlos' salario: 3500).


emp := departamento empregados at: 3.


departamento empregados removeKey: 3.

As mensagens #add:, #select: e #remove: aplicáveis a uma OrderedCollection foram trocadas por #at:put:, #at: e #removeKey: aceitas por um Dictionary.

Para aplicar a Lei de Demeter vamos evitar as mensagens endereçadas à estrutura interna do departamento. A classe Departamento é que deve prover um protocolo de mensagens para atender ao CRUD. Vamos então “esconder” um pouco a implementação. O código cliente abaixo tornou-se independente dos detalhes internos do objeto da classe Departamento.

A classe Departamento agora possui o protocolo composto pelos métodos #listarEmpregados, #adicionarEmpregado:, #recuperarEmpregadoMatricula: e #removerEmpregadoMatricula:. Todas as modificações de estrutura agora ficam confinadas aos bastidores da classe Departamento e não afetam o código cliente.

Nota: Os códigos usados nos exemplos não farão tratamento de exceções em favor da simplicidade e para que o objetivos de explicitar a aplicação da Lei de Demeter não fique “soterrado” com os detalhes da manipulação de erros.

Abaixo segue o file-out do código até aqui:

Object subclass: #Empregado
	instanceVariableNames: 'matricula nome salario'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'AdaptiveProgrammingBlogPost-Examples'!

!Empregado methodsFor: 'accessing' stamp: 'FranciscoAryMartins 6/14/2012 07:31'!
matricula
	^ matricula! !

!Empregado methodsFor: 'accessing' stamp: 'FranciscoAryMartins 6/14/2012 07:31'!
matricula: anObject
	matricula := anObject! !

!Empregado methodsFor: 'accessing' stamp: 'FranciscoAryMartins 6/14/2012 07:26'!
nome
	^ nome! !

!Empregado methodsFor: 'accessing' stamp: 'FranciscoAryMartins 6/14/2012 07:26'!
nome: anObject
	nome := anObject! !

!Empregado methodsFor: 'accessing' stamp: 'FranciscoAryMartins 6/14/2012 07:26'!
salario
	^ salario! !

!Empregado methodsFor: 'accessing' stamp: 'FranciscoAryMartins 6/14/2012 07:26'!
salario: anObject
	salario := anObject! !

"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!

Empregado class
	instanceVariableNames: 'proximaMatricula'!

!Empregado class methodsFor: 'as yet unclassified' stamp: 'FranciscoAryMartins 6/14/2012 07:56'!
initialize
	"
	self initialize
	"
	self reinicieMatriculas! !

!Empregado class methodsFor: 'as yet unclassified' stamp: 'FranciscoAryMartins 6/14/2012 07:32'!
matricula: anInteger nome: aString salario: aFloat
	^ self new
	      matricula: anInteger;
		nome: aString;
		salario: aFloat ! !

!Empregado class methodsFor: 'as yet unclassified' stamp: 'FranciscoAryMartins 6/14/2012 07:28'!
nome: aString salario: aFloat
	^ self new
		nome: aString;
		salario: aFloat ! !

!Empregado class methodsFor: 'as yet unclassified' stamp: 'FranciscoAryMartins 6/14/2012 07:37'!
proximaMatricula
	proximaMatricula := proximaMatricula + 1.
	^ proximaMatricula! !

!Empregado class methodsFor: 'as yet unclassified' stamp: 'FranciscoAryMartins 6/14/2012 07:55'!
reinicieMatriculas
	proximaMatricula := 0..! !

!Empregado class methodsFor: 'as yet unclassified' stamp: 'FranciscoAryMartins 6/14/2012 21:26'!
ultimaMatricula
	^ proximaMatricula! !

Object subclass: #Aplicacao
	instanceVariableNames: 'departamento'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'AdaptiveProgrammingBlogPost-Examples'!

!Aplicacao methodsFor: 'as yet unclassified' stamp: 'FranciscoAryMartins 6/14/2012 16:33'!
initialize
	departamento := Departamento new! !

!Aplicacao methodsFor: 'as yet unclassified' stamp: 'FranciscoAryMartins 6/14/2012 07:57'!
listarEmpregadosdoDepartamento: dep
	Transcript
		cr;
		show: 'Empregados:'.
	dep empregados
		do: [ :empregado |
			Transcript
				cr;
				tab;
				show: empregado matricula;
				space;
				show: empregado nome;
				space;
				show: empregado salario ]! !

!Aplicacao methodsFor: 'as yet unclassified' stamp: 'FranciscoAryMartins 6/16/2012 08:51'!
run
	| emp |
	Transcript clear.

	"Empregados atuais"
	departamento listarEmpregados.

	"Novo empregado (C do CRUD)"
	Transcript cr; show: 'Adicionando um empregado...'.
	emp := Empregado matricula: Empregado proximaMatricula  nome: 'Carlos' salario: 3500.
	Transcript  cr; show: emp matricula; space; show: emp nome; space; show: emp salario.
	departamento adicionarEmpregado: emp.
	departamento listarEmpregados.

	"Empregado com determinada matrícula (R do CRUD)"
	Transcript cr; show: 'Recuperando um empregado...'.
	emp := departamento recuperarEmpregadoMatricula: 3.
	Transcript  cr; show: emp matricula; space; show: emp nome; space; show: emp salario.

	"Atualizando empregado com determinada matrícula (U do CRUD)"
	Transcript cr; show: 'Atualizando um empregado...'.
	emp := departamento recuperarEmpregadoMatricula: 3.
	emp
		nome: 'Pedro Antonio';
		salario: 3600.
	Transcript  cr; show: emp matricula; space; show: emp nome; space; show: emp salario.
	departamento listarEmpregados.

	"Removendo  empregado (D do CRUD)"
	Transcript cr; show: 'Removendo um empregado...'.
	emp := departamento removerEmpregadoMatricula: 3.
	Transcript  cr; show: emp matricula; space; show: emp nome; space; show: emp salario.
	departamento listarEmpregados.
	! !

Object subclass: #Departamento
	instanceVariableNames: 'nome empregados'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'AdaptiveProgrammingBlogPost-Examples'!

!Departamento methodsFor: 'as yet unclassified' stamp: 'FranciscoAryMartins 6/16/2012 08:47'!
adicionarEmpregado: anEmpregado
	^ self empregados
		at: Empregado proximaMatricula
		put: anEmpregado! !

!Departamento methodsFor: 'as yet unclassified' stamp: 'FranciscoAryMartins 6/14/2012 21:25'!
initialize
	empregados := Dictionary  new.
	Empregado reinicieMatriculas .
	empregados
		at: Empregado proximaMatricula put: (Empregado matricula: Empregado ultimaMatricula  nome: 'João' salario: 5000);
				at: Empregado proximaMatricula put: (Empregado matricula: Empregado ultimaMatricula  nome: 'Maria' salario: 6000);
						at: Empregado proximaMatricula put: (Empregado matricula: Empregado ultimaMatricula  nome: 'Antonio' salario: 4000);
								at: Empregado proximaMatricula put: (Empregado matricula: Empregado ultimaMatricula  nome: 'Pedro' salario: 3000);
										at: Empregado proximaMatricula put: (Empregado matricula: Empregado ultimaMatricula  nome: 'Clara' salario: 4000)! !

!Departamento methodsFor: 'as yet unclassified' stamp: 'FranciscoAryMartins 6/16/2012 08:40'!
listarEmpregados
	Transcript
		cr;
		show: 'Empregados:'.
	self empregados
		do: [ :empregado |
			Transcript
				cr;
				tab;
				show: empregado matricula;
				space;
				show: empregado nome;
				space;
				show: empregado salario ]! !

!Departamento methodsFor: 'as yet unclassified' stamp: 'FranciscoAryMartins 6/16/2012 08:43'!
recuperarEmpregadoMatricula: aSmallInteger
	^ self empregados at: aSmallInteger! !

!Departamento methodsFor: 'as yet unclassified' stamp: 'FranciscoAryMartins 6/16/2012 08:48'!
removerEmpregadoMatricula: aSmallInteger
	^ self empregados removeKey: aSmallInteger! !

!Departamento methodsFor: 'accessing' stamp: 'FranciscoAryMartins 6/14/2012 07:24'!
empregados
	^ empregados! !

!Departamento methodsFor: 'accessing' stamp: 'FranciscoAryMartins 6/16/2012 09:23'!
nome
	^ nome! !

!Departamento methodsFor: 'accessing' stamp: 'FranciscoAryMartins 6/16/2012 09:23'!
nome: anObject
	nome := anObject! !

"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!

Departamento class
	instanceVariableNames: ''!

!Departamento class methodsFor: 'as yet unclassified' stamp: 'FranciscoAryMartins 6/16/2012 09:26'!
nome: aString
	^ self new nome: aString! !

Empregado initialize!

Continua na parte 2.

Anúncios

8 Respostas para “Adaptive Programming – Part 1

  1. Pingback: Adaptive Programming – Part 2 | Crab Log

  2. Pingback: Adaptive Programming – Part 4 | Crab Log

  3. Pingback: Adaptive Programming – Part 5 | Crab Log

  4. Pingback: Adaptive Programming – Part 6 | Crab Log

  5. Pingback: Adaptive Programming – Part 7 | Crab Log

  6. Pingback: Adaptive Programming – Part 8 | Crab Log

  7. Pingback: Adaptive Programming – Part 9 | Crab Log

  8. Pingback: Adaptive Programming – Part 11 | Crab Log

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s