JavaRush /Blog Java /Random-MS /Mari kita laksanakan Corak Perintah untuk bekerja dengan ...

Mari kita laksanakan Corak Perintah untuk bekerja dengan bot. (Bahagian 1) - "Projek Java dari A hingga Z"

Diterbitkan dalam kumpulan
Hello semua, kawan-kawan yang dikasihi. Hari ini kami akan melaksanakan templat (templat ialah corak, dalam konteks kami ia adalah perkara yang sama) reka bentuk Perintah untuk keperluan kami. Menggunakan templat ini, kami akan bekerja dengan mudah dan betul dengan pemprosesan arahan bot kami. "Projek Java dari A hingga Z": Melaksanakan Corak Perintah untuk bekerja dengan bot.  Bahagian 1 - 1
Rakan, adakah anda suka projek Javarush Telegram Bot ? Jangan malas: berikan bintang . Dengan cara ini ia akan menjadi jelas bahawa dia menarik, dan ia akan menjadi lebih menyenangkan untuk membangunkannya!
Sebagai permulaan, adalah baik untuk bercakap tentang jenis corak ini - Perintah. Tetapi jika saya melakukan ini, artikel itu akan menjadi sangat besar dan menyusahkan. Oleh itu, saya memilih bahan untuk belajar sendiri:
  1. Ini adalah artikel saya dari 4 tahun yang lalu. Saya menulisnya semasa saya masih muda, jadi jangan menilai terlalu keras.
  2. Video seorang warga Sweden yang sangat emosional dan interaktif di YouTube. Saya sangat mengesyorkannya. Dia bercakap dengan indah, bahasa Inggerisnya jelas dan boleh difahami. Dan secara umum, dia mempunyai video tentang corak reka bentuk lain.
  3. Dalam ulasan artikel saya, seseorang Nullptr35 mengesyorkan video ini .
Ini sepatutnya cukup untuk melibatkan diri dalam topik dan berada di halaman yang sama dengan saya. Nah, mereka yang biasa dengan corak reka bentuk ini boleh melangkau dan meneruskan dengan selamat.

Kami menulis JRTB-3

Semuanya sama seperti sebelumnya:
  1. Kami mengemas kini cawangan utama.
  2. Berdasarkan cawangan utama yang dikemas kini, kami mencipta JRTB-3 baharu .
  3. Mari kita laksanakan corak.
  4. Kami membuat komitmen baharu yang menerangkan kerja yang dilakukan.
  5. Kami membuat permintaan tarik, semaknya dan jika semuanya ok, kami menggabungkan kerja kami.
Saya tidak akan menunjukkan mata 1-2: Saya menerangkannya dengan teliti dalam artikel sebelumnya, jadi mari kita teruskan untuk melaksanakan templat. Mengapa templat ini sesuai untuk kami? Ya, kerana setiap kali kami melaksanakan arahan, kami akan pergi ke kaedah onUpdateReceived(Update update) , dan bergantung pada arahan kami akan melaksanakan logik yang berbeza. Tanpa corak ini, kita akan mempunyai pelbagai penyataan if-else if. Sesuatu seperti ini:
if (message.startsWith("/start")) {
   doStartCommand();
} else if(message.startsWith("/stop")) {
   doStopCommand();
} else if(message.startsWith("/addUser")) {
   doAddUserCommand();
}
...
else if(message.startsWith("/makeMeHappy")) {
   doMakeMeHappyCommand();
}
Lebih-lebih lagi, di mana terdapat elipsis, mungkin terdapat beberapa dozen lagi pasukan. Dan bagaimana untuk mengendalikan ini secara normal? Bagaimana untuk menyokong? Sukar dan sukar. Ini bermakna pilihan ini tidak sesuai dengan kami. Ia sepatutnya kelihatan seperti ini:
if (message.startsWith(COMMAND_PREFIX)) {
   String commandIdentifier = message.split(" ")[0].toLowerCase();
   commandContainer.getCommand(commandIdentifier, userName).execute(update);
} else {
   commandContainer.getCommand(NO.getCommand(), userName).execute(update);
}
Itu sahaja! Dan tidak kira berapa banyak arahan yang kita tambah, bahagian kod ini akan kekal tidak berubah. Apa yang dia buat? Yang pertama jika memastikan bahawa mesej bermula dengan awalan arahan "/". Jika ini berlaku, maka kami memilih barisan ke ruang pertama dan mencari arahan yang sepadan dalam CommandContainer; sebaik sahaja kami menemuinya, kami menjalankan arahan itu. Dan itu sahaja...) Jika anda mempunyai keinginan dan masa, anda boleh melaksanakan kerja dengan pasukan, pertama dalam satu kelas sekaligus, dengan banyak syarat dan semua itu, dan kemudian menggunakan templat. Anda akan nampak perbezaannya. Alangkah indahnya! Mula-mula, mari buat pakej di sebelah pakej bot, yang akan dipanggil command . "Projek Java dari A hingga Z": Melaksanakan Corak Perintah untuk bekerja dengan bot.  Bahagian 1 - 2Dan sudah dalam pakej ini akan ada semua kelas yang berkaitan dengan pelaksanaan arahan. Kami memerlukan satu antara muka untuk bekerja dengan arahan. Untuk kes ini, mari buatnya:
package com.github.javarushcommunity.jrtb.command;

