шаблоны проектирования, как избежать instanceOf при использовании List - PullRequest
0 голосов
/ 03 сентября 2018

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

class Dish { 
     List<Language> languages
     void addLanguage(Language lg){...}
}
class Language { getDescription(){}}

class French extends Language{}

class Menu {List<Dish> dishes }

Как мне избежать использования instance of, когда требуется описание определенного языка для этого блюда?

Должен ли я определить для каждого языка метод get в классе блюда: getFrench (), getArabic (), ..?

Или я должен сохранить его в списке и проверить, например, французский, зациклив список, а затем вызвать getDescription() для этого list object<language>?

Или есть способ для более полиморфного подхода?

Ответы [ 3 ]

0 голосов
/ 03 сентября 2018

Я думаю, что Language не имеет меню Description. Я бы разработал его таким образом.

 class Dish { 
       List<Description> descriptions;
       void addDescription(Description description){...}

       Description getDescription(Language language){
           // iterate over descriptions and select the appropriate
           // or use a Map<Language, Description>
           Optional<Description> applicableDescription =
                          descriptions.stream()
                          .filter(d -> d.getLanguage().isApplicable(language)).findFirst();
           return applicableDescription.orElseGet(this::getDefaultDescription);
       }

       private Description getDefaultDescription(){
           return descriptions.isEmpty() ? null : descriptions.get(0); 
       }
 }


 class Description {
     Language language;
     String shortDescription;
     String title;
     // and so on... 

     public boolean isApplicable(Language language){
         return this.language.equals(language);
         // This logic can be more sophisticated. E.g. If an Italian
         // wants to get a menu description, but no italian translation is
         // available, it might be useful to choose spanish. But then it
         // would be better to return a similarity, like 0.1 to 1.0, instead of true/false.
     }
 }

 class Language {
      String name; // like en, de, it, fr, etc. Maybe an enum is better or just using
                   // java.util.Locale
 }
0 голосов
/ 03 сентября 2018

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

Например, давайте определим в файл:

Первый для английского:

data_en.properties
hello=Hello World

И для французской версии:

data_fr.properties
hello=Bonjour le monde

Теперь давайте загрузим ресурсы и напечатаем значение:

//Locale.setDefault(Locale.FRENCH);
System.out.println(Locale.getDefault());
ResourceBundle rb = ResourceBundle.getBundle("data");
System.out.println(rb.getString("hello"));

Будет напечатано:

en_US
Привет, мир

И если я откомментирую первую строку для эмуляции французского языка:

фр
Bonjour le monde

Это может быть использовано для получения другого перевода. Обратите внимание, что я никогда не использовал его для данных, только для графического интерфейса пользователя (или фиксированных значений), но исходя из этого вопроса, вполне возможно адаптировать решение для использования базы данных.

Как загрузить сообщения пакета ресурсов из БД в Java?

Преимущество? вам не нужно загружать все переводы в память, только один для локали, с которой вы работаете, поэтому только тот, который вам действительно нужен.

0 голосов
/ 03 сентября 2018

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

Я бы использовал один класс Language, а внутри класса Dish я оставлю Map<Locale,Description> (я бы переименовал класс Language во что-то менее запутанное, например Description, так как он не представляет язык, он представляет описание на каком-то языке).

Теперь вы можете иметь

Description getDescription(Locale locale)

метод в вашем Dish классе, который будет возвращать описание блюда на языке переданного Locale.

Вы должны по возможности использовать стандартные классы JDK, такие как java.util.Locale, вместо пользовательских классов.

После рассмотрения комментариев и согласования некоторых из них я предлагаю исключить Map из класса Description.

class Dish 
{ 
      Description description = new Description ();

      void addDescription(Locale locale, String text)
      {
          description.addText(locale,text);
      }

      String getDescription(Locale locale) 
      {
          return description.getText(locale);
      }
 }

class Description 
{
    Map<Locale,String> descriptions = new HashMap<>();

    public void addText(Locale locale,String text) 
    {
        descriptions.put(locale,text);
    }

    public void getText(Locale locale) 
    {
        return descriptions.get(locale);
    }
}

Следует также отметить, что поиск описания конкретного языка в Map более эффективен, чем его поиск в List (O(1) времени поиска в HashMap против O(n) времени поиска в List).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...