设计模式使用List时如何避免instanceOf

时间:2018-09-03 08:04:04

标签: java design-patterns

想象一下,您有一个带有菜肴的菜单,每个菜肴应以多种语言提供(法语,英语,阿拉伯语...)。 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 :(得分:7)

我认为为每种语言创建一个单独的类不是一个好主意。毕竟,所有这些类都将具有完全相同的方法。

我将使用单个model.pic = System.IO.File.ReadAllBytes(Server.MapPath("~/image/seal.png")); 类,并且在Language类中,我将保留一个Dish(我将Map<Locale,Description>类重命名为以下类)之所以令人困惑,例如Language,因为它不代表某种语言,因此代表了某种语言的描述。

现在您可以拥有

Description
您的Description getDescription(Locale locale) 类中的

方法,它将以传递的Dish的语言返回对菜肴的描述。

您应该尽可能使用标准的JDK类(例如Locale而不是自定义类。

考虑了评论并同意其中的一些意见后,我建议将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中进行搜索更有效(ListO(1)中的查找时间与。HashMapO(n)中的查找时间。

答案 1 :(得分:2)

我认为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
 }

答案 2 :(得分:0)

要提出关于多语言解决方案的另一种方法,我建议看一下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"));

它将打印:

  

zh_CN
  你好世界

如果我取消注释第一行来模拟法语语言环境,则:

  

fr
  Bonjour le monde

这可用于获取不同的翻译。请注意,我从未将其用于数据,仅用于GUI(或固定值),但基于此问题,应该很可能将解决方案改编为使用数据库。

How to load resource bundle messages from DB in Java?

优势?您不必将所有翻译加载到内存中,只需运行该语言环境的翻译即可,因此只需加载真正需要的翻译。