import org.telegram.telegrambots.meta.api.objects.Update;

/**
* Command interface for handling telegram-bot commands.
*/
public interface Command {

   /**
    * Main method, which is executing command logic.
    *
    * @param update provided {@link Update} object with all the needed data for command.
    */
   void execute(Update update);
}
Pada ketika ini, kami tidak perlu melaksanakan operasi terbalik arahan, jadi kami akan melangkau kaedah ini (unexecute). Dalam kaedah pelaksanaan, objek Kemas kini datang sebagai hujah - betul-betul yang datang kepada kaedah utama kami dalam bot. Objek ini akan mengandungi semua yang diperlukan untuk memproses arahan. Seterusnya, kami akan menambah enum yang akan menyimpan nilai arahan (mula, berhenti, dan seterusnya). Mengapa kita memerlukan ini? Supaya kita hanya mempunyai satu sumber kebenaran untuk nama pasukan. Kami juga menciptanya dalam pakej arahan kami . Mari kita panggil CommandName :
package com.github.javarushcommunity.jrtb.command;

/**
* Enumeration for {@link Command}'s.
*/
public enum CommandName {

   START("/start"),
   STOP("/stop");

   private final String commandName;

   CommandName(String commandName) {
       this.commandName = commandName;
   }

   public String getCommandName() {
       return commandName;
   }

}
Kami juga memerlukan perkhidmatan yang akan menghantar mesej melalui bot. Untuk melakukan ini, kami akan mencipta pakej perkhidmatan di sebelah pakej arahan , yang mana kami akan menambah semua perkhidmatan yang diperlukan. Di sini adalah bernilai memberi tumpuan kepada apa yang saya maksudkan dengan perkataan perkhidmatan dalam kes ini. Jika kita mempertimbangkan aplikasi, ia sering dibahagikan kepada beberapa lapisan: lapisan untuk bekerja dengan titik akhir - pengawal, lapisan logik perniagaan - perkhidmatan, dan lapisan untuk bekerja dengan pangkalan data - repositori. Oleh itu, dalam kes kami, perkhidmatan ialah kelas yang melaksanakan beberapa jenis logik perniagaan. Bagaimana untuk membuat perkhidmatan dengan betul? Pertama, buat antara muka untuknya dan pelaksanaan. Tambahkan pelaksanaan menggunakan anotasi `@Service` pada Konteks Aplikasi aplikasi SpringBoot kami dan, jika perlu, ketatkannya menggunakan anotasi `@Autowired`. Oleh itu, kami mencipta antara muka SendBotMessageService (dalam menamakan perkhidmatan mereka biasanya menambah Perkhidmatan pada akhir nama):
package com.github.javarushcommunity.jrtb.service;

/**
* Service for sending messages via telegram-bot.
*/
public interface SendBotMessageService {

