[RESOLVIDO]Implementação insegura no Command Pattern?

Boa noite gente. Eu to com uma dúvida no padrão Command que consiste na chamada dos métodos de configuração de uma classe. Por favor, observem os códigos abaixo e o comentário no código de Remote Loader (que tem o método main):

package headfirst.command.remote;

public class Stereo {
	String location;
    boolean on;
	
    public Stereo(String location) {
		this.location = location;
	}

	public void on() {
		on = true;
		System.out.println(location + " stereo is on");
	}

	public void off() {
		on = false;
		System.out.println(location + " stereo is off");
	}

	public void setCD() {
		if(on)
		System.out.println(location + " stereo is set for CD input");
	}

	public void setDVD() {
		if(on)
		System.out.println(location + " stereo is set for DVD input");
	}

	public void setRadio() {
		if(on)
		System.out.println(location + " stereo is set for Radio");
	}

	public void setVolume(int volume) {
		// code to set the volume
		// valid range: 1-11 (after all 11 is better than 10, right?)
		if(on)
		System.out.println(location + " Stereo volume set to " + volume);
	}
}

package headfirst.command.remote;

public class StereoOnWithCDCommand implements Command {
	Stereo stereo;
 
	public StereoOnWithCDCommand(Stereo stereo) {
		this.stereo = stereo;
	}
 
	public void execute() {
		stereo.on();
		stereo.setCD();
		stereo.setVolume(11);
	}
}

package headfirst.command.remote;

public class RemoteLoader {
 
	public static void main(String[] args) {
		RemoteControl remoteControl = new RemoteControl();
 
		Light livingRoomLight = new Light("Living Room");
		Light kitchenLight = new Light("Kitchen");
		CeilingFan ceilingFan= new CeilingFan("Living Room");
		GarageDoor garageDoor = new GarageDoor("");
		Stereo stereo = new Stereo("Living Room");
		
	    //stereo.setVolume(11);
	    // o método setVolume só terá o seu conteúdo executado se uma 
	    // variável 'on' estiver configurada como true.
	    
		/* Este é um grande problema: 
		 * stereo.on();
		 * stereo.setVolume(11);
		 * O problema está no fato de que só deveria ser possível ligar o aparelho
		 * através de um comando como StereoOnWithCDCommand() e não diretamente por main! 
		 */
	    
		LightOnCommand livingRoomLightOn = 
				new LightOnCommand(livingRoomLight);
		LightOffCommand livingRoomLightOff = 
				new LightOffCommand(livingRoomLight);
		LightOnCommand kitchenLightOn = 
				new LightOnCommand(kitchenLight);
		LightOffCommand kitchenLightOff = 
				new LightOffCommand(kitchenLight);
  
		CeilingFanOnCommand ceilingFanOn = 
				new CeilingFanOnCommand(ceilingFan);
		CeilingFanOffCommand ceilingFanOff = 
				new CeilingFanOffCommand(ceilingFan);
 
		GarageDoorUpCommand garageDoorUp =
				new GarageDoorUpCommand(garageDoor);
		GarageDoorDownCommand garageDoorDown =
				new GarageDoorDownCommand(garageDoor);
 
		StereoOnWithCDCommand stereoOnWithCD =
				new StereoOnWithCDCommand(stereo);
		StereoOffCommand  stereoOff =
				new StereoOffCommand(stereo);
 
		remoteControl.setCommand(0, livingRoomLightOn, livingRoomLightOff);
		remoteControl.setCommand(1, kitchenLightOn, kitchenLightOff);
		remoteControl.setCommand(2, ceilingFanOn, ceilingFanOff);
		remoteControl.setCommand(3, stereoOnWithCD, stereoOff);
  
		System.out.println(remoteControl);
 
		remoteControl.onButtonWasPushed(0);
		remoteControl.offButtonWasPushed(0);
		remoteControl.onButtonWasPushed(1);
		remoteControl.offButtonWasPushed(1);
		remoteControl.onButtonWasPushed(2);
		remoteControl.offButtonWasPushed(2);
		remoteControl.onButtonWasPushed(3);
		remoteControl.offButtonWasPushed(3);
	}
}

Esse código está violando o encapsulamento, não está ? se eu puder chamar o método on() e quaisquer outros métodos eu farei o programa ficar ambíguo né ? eu poderei chamar os métodos citados tanto através de main quanto pela classe de comando! :shock:

Se você está preocupado com isso, altere o modificador de acesso para protected ou default (package) e coloque os comandos e a classe Stereo juntos em um mesmo pacote. Coloque a classe Main em outro pacote.

Ótima solução! não pensei nisso, obrigado!

Na verdade não. O que está acontecendo é que Main está com muita “responsabilidade” pois ela cria os objetos e faz as operações sobre eles, o que você pode fazer é distribuir as responsabilidades passando a construção para uma fabrica (pode ser uma method factory) obtendo somente os comandos ou somente remoteControl.
Sempre que você criar um objeto, você terá acesso aos membros desse objeto, você quebra o encapsulamento quando algum atributo ou método não pode ser chamado fora da sua classe de maneira alguma e ele está declarado como público e não quando você não quer que um objeto chame o método do seu objeto.
Na verdade a classe Stereo pode ser utilizada fora do controle remoto por outra aplicação, no command você está apenas isolando quem faz a ação de quem recebe a ação.
Se você defini-la apenas dentro do pacote do controle remoto, ela so poderia ser utilizado por esse, ou seja ficaria dependente do controle remoto, se você quiser ter uma classe DJ e usar o stereo “manualmente” não poderia. Não poderia também usar com outro controle remoto diferente.
Se você não quer que um objeto tenha acesso somente a partes de do seu objeto você tem que criar uma interface diferente para ele e chama-lo a partir desse interface. Entendeu?

Essa sua observação foi muito interessante.

[quote=x@ndy]Se você não quer que um objeto tenha acesso somente a partes de do seu objeto você tem que criar uma interface diferente para ele e chama-lo a partir desse interface. Entendeu?
[/quote]

Não entendi muito bem. Se não for pedir muito, você poderia dar um exemplo em código ?

Você declara uma interface, digamos equipamento.

public interface Equipamento
{	
	public void on();
	public void off();  
}

A classe Stereo implementa a interface Equipamento.

public class Stereo implements Equipamento{
	...
  @Override
	public void on(){
    on = true;  
    System.out.println(location + " stereo is on");    
	}
	@Override
	public void off(){
		on = false;  
		System.out.println(location + " stereo is off");	
	}
  ...
}

Ai para criar a classe você faz:

Equipamento stereo = new Stereo("Living Room");
stereo.on();
stereo.off();

Assim você está "programando para uma interface e não para uma implementação"
Lembrando que isso não funciona para a classe StereoOnWithCDCommand pois ela precisa conhecer a classe, pois ela não apenas liga e desliga o aparelho mas aumenta o som também.
No caso da função main, ela está com muita responsabilidade, como disse antes.

Ah ta… essa diretriz… eu não implementei dessa forma porque no livro da Head First também não estava implementado assim. Contudo, ainda não terminei de estudar o cap. referente a esse padrão.

Agradeço muito por ter elucidado o conceito de encapsulamento.