Как я могу использовать шаблон команд в Java, как я делаю в C ++? - PullRequest
0 голосов
/ 15 февраля 2012

Недавно я работал над игрой на C ++, где реализовал шаблон команд для управления вводом с клавиатуры и управления космическим кораблем. При создании всех команд я передавал указатель космического корабля каждому конструктору, чтобы все команды работали с одним и тем же объектом космического корабля.

Этот шаблон имеет смысл в C ++, потому что вы можете передавать по ссылке, но в Java все передается по значению. Если бы я попытался реализовать то же самое в Java, как бы каждая команда указывала на один и тот же объект?

Во всех примерах, которые я видел для шаблона команд, используемого в Java, не имеет значения, являются ли переменные-члены каждой команды копиями или ссылками.

Ответы [ 4 ]

2 голосов
/ 15 февраля 2012

Java передается по значению во всех случаях, однако все «не примитивные» типы существуют как ссылки, поэтому для таких типов, как String, List или Spaceship, вы фактически передаете ссылку вокруг - проход по значению, потому что вы передаете ссылку по значению. Я всегда чувствовал, что это довольно запутанный способ выразить это.

Как бы то ни было, результатом этого является то, что если у вас есть (чрезвычайно упрощенный) класс команд

public class Command {
    private Spaceship spaceship;
    public Command(Spaceship ship) {
        spaceship = ship;
    }
}

И тогда вы делаете это:

Spaceship ship = new Spaceship();
List<Command> ships = new List<Command>();
for (int i = 0; i < 40; i++) {
    ships.add(new Command(ship));
}

тогда каждый из этих Command объектов имеет ссылку на такой же Spaceship объект. Передача ссылки по значению не вызывает копию объекта Spaceship, на который указывает ссылка. Однако, если бы вы передавали int s, вы бы действительно передавали копии.

Просто запомните различие между примитивными типами, такими как int, и типами объектов, такими как String, и все будет в порядке. Полезно помнить, что имена примитивных типов начинаются со строчной буквы (и никогда не могут быть определены пользователем), тогда как типы объектов начинаются с заглавной буквы. Все пользовательские типы являются типами объектов.

1 голос
/ 18 февраля 2012

Спасибо за вашу помощь, ребята. В конце концов, дело дошло до моего полного недопонимания о том, как работает Java. У меня сложилось впечатление (по какой-то странной причине), что создание Command и предоставление ему моего объекта означало, что он получил копию вместо ссылки на оригинал. Если бы это было так, то вызов .execute() в Command не оказал бы влияния на объект вне класса.

Тем не менее, я обнаружил, что это был не случай после создания небольшого теста:

Sprite.java:

public class Sprite {
    private int x;

    Sprite(int x) {
        this.x = x;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }
}

Command.java:

public interface Command {
    void execute();
}

MoveLeftCommand.java:

public class MoveLeftCommand implements Command {
    private Sprite s;

    MoveLeftCommand(Sprite s) {
        this.s = s;
    }

    public void execute() {
        s.setX(s.getX() - 1);
    }
}

MoveRightCommand.java:

public class MoveRightCommand implements Command {
    private Sprite s;

    MoveRightCommand(Sprite s) {
        this.s = s;
    }

    public void execute() {
        s.setX(s.getX() + 1);
    }
}

Test.java:

import java.lang.*;
import java.util.*;

public class Test
{    
    public static void main(String[] args) {
        Sprite mario = new Sprite(0);
        Command command = null;

        Map<String, Command> commands = new HashMap<String, Command>();
        commands.put("a", new MoveLeftCommand(mario));
        commands.put("d", new MoveRightCommand(mario));

        // Test...
        System.out.println(mario.getX()); // 0

        command = (Command) commands.get("a");

        command.execute();
        System.out.println(mario.getX()); // -1
        command.execute();
        System.out.println(mario.getX()); // -2

        command = (Command) commands.get("d");

        command.execute();
        System.out.println(mario.getX()); // -1
    }
}

Я правильно увидел 0 -1 -2 -1 в консоли, так же, как и в C ++.

0 голосов
/ 15 февраля 2012

Поскольку Java всегда действительно проходит по значению, вам нужно будет сделать что-то изменчивое, чтобы пройти через него (передать изменяемый объект).

Рассмотрим этот пример:

В этом независимом языке, который поддерживает передачу по ссылке

string commandArg = "";
new StringCommand().execute(commandArg);
Console.write(commandArg);

А в StringCommand.execute(string arg) у вас есть arg += "Hello World", тогда ваш вывод будет Hello World.

В Javaпросто передайте StringBuilder следующим образом:

StringBuilder sb = new StringBuilder();
new StringCommand().execute(sb);
System.out.println(sb.toString());

, где StringCommand.execute(StringBuilder sb) содержит:

sb.append("Hello World");

Ссылка StringBuilder передается по значению в StringCommand.execute.В двух словах, примитивные типы передаются по значению, а для объектов ссылка объекта передается по значению.

Надеюсь, это поможет.

0 голосов
/ 15 февраля 2012

В Java они передаются "по значению" ... Вы передаете реальный объект, который будет реализовывать метод execute. Совместное использование ссылок между различными объектами может быть достигнуто путем правильного проектирования ОО или внедрения метода поиска для общего атрибута. Пример шаблона Command был взят из wikipedia

/*the Command interface*/
public interface Command {
   void execute();
}


/*the Invoker class*/
import java.util.List;
import java.util.ArrayList;

public class Switch {

   private List<Command> history = new ArrayList<Command>();

   public Switch() {
    }

   public void storeAndExecute(Command cmd) {
      this.history.add(cmd); // optional 
      cmd.execute();        
   }

}


/*the Receiver class*/
public class Light {

   public Light() {
   }

   public void turnOn() {
      System.out.println("The light is on");
   }

   public void turnOff() {
      System.out.println("The light is off");
   }

}


/*the Command for turning on the light - ConcreteCommand #1*/
public class FlipUpCommand implements Command {

   private Light theLight;

   public FlipUpCommand(Light light) {
      this.theLight = light;
   }

   public void execute(){
      theLight.turnOn();
   }

}


 }
...