   /**
    * Send message via telegram bot.
    *
    * @param chatId provided chatId in which messages would be sent.
    * @param message provided message to be sent.
    */
   void sendMessage(String chatId, String message);
}
Seterusnya, kami mencipta pelaksanaannya:
package com.github.javarushcommunity.jrtb.service;

import com.github.javarushcommunity.jrtb.bot.JavarushTelegramBot;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.exceptions.TelegramApiException;

/**
* Implementation of {@link SendBotMessageService} interface.
*/
@Service
public class SendBotMessageServiceImpl implements SendBotMessageService {

   private final JavarushTelegramBot javarushBot;

   @Autowired
   public SendBotMessageServiceImpl(JavarushTelegramBot javarushBot) {
       this.javarushBot = javarushBot;
   }

   @Override
   public void sendMessage(String chatId, String message) {
       SendMessage sendMessage = new SendMessage();
       sendMessage.setChatId(chatId);
       sendMessage.enableHtml(true);
       sendMessage.setText(message);

       try {
           javarushBot.execute(sendMessage);
       } catch (TelegramApiException e) {
           //todo add logging to the project.
           e.printStackTrace();
       }
   }
}
Inilah rupa pelaksanaannya. Keajaiban yang paling penting ialah tempat pereka dicipta. Menggunakan anotasi @Autowired pada pembina, SpringBoot akan mencari objek kelas ini dalam Konteks Aplikasinya. Dan dia sudah berada di sana. Ia berfungsi seperti ini: dalam aplikasi kami, di mana-mana sahaja kami boleh mengakses bot dan melakukan sesuatu. Dan perkhidmatan ini bertanggungjawab untuk menghantar mesej. Supaya kami tidak menulis sesuatu seperti ini setiap kali di setiap tempat:
SendMessage sendMessage = new SendMessage();
sendMessage.setChatId(chatId);
sendMessage.setText(message);

try {
   javarushBot.execute(sendMessage);
} catch (TelegramApiException e) {
   //todo add logging to the project.
   e.printStackTrace();
}
Kami telah memindahkan logik ini ke dalam kelas yang berasingan dan akan menggunakannya jika perlu. Sekarang kita perlu melaksanakan tiga arahan: StartCommand, StopCommand dan UnknownCommand. Kami memerlukannya supaya kami mempunyai sesuatu untuk mengisi bekas kami untuk arahan. Buat masa ini, teks akan menjadi kering dan tidak bermaklumat; untuk tujuan tugas ini, ini tidak begitu penting. Jadi, StartCommand:
package com.github.javarushcommunity.jrtb.command;

import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
import org.telegram.telegrambots.meta.api.objects.Update;

/**
* Start {@link Command}.
*/
public class StartCommand implements Command {

   private final SendBotMessageService sendBotMessageService;

   public final static String START_MESSAGE = "Привет. Я Javarush Telegram Bot. Я помогу тебе быть в курсе последних " +
           "статей тех авторов, котрые тебе интересны. Я еще маленький и только учусь.";

   // Здесь не добавляем сервис через получение из Application Context.
   // Потому что если это сделать так, то будет циклическая зависимость, которая
   // ломает работу applications.
   public StartCommand(SendBotMessageService sendBotMessageService) {
       this.sendBotMessageService = sendBotMessageService;
   }

   @Override
   public void execute(Update update) {
       sendBotMessageService.sendMessage(update.getMessage().getChatId().toString(), START_MESSAGE);
   }
}
Sila baca komen dengan teliti sebelum pereka bentuk. Kebergantungan bulat ( circular dependency ) boleh berlaku disebabkan oleh seni bina yang tidak betul. Dalam kes kami, kami akan memastikan semuanya berfungsi dan betul. Objek sebenar daripada Konteks Aplikasi akan ditambah apabila mencipta arahan yang sudah ada dalam CommandContainer. StopCommand:
package com.github.javarushcommunity.jrtb.command;

import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
import org.telegram.telegrambots.meta.api.objects.Update;

/**
* Stop {@link Command}.
*/
public class StopCommand implements Command {

   private final SendBotMessageService sendBotMessageService;

