명령 패턴 : 명령에 매개 변수를 전달하는 방법은 무엇입니까?
내 질문은 다음과 같은 추상화 (C # 코드)가있는 명령 패턴과 관련이 있습니다.
public interface ICommand
{
void Execute();
}
애플리케이션에서 엔티티를 삭제하는 것을 목표로하는 간단한 구체적인 명령을 보겠습니다. Person
예, 예.
나는거야 DeletePersonCommand
, 어떤 구현을 ICommand
. 이 명령은 메서드가 호출 Person
될 때 삭제하기 위해 매개 변수로 삭제 해야합니다 Execute
.
매개 변수화 된 명령을 관리하는 가장 좋은 방법은 무엇입니까? 명령을 실행하기 전에 매개 변수를 명령에 전달하는 방법은 무엇입니까?
생성자 또는 setter 주입 (또는 이와 동등한 방법)을 통해 매개 변수를 명령 개체와 연결해야합니다. 아마도 다음과 같습니다.
public class DeletePersonCommand: ICommand
{
private Person personToDelete;
public DeletePersonCommand(Person personToDelete)
{
this.personToDelete = personToDelete;
}
public void Execute()
{
doSomethingWith(personToDelete);
}
}
생성자 또는 setter를 통해 데이터를 전달하는 것은 작동하지만 명령 작성자가 명령에 필요한 데이터를 알아야합니다.
"컨텍스트"아이디어는 정말 좋은데, 한동안 그것을 활용 한 (내부) 프레임 워크를 작업하고있었습니다.
컨트롤러 (사용자와 상호 작용하는 UI 구성 요소, 사용자 명령을 해석하는 CLI, 들어오는 매개 변수 및 세션 데이터를 해석하는 서블릿 등)를 설정하여 사용 가능한 데이터에 대한 명명 된 액세스를 제공하는 경우 명령은 원하는 데이터를 직접 요청할 수 있습니다.
이런 설정이 허용하는 분리가 정말 마음에 듭니다. 다음과 같이 레이어링을 고려하십시오.
User Interface (GUI controls, CLI, etc)
|
[syncs with/gets data]
V
Controller / Presentation Model
| ^
[executes] |
V |
Commands --------> [gets data by name]
|
[updates]
V
Domain Model
이 작업을 "올바르게"수행하면 모든 유형의 사용자 인터페이스에서 동일한 명령 및 프리젠 테이션 모델을 사용할 수 있습니다.
한 단계 더 나아가 위의 "컨트롤러"는 매우 일반적입니다. UI 컨트롤 은 호출 할 명령 의 이름 만 알면됩니다. 그 (또는 컨트롤러)는 해당 명령을 만드는 방법이나 명령에 필요한 데이터에 대해 알 필요가 없습니다. 이것이 진정한 장점입니다.
예를 들어, 맵에서 실행할 명령의 이름을 보유 할 수 있습니다. 구성 요소가 "트리거"될 때마다 (일반적으로 actionPerformed) 컨트롤러는 명령 이름을 조회하고 인스턴스화하고 실행을 호출 한 다음 실행 취소 스택에 푸시합니다 (사용하는 경우).
몇 가지 옵션이 있습니다.
속성 또는 생성자로 매개 변수를 전달할 수 있습니다.
다른 옵션은 다음과 같습니다.
interface ICommand<T>
{
void Execute(T args);
}
그리고 모든 명령 매개 변수를 값 객체에 캡슐화합니다.
명령 개체를 만들 때 사람을 전달하십시오.
ICommand command = new DeletePersonCommand(person);
so that when you execute the command, it already knows everything that it needs to know.
class DeletePersonCommand : ICommand
{
private Person person;
public DeletePersonCommand(Person person)
{
this.person = person;
}
public void Execute()
{
RealDelete(person);
}
}
My implementation would be this (using the ICommand proposed by Juanma):
public class DeletePersonCommand: ICommand<Person>
{
public DeletePersonCommand(IPersonService personService)
{
this.personService = personService;
}
public void Execute(Person person)
{
this.personService.DeletePerson(person);
}
}
IPersonService could be an IPersonRepository, it depends in what "layer" your command is.
In this case, what we've done with our Command objects is to create a Context object which is essentially a map. The map contains name value pairs where the keys are constants and the values are parameters that are used by the Command implementations. Especially useful if you have a Chain of Commands where later commands depend on context changes from earlier commands.
So the actual method becomes
void execute(Context ctx);
In the constructor and stored as fields.
You will also want to eventually make your ICommands serializable for the undo stack or file persistence.
Based on the pattern in C#/WPF the ICommand Interface (System.Windows.Input.ICommand) is defined to take an object as a parameter on the Execute, as well as the CanExecute method.
interface ICommand
{
bool CanExecute(object parameter);
void Execute(object parameter);
}
This allows you to define your command as a static public field which is an instance of your custom command object that implements ICommand.
public static ICommand DeleteCommand = new DeleteCommandInstance();
In this way the relevant object, in your case a person, is passed in when execute is called. The Execute method can then cast the object and call the Delete() method.
public void Execute(object parameter)
{
person target = (person)parameter;
target.Delete();
}
You should create a CommandArgs object to contain the parameters you want to use. Inject the CommandArgs object using the constructor of the Command object.
DeletePersonCommand can have parameter in its constructor or methods . DeletePersonCommand will have the Execute() and in the execute can check attribute that will be passed by Getter/Setter previously the call of the Execute().
I would add any necessary arguments to the constructor of DeletePersonCommand
. Then, when Execute()
is called, those parameters passed into the object at construction time are used.
Have "Person" implement some sort of IDeletable interface, then make the command take whatever base class or interface your entities use. That way, you can make a DeleteCommand, which tries to cast the entity to an IDeletable, and if that works, call .Delete
public class DeleteCommand : ICommand
{
public void Execute(Entity entity)
{
IDeletable del = entity as IDeletable;
if (del != null) del.Delete();
}
}
참고URL : https://stackoverflow.com/questions/104918/command-pattern-how-to-pass-parameters-to-a-command
'Program Tip' 카테고리의 다른 글
생성자에 NULL 전송 (0) | 2020.12.04 |
---|---|
사전에서 비교할 때 키를 선택하기 위해 Python 3.5가 선택한 사항 (0) | 2020.12.04 |
정수를 십진수로 풀 수없는 이유는 무엇입니까? (0) | 2020.12.04 |
문자열이 try-catch없이 Long으로 구문 분석 가능한지 확인하십시오. (0) | 2020.12.04 |
XML 속성을 변경하는 방법 (0) | 2020.12.03 |