誰かがコマンドパターンを簡単な例で説明できますか?インターネットで検索してみましたが、戸惑いました。
6 に答える
public interface Command {
public void execute();
}
ほとんどの場合、コマンドは不変であり、オンデマンドで実行される単一のアクションをカプセル化する命令が含まれています。実行時に命令を受け入れるRuntimeCommandもあるかもしれませんが、これは実装に応じてストラテジーまたはデコレーターパターンをさらに掘り下げます。
私自身の意見では、コマンドの不変のコンテキストに注意することが非常に重要だと思います。そうしないと、コマンドが提案になります。例えば:
public final class StopServerCommand implements Command {
private final Server server;
public StopServerCommand(Server server) { this.server = server; }
public void execute() {
if(server.isRunning()) server.stop();
}
}
public class Application {
//...
public void someMethod() {
stopButton.addActionListener(new ActionListener() {
public void actionPerformed(Event e) {
stopCommand.execute();
}
});
}
}
個人的にはコマンドはあまり好きではありません。私自身の経験では、これらはフレームワークのコールバックに対してのみうまく機能します。
それが役立つ場合は、比喩的な意味でコマンドを考えてください。訓練を受けた兵士は彼/彼女の指揮官によって命令を与えられ、要求に応じて兵士はこの命令を実行します。
コマンドパターンのワークフローは次のように考えることができます。
クライアントはInvokerを呼び出します=> InvokerはConcreteCommandを呼び出します => ConcreteCommandはReceiverメソッドを呼び出します。Receiverメソッドは抽象的なCommandメソッドを実装します。
dofactoryの記事からのUML図:
主な機能:
コマンドは、すべてのコマンドのインターフェイスを宣言し、コマンドの受信者に操作の実行を要求する単純なexecute()メソッドを提供します。
受信者は、要求を実行するために何をすべきかについての知識を持っています。
呼び出し元はコマンドを保持し、executeメソッドを呼び出すことでコマンドに要求を実行させることができます。
クライアントはConcreteCommandsを作成し、コマンドのレシーバーを設定します。
ConcreteCommandは、アクションとレシーバーの間のバインディングを定義します。
Invoker呼び出しが実行されると、ConcreteCommandはReceiverで1つ以上のアクションを実行します。
コードスニペット:
interface Command {
void execute();
}
interface Receiver {
public void switchOn();
}
class OnCommand implements Command{
private Receiver receiver;
public OnCommand(Receiver receiver){
this.receiver = receiver;
}
public void execute(){
receiver.switchOn();
}
}
class Invoker {
private Command command;
public Invoker(Command command){
this.command = command;
}
public void execute(){
this.command.execute();
}
}
class TV implements Receiver{
public void switchOn(){
System.out.println("Switch on from TV");
}
}
class DVDPlayer implements Receiver{
public void switchOn(){
System.out.println("Switch on from DVDPlayer");
}
}
public class CommandDemoEx{
public static void main(String args[]){
// On command for TV with same invoker
Receiver receiver = new TV();
Command onCommand = new OnCommand(receiver);
Invoker invoker = new Invoker(onCommand);
invoker.execute();
// On command for DVDPlayer with same invoker
receiver = new DVDPlayer();
onCommand = new OnCommand(receiver);
invoker = new Invoker(onCommand);
invoker.execute();
}
}
出力:
Switch on from TV
Switch on from DVDPlayer
説明:
この例では、
- コマンドインターフェイスは
execute()
メソッドを定義します。 - OnCommandは、メソッドを実装するConcreteCommandです。
execute()
- Receiverはインターフェースであり、実装者はメソッドの実装を提供する必要があります。
- TVとDVDPlayerは2種類のレシーバーであり、OnCommandのようにConcreteCommandに渡されます。
- 呼び出し元にはコマンドが含まれています。SenderをReceiverから切り離すための鍵です。
- InvokerはOnCommand- >を受信し、 Receiver(TV)を呼び出してこのコマンドを実行します。
Invokerを使用すると、TVとDVDPlayerの電源を入れることができます。このプログラムを拡張すると、TVとDVDPlayerの両方もオフになります。
コマンドパターンを使用して
コマンドの送信者と受信者を切り離します
コールバックメカニズムを実装する
元に戻すおよびやり直し機能を実装する
コマンドの履歴を維持する
このdzoneとjournaldevおよびWikipediaの記事をご覧ください。
ウィキペディアのページとしてのソースコードは、シンプルでわかりやすく、自明です。
この記事で引用されている手順に従うと、元に戻すとやり直しを実装できます
これは、実際のシナリオを使用して、コマンドパターンがどのように機能するかを理解するために使用できる別の例です。コマンドパターンを使用せずに飛行機である場所から別の場所に移動することはできません。
あなたが頻繁に旅行する場合、クライアントとしてあなたが気にするのはあなたがいる場所から別の場所に旅行することだけです。パイロットが飛行機をどのように飛ばすか、どの航空会社が利用可能になるかは気にしません..それを実際に予測することはできません。あなたが望むのは、空港を取得し、あなたを目的地に連れて行くように彼らに言うことです。
しかし、そうすると、空港当局へのあなたの命令は笑われるでしょう!チケットであるコマンドオブジェクトを提供する必要があります。どの航空会社や飛行機の種類を気にしない限り、飛行の準備ができたら、チケットコマンドオブジェクトを指定する必要があります。空港職員である発動者は、コマンド(チケット)を確認して検証し、偽物の場合は元に戻し、間違えた場合はやり直す必要があります(予約プロセス全体を実行する必要はありません)。 。
つまり、コマンドを呼び出すか実行するかを決定する前に、コマンド(チケット)を完全に制御する必要があります。これにより、航空会社(受信者)が実行(飛行機に乗って目的地に移動)できるようになります。
念のために言っておきますが、あなたのコマンド(あなたのチケット)にはすでに受信者(航空会社)の情報があり、それがないと空港当局はそもそもあなたのチケットの処理を開始しません。
空港当局は、彼らが取り組んでいるチケットの束を持っている可能性さえあります。彼らは私のチケットを遅らせて、私の後に来た誰かを通過させることを選ぶかもしれません(私の前に別の人のチケットを呼び出す)
コードは次のとおりです。
[TestClass]
public class Client
{
[TestMethod]
public void MyFlight_UsingCommandPattern()
{
var canadianAirline = new Airline();
AirlineTicket_Command myTicket = new MyAirLineTicket(canadianAirline);
var airportOfficials = new AirportOfficials_Invoker(myTicket);
airportOfficials.ProcessPasengerTicket_And_AllowPassengerToFly_Execute();
//assert not implemented
}
}
public class AirportOfficials_Invoker
{
private AirlineTicket_Command PassengerTicket { set; get; }
public AirportOfficials_Invoker(AirlineTicket_Command passengerTicket)
{
throw new NotImplementedException();
}
public void ProcessPasengerTicket_And_AllowPassengerToFly_Execute()
{
PassengerTicket.Execute();
}
}
public abstract class AirlineTicket_Command
{
protected Airline Airline { set; get; }
protected AirlineTicket_Command(Airline airline)
{
Airline = airline;
}
public abstract void Execute();
}
public class MyAirLineTicket : AirlineTicket_Command
{
public MyAirLineTicket(Airline airline)
: base(airline)
{
}
public override void Execute()
{
Airline.FlyPassenger_Action();
}
}
public class Airline
{
public void FlyPassenger_Action()
{
//this will contain all those stuffs of getting on the plane and flying you to your destination
}
}
私の要件は、それぞれ独自の例外フローを使用して一連のタスク(複数のユースケースで再利用できます)を実行することです。ここで論理的なコマンドパターンの実装が見つかりました。
コマンドによって実行される各アクション(通常/代替フロー)も例外ハンドラーになるようにしようとしています。ただし、コマンドが別のハンドラーに登録されている場合は、これを使用する必要があります。改善/修正のための提案は大歓迎です。
public interface Command {
Result run() throws Exception;
Command onException(ExceptionHandler handler);
}
public class Result {
}
public interface ExceptionHandler {
void handleException(Exception e);
}
public interface Action {
Result execute() throws Exception;
}
public class BasicCommand implements Command {
private Action action;
private ExceptionHandler handler;
public BasicCommand(Action action) {
if (action == null) {
throw new IllegalArgumentException("Action must not be null.");
}
this.action = action;
this.handler = (ExceptionHandler) this.action;
}
@Override
public Command onException(ExceptionHandler handler) {
if (handler != null) {
this.handler = handler;
}
return this;
}
public Result run() throws Exception {
Result result = null;
try {
result = action.execute();
} catch (Exception e) {
handler.handleException(e);
}
return result;
}
}
public class BasicAction implements Action, ExceptionHandler {
private Object[] params;
public BasicAction(Object... params) {
this.params = params;
}
@Override
public Result execute() throws Exception {
// TODO Auto-generated method stub
return null;
}
@Override
public void handleException(Exception e) {
// TODO exception translation: prepare unchecked application. exception and throw..
}
}
public class Main {
public static void main(String[] args) throws Exception {
int param1 = 10;
String param2 = "hello";
// command will use the action itself as an exception handler
Result result = new BasicCommand(new BasicAction(param1, param2)).run();
ExceptionHandler myHandler = new ExceptionHandler(){
@Override
public void handleException(Exception e) {
System.out.println("handled by external handler");
}
};
// command with an exception handler passed from outside.
Result result2 = new BasicCommand(new BasicAction(param1, param2)).onException(myHandler).run();
}
}
コマンドデザインパターンは、サービスの呼び出し元とサービスのプロバイダーを切り離します。一般的なシナリオでは、たとえば、Object A
のサービスが必要な場合はObject B
、を直接呼び出しB.requiredService()
ます。したがって、AはBを認識します。コマンドパターンでは、この結合は削除されます。ここに、画像と呼ばれる中間オブジェクトがありますCommand
。したがって、オブジェクトをA
処理Command
し、コマンドオブジェクトは実際のオブジェクトを処理しますB
。このアプローチには、アプリケーションの設計など、いくつかのアプリケーションがあります。
- コマンドをリクエストとして受け入れます。
- リクエストを元に戻します。
- リクエストリクエスト。
- マクロの作成。
- タスクエグゼキュータとタスクマネージャの作成。
コマンドデザインパターンの詳細については、https://en.wikipedia.org/wiki/Command_patternをお勧めします。他のすべてのデザインパターンについては、https://www.u-cursos.cl/usuario/.../mi_blog/r/head_first_design_patterns.pdfを参照してください。
ここで、もう1つの大まかな例えを示します。
ある日、神があなたを呼び求め、世界が危機に瀕していて、それを救うためにあなたの助けが必要であるとあなたに告げたとしましょう。さらにあなたを助けて、彼は彼が地球上にいくつかのスーパーヒーローを送ったとあなたに話します。
彼はおっとを知らないので、彼らをスーパーヒーローとは呼びません(インターフェースや抽象クラスを提供しません)が、元バットマン、スーパーマン、アイアンマン、そして彼らが持っている力の名前を教えてください。
彼はまた、将来、彼は将来そのような人をもっと送るかもしれないと言います。
今、彼はあなたに特別な責任を割り当てます->それらを制御し、そのためにあなたに7つの手を提供します。彼はそれぞれの手の仕事を自分で修正するのではなく、あなたに任せます。
スーパーヒーローの力を手動で制御できる柔軟性が必要であり、複数の条件で繰り返し物事を変更したくない場合。
あなたは修正中です。これでどうしますか?
コマンドパターンを入力します。
インターフェイスコマンドを作成し、その中にメソッドexecute()を1つだけ含めます。各スーパーヒーローのすべての力をカプセル化し、ex-IronManCreatesSuitCommandのコマンドを実装するようにします
これで、いつでも任意のコマンドに任意の手を割り当てることができるため、実行する必要のある特定のタスクを気にする手がなくなるため、柔軟性が大幅に向上します。コマンドを割り当てるだけです。それはexecuteを呼び出し、コマンドは他のすべてを処理します。
今、神が異なる力を持つ他のスーパーヒーローを送ったとしても、あなたは何をすべきかを知っています。