-
Notifications
You must be signed in to change notification settings - Fork 0
커맨드 패턴 (Command pattern)
명령 자체를 객체의 형태로 캡슐화하는 패턴
Unity를 알고있다면?
Unity의 Event Trigger를 생각하면 된다.
형광등 불을 키는 버튼을 만들것이다.

간단하다. 해당 다이어그램대로
버튼을 형광등과 곧바로 연결하면 될 것이다.
Invoker 클래스
public class LightOnButton
{
private Light light;
//켤 불을 등록한다.
public void setLight(Light light)
{
this.light= light;
}
// 버튼이 눌리면 현재 등록된 light의 LightOn 메서드를 호출한다.
public void pressed()
{
light.LightOn();
}
}
Receiver 클래스
public class Light
{
public void LightOn()
{
Console.WriteLine("형광등을 킴");
}
public void LightOff()
{
Console.WriteLine("형광등을 끔");
}
}
메인 클래스
class Program
{
static void Main(string[] args)
{
Light light = new Light();
LightOnButton buttonLigtOn = new LightOnButton();
buttonLigtOn.setLight(light); // 형광등 등록
buttonLigtOn.pressed(); // 형광등을 킴
}
}
해당 코드는 불을 켜는데 문제는 없을 것이다.
하지만, 다시 불을 끄고 싶다면? TV, 선풍기 등 새로운 제품을 추가하게 된다면?
- 버튼은
누른다
라는 한 가지 기능밖에 존재하지 않는다. - 그렇다면, 제품마다 On버튼, Off버튼을 따로 만들어야 한다.
-
누른다
라는 한 가지의 동일한 기능을 가진 버튼들이 무수히 많아 진다.
∴결국, 제품 제어를 원할 때마다, 매번 다른 버튼들을 조작해야 한다. 매우 비 효율 적이지 않을 수 없다.[1]
눈치 챘는지 모르겠지만, 에초에 위의 코드는 🛑DIP위반이다.
상위수준의 개체인 버튼이 불을 켜기 위해, 하위수준의 개체인 형광등에게 의존하고 있기 때문이다.
[1]이것이 DIP원칙을 지켜야 하는 이유이다.[↻]
그렇다면 어떻게 고쳐야 할까?
해당 문제는 커맨드 패턴을 적용시키면, 간단히 해결된다.
Command 인터페이스
public interface Command
{
public void execute();
}
Receiver 클래스
public class Light
{
public void LightOn()
{
Console.WriteLine("형광등을 킴");
}
public void LightOff()
{
Console.WriteLine("형광등을 끔");
}
}
public class TV
{
public void TVOn()
{
Console.WriteLine("TV를 킴");
}
public void TVOff()
{
Console.WriteLine("TV를 끔");
}
}
Concreate Command 클래스
// 불을 켜는 LightOnCommand 클래스
public class LightOnCommand : Command
{
private Light light;
public LightOnCommand(Light light)
{
this.light = light;
}
// Command 인터페이스의 execute 메서드
public void execute()
{
light.LightOn();
}
}
// 불을 끄는 LightOffCommand 클래스
public class LightOffCommand : Command
{
private Light light;
public LightOffCommand(Light light)
{
this.light = light;
}
// Command 인터페이스의 execute 메서드
public void execute()
{
light.LightOff();
}
}
// TV 켜는 TVOnCommand 클래스
public class TVOnCommand : Command
{
private TV tv;
public TVOnCommand(TV tv)
{
this.tv = tv;
}
// Command 인터페이스의 execute 메서드
public void execute()
{
tv.TVOn();
}
}
// TV 끄는 TVOffCommand 클래스
public class TVOffCommand : Command
{
private TV tv;
public TVOffCommand(TV tv)
{
this.tv = tv;
}
// Command 인터페이스의 execute 메서드
public void execute()
{
tv.TVOff();
}
}
Invoker 클래스
public class Button
{
private Command[] theCommand;
// 버튼의 명령을 theCommand변수에 등록한다.
public void setCommand(Command[] newCommand)
{
this.theCommand = newCommand;
}
// 버튼이 눌리면 현재 등록된 Command의 execute 메서드를 호출한다.
public void pressed()
{
for (int i = 0; i < theCommand.Length; i++)
theCommand[i].execute();
}
}
메인 클래스
class Program
{
static void Main(string[] args)
{
Light light = new Light();
Command lightOnCommand = new LightOnCommand(light);
Command lightOffCommand = new LightOffCommand(light);
TV tv = new TV();
Command tvOnCommand = new TVOnCommand(tv);
Command tvOffCommand = new TVOffCommand(tv);
Button button = new Button();
Command[] commands = new Command[] { lightOnCommand };
button.setCommand(commands); // 커맨드목록 등록
button.pressed(); // 형광등을 킴
commands = new Command[] { tvOnCommand };
button.setCommand(commands); // 커맨드목록 등록
button.pressed(); // TV를 킴
commands = new Command[] { lightOffCommand, tvOffCommand };
button.setCommand(commands); // 커맨드목록 등록
button.pressed(); // TV와 형광등을 동시에 끔
}
}
이제, 하나의 버튼으로 여러가지 명령을 수행할 수 있게 되었다.
또한, 버튼의 명령들을 배열변수로 등록함으로서, 한번에 여러 명령을 동시에 수행할 수 도 있다.
만약, 버튼 클래스에서 theCommand변수의 getter를 구현하면, 다른 버튼에게 명령들을 넘기는 것 또한 가능할 것이다.
public class Switch
{
private Command[] onCommand;
private Command[] offCommand;
// 스위치의 명령을 onCommand와 offCommand 변수에 등록한다.
public void setCommand(Command[] onCommand, Command[] offCommand)
{
this.onCommand = onCommand;
this.offCommand = offCommand;
}
// 스위치를 켜면 현재 등록된 onCommand들의 execute 메서드를 호출한다.
public void switchOn()
{
for (int i = 0; i < onCommand.Length; i++)
onCommand[i].execute();
}
// 스위치를 끄면 현재 등록된 offCommand들의 execute 메서드를 호출한다.
public void switchOff()
{
for (int i = 0; i < offCommand.Length; i++)
offCommand[i].execute();
}
}
게다가, 커맨드를 사용하면 다른 기능제품들도 손쉽게 만들어 낼 수 있다.
여러 출처를 바탕으로 최대한 오류를 범하지 않도록 작성하였으나, 이 페이지를 작성하는 저 또한 해당 학문을 공부하는 학생입니다.
∴해당 페이지의 정보를 맹신하지 마시길 바랍니다.
-
생성 패턴
-
구조 패턴
- 데코레이터 패턴 (Decorator pattern)
- 어댑터 패턴 (Adapter pattern)
- 퍼사드 패턴 (Facade pattern)
- 프록시 패턴 (Proxy pattern)
- 이터레이터, 컴포지트 패턴 (Iterator, Composite pattern)
-
동작 패턴
- 옵저버 패턴 (Observer pattern)
- 템플릿 메소드 패턴 (Template pattern)
- 커맨드 패턴 (Command pattern)
- 스트래티지 패턴 (Strategy pattern)
- 스테이트 패턴 (State pattern)
-
기타 패턴
- 컴파운드 패턴 (Compound pattern)
- 메소드 체이닝 (Method chaining)
- Dispose 패턴 (Dispose pattern)