намерение
Инкапсулирует запрос в виде объекта, позволяя вам параметризовать клиентов с различными запросами, ставить в очередь или регистрировать запросы и поддерживать операции, которые нельзя отменить.
Рождение шаблона команд
[Продукт]: Брат-разработчик, приходите на работу, нам нужно спроектировать пульт дистанционного управления, основная функция - несколько кнопок, но может потребоваться управление многими различными марками оборудования, вы можете подумать об этом ~
【Развитие】: Кнопка? Его не существует, это просто просьба ко мне, Босс, помогите подумать, как адаптироваться к разным маркам техники?
[БОСС]: Мы не можем просто полагаться на нас в адаптации устройства. Это результат сотрудничества. Поскольку вы также сказали, что кнопка — это просто запрос, вы можете рассмотреть возможность использования командного режима для инкапсуляции запроса в виде объекта. Мы берем на себя инициативу связать исполнителей, соответствующих разным брендам, понятно?
【Развитие】: А? О, понял, понял (я понял!)
Основной код HeadFirst
"родительский интерфейс"
public interface Command {
void execute();
}
"Инкапсулирует запрос как объект"
public class LightOnCommand implements Command {
Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
}
"API запрос-ответ"
public class Light {
/***
* on方法
*/
public void on() {
System.out.println("On...");
}
/***
* off方法
*/
public void off() {
System.out.println("Off...");
}
}
"код вызывающего абонента"
public class SimpleRemoteControl {
Command slot;
public SimpleRemoteControl() {}
public void setCommand(Command command) {
slot = command;
}
public void buttonWasPressed() {
slot.execute();
}
}
//******************************************
public static void main(String[] args) {
SimpleRemoteControl remote = new SimpleRemoteControl();
Light light = new Light();
LightOnCommand lightOn = new LightOnCommand(light);
remote.setCommand(lightOn);
remote.buttonWasPressed();
LightOffCommand lightOff = new LightOffCommand(light);
remote.setCommand(lightOff);
remote.buttonWasPressed();
}
Идея оформления командного режима:
- Команда объявляет интерфейс для команды
- ConcreteCommand конкретное действие | команда
- Клиент клиентский запрос
- Команда и получатель привязки Invoker
- Receiver Receiver умеет реализовывать операции, связанные с выполнением запроса, любой класс может быть приемником
Суть кода: абстрагировать запрос в команду, отделить получателя выполнения команды от самой команды и передать его стороннему классу (Invoker) для управления им для достижения цели разделения.
Попробуйте инкапсулировать простых джедаев в командном режиме
Советы по протоколу Redis
Redis — это REmote Dictionary Server (служба удаленного словаря);
Спецификация протокола Redis — это протокол сериализации Redis (протокол сериализации Redis).
RESP — это протокол связи, который ранее использовался клиентом и сервером Redis;
Особенности RESP: простая реализация, быстрый синтаксический анализ, хорошая читабельность.
Соглашение заключается в следующем:
Клиент отправляет команды на сервер в указанном формате
/***
* set key value 协议翻译如下:
*
* * 3 -> 表示以下有几组命令
*
* $ 3 -> 表示命令长度是3
* SET
*
* $6 -> 表示长度是6
* keykey
*
* $5 -> 表示长度是5
* value
*
* 完整即:
* * 3
* $ 3
* SET
* $6
* keykey
* $5
* value
*/
❝Что касается протокола RESP, связанного с Redis, я дам специальное объяснение в следующих статьях~
❞
Инкапсулирует команду Get
public class GetCommand implements Command {
private GetReceiver receiver;
private String arg;
@Override
public void execute() {
receiver.doCommand(this.arg);
}
public GetCommand(GetReceiver receiver, String arg) {
this.receiver = receiver;
this.arg = arg;
}
}
Инкапсулирует приемник Get
public class GetReceiver {
OutputStream write;
InputStream read;
public void doCommand (String arg) {
String[] strings = arg.split(" ");
String key = strings[0];
byte[] bytes;
try {
String sb = "*2" + SPILT +
"$3" + SPILT +
"GET" + SPILT +
"$" + key.getBytes().length + SPILT +
key + SPILT;
write.write(sb.getBytes());
bytes = new byte[1024];
read.read(bytes);
System.out.println("Result: " + new String(bytes));
} catch (IOException e) {
e.printStackTrace();
}
}
public GetReceiver(OutputStream write, InputStream read) {
this.write = write;
this.read = read;
}
final String SPILT = "\r\n";
}
Инвокер пакета
Используя стек для хранения команд можно хорошо контролировать изменения команд и т.д.
public class Invoker {
private final Stack<Command> commands;
public Invoker() {
commands = new Stack<>();
}
public void addCommand(Command command) {
commands.push(command);
}
public void undoCommand() {
if (!commands.empty()) {
commands.pop();
}
}
public void execute() {
while (!commands.empty()) {
Command command = commands.pop();
command.execute();
}
}
}
тестовый класс
/***
* 简易Jedis代码, 利用栈存储命令(可根据需求更改数据结构)
*
* 推荐阅读顺序:
* @see Command
* @see GetCommand | SetCommand
* @see GetReceiver | SetReceiver
* @see Invoker
*/
public static void main(String[] args) throws IOException {
// 初始化Socket流
Socket socket = new Socket("127.0.0.1", 6379);
OutputStream write = socket.getOutputStream();
InputStream read = socket.getInputStream();
Invoker invoker = new Invoker();
// 初始化Get | Set任务执行者
GetReceiver getReceiver = new GetReceiver(write, read);
SetReceiver setReceiver = new SetReceiver(write, read);
// 测试get命令
invoker.addCommand(new GetCommand(getReceiver, "key"));
// 测试set命令
invoker.addCommand(new SetCommand(setReceiver, "key xixixi"));
// 测试get命令
invoker.addCommand(new GetCommand(getReceiver, "key"));
// 测试get命令
invoker.addCommand(new GetCommand(getReceiver, "key"));
// 测试撤销上一个命令 -> 输出四次则测试失败,三次则成功
invoker.undoCommand();
invoker.execute();
}
Выходной результат:
/***
* Result: $4
* test
*
* Result: +OK
*
* Result: $6
* xixixi
*/
// 测试成功~
❝Объем кода немного мал. Если вам нужно увидеть подробности, перейдите по соответствующей ссылке кода внизу ~
❞
какие сценарии применяются
Режим командного метода можно использовать в следующих ситуациях:
- Необходимо абстрагировать действие, которое необходимо выполнить, чтобы параметризовать объект
- Задавайте, ставьте в очередь и выполняйте запросы в разные моменты времени
- Поддержка отмены операции
Код / практическое применение в жизни
В повседневной жизни существует понятие порядка, почему мы делаем заказ, а официант или другой персонал полностью понимает наше намерение? Именно потому, что мы построили команду по правилам, которые они составили, поэтому нет необходимости в послойной коммуникации в процессе взаимодействия, что удобно для развязки.
UML-диаграмма
Принципы дизайна, которым нужно следовать
- Программирование для интерфейса, а не для реализации
- Стремитесь к слабосвязанному дизайну взаимодействующих объектов
- Классы должны быть открыты для расширения и закрыты для модификации
Связанные ссылки на код
- С учетом кейсов в двух классических книгах "HeadFirst" и "GOF"
- Предоставляет дружественное руководство по чтению