【问题标题】:Extracting named colors out of ColorFactory.java class by the lobo project?lobo 项目从 ColorFactory.java 类中提取命名颜色?
【发布时间】:2012-08-02 22:43:36
【问题描述】:

在新课程中我需要什么类型的过程来“读取”下面这个课程中的所有颜色?我使用该类为组件设置颜色并喜欢它。但是现在是时候让我的应用程序右键单击面板并更改背景颜色了。

在继续使用这些颜色时,什么会让我以正确的方向开始代码:ColorFactory.java

我刚刚开始考虑在类中添加一个新方法,例如:

public Map<String, Color> getColorMapValues(){

    return colorMap;
    //
}

【问题讨论】:

  • 哎呀,这个类的同步策略完全被破坏了。
  • @OlivierCroisier 您好 Oliver,我是 Java 新手,这是什么意思?谢谢你。乔乔
  • 这可能与这个类不需要同步的事实有关。就个人而言,我会让地图不可变,并且每次都应该重新解析颜色代码字符串 - 它甚至可能比使用地图更快!但即便如此,非阻塞地图也可以:所以偶尔你会解析并放置相同的颜色两次——这不是什么大问题。
  • 单例初始化方案被破坏。它使用一种称为“双重检查锁定”的技术,该技术在很久以前就被证明是错误的。我只需在其声明站点初始化单例: private final ColorFactory INSTANCE = new ColorFactory();并摆脱 getInstance() 方法中的所有同步。此外,颜色图初始化可以在构造函数或非静态初始化块中完成。
  • 请不要在此处复制 LGPL 代码;此站点使用不同的license

标签: java swing


【解决方案1】:

由于在这种情况下您可以控制源代码,因此最好重构类以满足您的需求而不是使用反射(这不仅成本高昂,而且根据您的安全策略被禁止)。

常用的技术是Holder pattern。有关更多信息,请参阅 Joshua Bloch 的 Effective Java(第 2 版)第 71 条。我们还可以通过使用线程安全的非阻塞结构来避免读取同步,即java.util.concurrent.ConcurrentHashMap

public class ColorFactory {
    private static class ColorFactoryHolder {
        // creates on instantiation of ColorFactoryHolder
        // synchronization is baked into the JVM, but won't be created until
        // the class is used, see JLS 12.4.1
        static final ColorFactory instance = new ColorMap();
    }
    public static ColorFactory getInstance() { return ColorFactoryHolder.instance; }
    // concurrent hash map - all operations are thread safe
    Map<String,Color> colormap = new ConcurrentHashMap<String,Color>();
    private final Object lock = new Object();
    private ColorFactory() {
        colormap.add("blue",new Color(0,0,255));
        // rest of colors here
    }
    public Color getColor(String spec) {
        if(colormap.containsKey(spec)) return colormap.get(spec);
        // don't synchronize externally - Bloch et al, item 70
        synchronized(lock) { 
            // double check idiom - not broken, as map is thread safe
            if(colormap.containsKey(spec)) return colormap.get(spec);
            Color color = parse(spec); // parse method can be extracted from old code
            colormap.put(spec,color);
            return color;
        }
    }
    private static Color parse(String spec) {
        // parse the color spec here
    }    
}

事实上,因为解析操作可能非常非常快(与同步相比),我们可以完全取消同步。所以我们最终可能会多次解析这个值——这并不是一个大问题,因为每次的结果都是一样的。见布洛赫等人。更多信息请参阅第 69 项。

public class ColorFactory {
    private static class ColorFactoryHolder {
        // same as above, snipped for brevity
    }
    public static ColorFactory getInstance() { return ColorFactoryHolder.instance; }
    // requires ConcurrentMap reference to get putIfAbsent(K,V) method
    ConcurrentMap<String,Color> colormap = new ConcurrentHashMap<String,Color>();
    // private final Object lock = new Object(); - removed
    private ColorFactory() {
        colormap.add("blue",new Color(0,0,255));
        // rest of colors here
    }
    public Color getColor(String spec) {
        Color result = colormap.get(spec);
        if(result == null) {         
            result = parse(spec); // may parse multiple times, but still
                                  // cheaper than synchronization
            colormap.putIfAbsent(spec,result);            
        }
        return result
    }
    private static Color parse(String spec) {
        // parse the color spec here
    }    
}

【讨论】:

    【解决方案2】:

    您可以通过反射访问颜色图。 我不保证以下代码,因为我手头没有我的 IDE,但这应该可以解决问题:

    private static Map<String, Color> getColorMap() throws Exception {
        Field colorMapField = ColorFactory.class.getDeclaredField("colorMap");
        colorMapField.setAccessible(true);
        return (Map<String, Color>) colorMapField.get(ColorFactory.getInstance());
    }
    

    然后你可以只查询返回的颜色图来按名称获取颜色:

    Map<String, Color> colorMap = getColorMap();
    Color yellow = colorMap.get("yellow");
    

    如果你想修改颜色图,只需用上面的方法检索它,put()“任何你喜欢的颜色”(是的,Pink Floyd 参考!),然后调用这个方法:

    public void setColorMap(Map<String, Color> colorMap) throws Exception {
        Field colorMapField = ColorFactory.class.getDeclaredField("colorMap");
        colorMapField.setAccessible(true);
        colorMapField.set(ColorFactory.getInstance(), colorMap);
    }
    

    请注意,这对于类的同步策略是非常危险的,但如果您不使用多个线程发送垃圾邮件,它应该可以工作。

    【讨论】:

    • 感谢您的好评。那么这里正确的导入是 import java.lang.reflect.Field; ?很多人出现了,但我想我正在关注这个。我会看看我能不能得到一些工作,我现在要创建一个右键单击操作来显示一个菜单;那是在我做任何其他事情之前!哈。 =)
    • 没错!此代码使用反射,因此您必须从 java.lang.reflect 包中导入一些类。
    • 我还在 getColorMap() 上添加了@SuppressWarnings("unchecked")。Eclipse 正在咆哮说有一个从 Object 到 Map 的 Unchecked cast。
    • 另请参阅 answer 到 JoJo 上一个关于此主题的问题。
    猜你喜欢
    • 2021-09-21
    • 2021-05-14
    • 1970-01-01
    • 2021-09-09
    • 2011-05-09
    • 2019-02-16
    • 2018-05-03
    • 2012-09-05
    • 2016-11-27
    相关资源
    最近更新 更多