   public static final String STOP_MESSAGE = "Деактивировал все ваши подписки \uD83D\uDE1F.";

   public StopCommand(SendBotMessageService sendBotMessageService) {
       this.sendBotMessageService = sendBotMessageService;
   }

   @Override
   public void execute(Update update) {
       sendBotMessageService.sendMessage(update.getMessage().getChatId().toString(), STOP_MESSAGE);
   }
}
Dan UnknownCommand. Mengapa kita memerlukannya? Bagi kami, ini adalah arahan penting yang akan bertindak balas jika kami tidak dapat mencari arahan yang diberikan kepada kami. Kami juga memerlukan NoCommand dan HelpCommand.
  • NoCommand - akan bertanggungjawab untuk situasi apabila mesej tidak bermula dengan arahan sama sekali;
  • HelpCommand akan menjadi panduan untuk pengguna, sejenis dokumentasi.
Mari tambahkan HelpCommand:
package com.github.javarushcommunity.jrtb.command;

import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
import org.telegram.telegrambots.meta.api.objects.Update;

import static com.github.javarushcommunity.jrtb.command.CommandName.*;

/**
* Help {@link Command}.
*/
public class HelpCommand implements Command {

   private final SendBotMessageService sendBotMessageService;

   public static final String HELP_MESSAGE = String.format("✨<b>Дотупные команды</b>✨\n\n"

                   + "<b>Начать\\закончить работу с ботом</b>\n"
                   + "%s - начать работу со мной\n"
                   + "%s - приостановить работу со мной\n\n"
                   + "%s - получить помощь в работе со мной\n",
           START.getCommandName(), STOP.getCommandName(), HELP.getCommandName());

   public HelpCommand(SendBotMessageService sendBotMessageService) {
       this.sendBotMessageService = sendBotMessageService;
   }

   @Override
   public void execute(Update update) {
       sendBotMessageService.sendMessage(update.getMessage().getChatId().toString(), HELP_MESSAGE);
   }
}
NoCommand:
package com.github.javarushcommunity.jrtb.command;

import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
import org.telegram.telegrambots.meta.api.objects.Update;

/**
* No {@link Command}.
*/
public class NoCommand implements Command {

   private final SendBotMessageService sendBotMessageService;

   public static final String NO_MESSAGE = "Я поддерживаю команды, начинающиеся со слеша(/).\n"
           + "Whatбы посмотреть список команд введите /help";

   public NoCommand(SendBotMessageService sendBotMessageService) {
       this.sendBotMessageService = sendBotMessageService;
   }

   @Override
   public void execute(Update update) {
       sendBotMessageService.sendMessage(update.getMessage().getChatId().toString(), NO_MESSAGE);
   }
}
Dan untuk tugas ini masih terdapat UnknownCommand:
package com.github.javarushcommunity.jrtb.command;

import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
import org.telegram.telegrambots.meta.api.objects.Update;

/**
* Unknown {@link Command}.
*/
public class UnknownCommand implements Command {

   public static final String UNKNOWN_MESSAGE = "Не понимаю вас \uD83D\uDE1F, напишите /help чтобы узнать что я понимаю.";

   private final SendBotMessageService sendBotMessageService;

   public UnknownCommand(SendBotMessageService sendBotMessageService) {
       this.sendBotMessageService = sendBotMessageService;
   }

   @Override
   public void execute(Update update) {
       sendBotMessageService.sendMessage(update.getMessage().getChatId().toString(), UNKNOWN_MESSAGE);
   }
}
Seterusnya, mari tambah bekas untuk arahan kami. Ia akan menyimpan objek arahan kami dan atas permintaan kami mengharapkan untuk menerima arahan yang diperlukan. Mari kita panggil CommandContainer :
package com.github.javarushcommunity.jrtb.command;

import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
import com.google.common.collect.ImmutableMap;

import static com.github.javarushcommunity.jrtb.command.CommandName.*;

/**
* Container of the {@link Command}s, which are using for handling telegram commands.
*/
public class CommandContainer {

   private final ImmutableMap<String, Command> commandMap;
   private final Command unknownCommand;

   public CommandContainer(SendBotMessageService sendBotMessageService) {

       commandMap = ImmutableMap.<string, command="">builder()
               .put(START.getCommandName(), new StartCommand(sendBotMessageService))
               .put(STOP.getCommandName(), new StopCommand(sendBotMessageService))
               .put(HELP.getCommandName(), new HelpCommand(sendBotMessageService))
               .put(NO.getCommandName(), new NoCommand(sendBotMessageService))
               .build();

       unknownCommand = new UnknownCommand(sendBotMessageService);
   }

   public Command retrieveCommand(String commandIdentifier) {
       return commandMap.getOrDefault(commandIdentifier, unknownCommand);
   }

}
Seperti yang anda lihat, semuanya dilakukan dengan mudah. Kami mempunyai peta yang tidak boleh diubah dengan kunci dalam bentuk nilai arahan dan nilai dalam bentuk objek arahan jenis Perintah. Dalam pembina, kami mengisi peta tidak berubah sekali dan mengaksesnya sepanjang operasi aplikasi. Kaedah utama dan satu-satunya untuk bekerja dengan bekas ialah retrieveCommand(String commandIdentifier) ​​​​. Terdapat perintah yang dipanggil UnknownCommand, yang bertanggungjawab untuk kes apabila kita tidak dapat mencari arahan yang sepadan. Kini kami bersedia untuk melaksanakan bekas ke dalam kelas bot kami - dalam JavaRushTelegramBot: Inilah rupa kelas bot kami sekarang:
package com.github.javarushcommunity.jrtb.bot;

import com.github.javarushcommunity.jrtb.command.CommandContainer;
import com.github.javarushcommunity.jrtb.service.SendBotMessageServiceImpl;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.telegram.telegrambots.bots.TelegramLongPollingBot;
import org.telegram.telegrambots.meta.api.objects.Update;

import static com.github.javarushcommunity.jrtb.command.CommandName.NO;

/**
* Telegram bot for Javarush Community from Javarush community.
*/
@Component
public class JavarushTelegramBot extends TelegramLongPollingBot {

   public static String COMMAND_PREFIX = "/";

   @Value("${bot.username}")
   private String username;

   @Value("${bot.token}")
   private String token;

   private final CommandContainer commandContainer;

   public JavarushTelegramBot() {
       this.commandContainer = new CommandContainer(new SendBotMessageServiceImpl(this));
   }

   @Override
   public void onUpdateReceived(Update update) {
       if (update.hasMessage() && update.getMessage().hasText()) {
           String message = update.getMessage().getText().trim();
           if (message.startsWith(COMMAND_PREFIX)) {
               String commandIdentifier = message.split(" ")[0].toLowerCase();

               commandContainer.retrieveCommand(commandIdentifier).execute(update);
           } else {
               commandContainer.retrieveCommand(NO.getCommandName()).execute(update);
           }
       }
   }

   @Override
   public String getBotUsername() {
       return username;
   }

   @Override
   public String getBotToken() {
       return token;
   }
}
Dan itu sahaja, perubahan pada kod telah selesai. Bagaimanakah saya boleh menyemak ini? Anda perlu melancarkan bot dan pastikan semuanya berfungsi. Untuk melakukan ini, saya mengemas kini token dalam application.properties, tetapkan yang betul dan melancarkan aplikasi dalam kelas JavarushTelegramBotApplication: "Projek Java dari A hingga Z": Melaksanakan Corak Perintah untuk bekerja dengan bot.  Bahagian 1 - 3Sekarang kita perlu menyemak bahawa arahan berfungsi seperti yang diharapkan. Saya menyemaknya langkah demi langkah:
  • StopCommand;
  • StartCommand;
  • HelpCommand;
  • NoCommand;
  • Arahan yang tidak diketahui.
Inilah yang berlaku: "Projek Java dari A hingga Z": Melaksanakan Corak Perintah untuk bekerja dengan bot.  Bahagian 1 - 4Bot berfungsi tepat seperti yang kami jangkakan. Bersambung melalui pautan .

Senarai semua bahan dalam siri ini adalah pada permulaan artikel ini.

Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION