`
ajoo
  • 浏览: 453831 次
社区版块
存档分类
最新评论

动态properties转换

阅读更多
今天同事和我讨论他遇到的一个问题。具体要求是这样的,在运行时,我们会从模块G得到一个Map,这个Map里面都是一些字符串对,你可以理解成一个字典,有字符串的key和字符串的value。简短节说,就是
Map<String, String>

非常非常复杂深奥。

好,现在我们事先知道要从这个map里读取一些数据点,比如:id, name, sex等等。

对id,我们知道读出来的是int;对name,是string;对sex,应该对应一个叫Gender的enum类型。

这就涉及一个自动类型转换的问题。我们希望不用对每个数据点做手工类型转换。

另外一个需求,一些数据点是有缺省值的。比如name我们可以缺省为空字符串。
这样,如果map里面没有某个值,我们就看缺省值,如果有,就用这个缺省值,如果没有,就抛异常。

手工做的话,大概是这样:
String idValue = map.get("id");
if (idValue == null) {
  throw ...;
}
int id = Integer.parseInt(idValue);

String name = map.get("name");
if (name == null) {
  name = "";
}

String sexValue = map.get("sex");
if (sexValue == null) {
  throw ...;
}
Gender sex = Gender.valueOf(sexValue);
...


比较痛苦。于是做了一个动态代理:
public final class PropertyConverter<T> {
  private final Class<T> targetType;
  
  private PropertyConverter(Class<T> targetType) {...}

  public static <T> PropertyConverter<T> to(Class<T> targetType) {
    return new PropertyConverter<T>(targetType);
  }

  public T from(final Map<String, String> map) {
    return Proxy.newProxyInstance(
      new Class[]{targetType}, targetType.getClassLoader(), new InvocationHandler() {
        public Object invoke(Object proxy, Method method, Object[] args) {
          String value = map.get(method.getName());
          if (value == null) {
            Object defaultValue = method.getDefaultValue();
            if (defaultValue == null) {
              throw ...;
            }
            return defaultValue;
          }
          return convert(value, method.getReturnType());
        }
    });
  }
}


convert()函数是调用apache的ConvertUtilsBean做的,没什么说的。

那么,用法呢?

@interface Foo {
  int id();
  String name() default "";
  Gender sex();
}

Map<String, String> map = ...;
Foo foo = PropertyConverter.to(Foo.class).from(map);
foo.id();
foo.name();


这里面,对annotation的用法比较特别。不过不这么做,java也不提供一个简单并且类型安全的指定缺省值的方法。当然,如果你凑巧不需要缺省值,那么也不用annotation,直接用interface就好。
分享到:
评论
52 楼 hanjs 2008-12-04  
annotation 是啥?

没看懂,明天继续吧
51 楼 fjlyxx 2008-12-01  
多维持一个映射吧,不要把这么多逻辑都在一个方法里面进行处理.写多了就要考虑重构了.
维持一个类型转换映射多好. int--->xxx.xxx.xx.class 代理调用这类的接口实现方法就可以了.
50 楼 upyaya 2008-07-19  
恩,不错,method.getReturnType 都用上了。动态代理的价值全被体现了
49 楼 kimmking 2008-07-12  
apache beanutils中
dwr中
jsf myfaces中

都有基本类型的转换实现。


自己反射下属性(beanutils也可以帮忙),加自动转换类型。
lz的东西就好了。
48 楼 soleghost 2008-07-12  
签名,泛型,代理
都是自己平时很少用到的,运行一下,学习一下
47 楼 racnow 2008-06-16  
呵呵,精益求精啊,不过有个地方参数顺序好像错了:

public final class PropertyConverter<T> {
  private final Class<T> targetType;
  
  private PropertyConverter(Class<T> targetType) {...}

  public static <T> PropertyConverter<T> to(Class<T> targetType) {
    return new PropertyConverter<T>(targetType);
  }

  public T from(final Map<String, String> map) {
    return Proxy.newProxyInstance(
      new Class[]{targetType}, targetType.getClassLoader(), new InvocationHandler() {
        public Object invoke(Object proxy, Method method, Object[] args) {
          String value = map.get(method.getName());
          if (value == null) {
            Object defaultValue = method.getDefaultValue();
            if (defaultValue == null) {
              throw ...;
            }
            return defaultValue;
          }
          return convert(value, method.getReturnType());
        }
    });
  }
}

11行,我看api里是下面这样的啊
newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 


46 楼 yufu 2008-06-10  
感觉可以参考spring里面的类型转换吧,它好像用的是java的PropertyEditorSupport,感觉可以参考参考啊
45 楼 williamy 2008-06-10  
我觉得完全可以在这里问题上使用state模式,或者命令模式,或者使用aop来实现类型转换,那样楼主肯定觉得更优雅
44 楼 bloodrate 2008-06-10  
@interface Foo {  
  int id();  
  String name() default "";  
  Gender sex();  
}  
 
Map<String, String> map = ...;  
Foo foo = PropertyConverter.to(Foo.class).from(map);  
foo.id();  
foo.name(); 


没见过这种用法,学习
43 楼 williamy 2008-05-21  
搞了一堆代码去代替原来很清晰的一堆代码,有意思吗?
42 楼 williamy 2008-05-21  
不明白为什么需要这么处理,标准的简单问题复杂化
41 楼 murainwood 2008-05-19  
创意无限......
40 楼 ajoo 2008-05-13  
引用


我的方案也差不多啦,IDE 里面 rename 一下,然后也只要改一处字符串就好。


这个,没懂啊。怎么能只改一处字符串的?假设有10个test case都依赖Foo。
39 楼 Elminster 2008-05-13  
ajoo 写道
对,也不算什么大优势啦。其实测试驱动已经很多时候让类型安全变得不那么重要了。不过我总觉得,既然还是在用老土的java,还是应该注重静态类型安全和重构能力的。毕竟除了这个,java也没啥别的可以拿出来和别人比了。比如你的id这个key变成“uid”了,只要Eclipse里rename一下,世界就清净了,该改的ide都帮你改好了。总比跟着测试的红条条到处救火强吧?


我的方案也差不多啦,IDE 里面 rename 一下,然后也只要改一处字符串就好。

ajoo 写道
合辙就是用继承的么?可是这样不干净啊。一旦你不小心忘了重载某个method,行为又悄悄地依赖那个缺省的map了。而且如果你通通都override了,那还要那个碍眼的Map在那干啥?

不知道我们javaer喜欢final class的么?不知道我们喜欢用EasyMock而不是自己写子类的么?


行行,随你。这个话题再说下去就无聊了,就此打住。
另,收短信收短信。
38 楼 ajoo 2008-05-12  
引用

明白了。不过这个算不上什么优势吧?无非就是 id 对应的类型变了,测试代码若是忘了对应改变,你的方案可以在编译时报错,用 Map 的要等到跑测试的时候才发现对吧?考虑单元测试扮演的角色,不认为这是很大的区别,至少我的单元测试是直接放在“编译后动作”里面的。

对,也不算什么大优势啦。其实测试驱动已经很多时候让类型安全变得不那么重要了。不过我总觉得,既然还是在用老土的java,还是应该注重静态类型安全和重构能力的。毕竟除了这个,java也没啥别的可以拿出来和别人比了。比如你的id这个key变成“uid”了,只要Eclipse里rename一下,世界就清净了,该改的ide都帮你改好了。总比跟着测试的红条条到处救火强吧?
引用

嗯嗯,而且要实现你这个能力也不是不可以(先声明,我不喜欢这个玩法):

public final class Converter  // 用组合了,省得你们这些讲究繁文缛节的 Java 程序员呱噪 ……
{
    private final Map<String, String> _map;
    public Base(Map<String, String> map) { _map = map; }
    public T GetProperty<T>(Map<String, String> map, String key, T defaultValue)
    {  
        String value = _map.get(key);
        if (value == null) {
            if (defaultValue == null) {
                throw ...;
            }
            return defaultValue;
        }
        return convert(value);
    }
}

public abstract class Foo
{
    private final Converter _conv;
    public virtual int id() { return _conv.GetProperty<int>("id<script type="text/javascript" src="http://www.iteye.com/javascripts/tinymce/themes/advanced/langs/zh.js"></script><script type="text/javascript" src="http://www.iteye.com/javascripts/tinymce/plugins/javaeye/langs/zh.js"></script>", null); }
    public virtual String name() { return _conv.GetProperty<String>("name", ""); }
    public virtual Gender sex() { return _conv.GetProperty<Gender>("sex", null); }

    private final class Dummy : Foo {}
    public static Foo Instance(Map<String, String> map)
    {
        Foo foo = new Dummy();
        foo._conv = new Converter(map);
        return foo;
    }
}


测试代码要玩,可以自己继承一个 Foo 然后提供新的 id / name / sex 。


合辙就是用继承的么?可是这样不干净啊。一旦你不小心忘了重载某个method,行为又悄悄地依赖那个缺省的map了。而且如果你通通都override了,那还要那个碍眼的Map在那干啥?

不知道我们javaer喜欢final class的么?不知道我们喜欢用EasyMock而不是自己写子类的么?
37 楼 Elminster 2008-05-12  
ajoo 写道
引用
明白了,type erasure 的泛型总是出乎我意料地弱 &hellip;&hellip; 那么,在泛型函数内取 T 对应的 class 对象行不行?这个只需要编译器给泛型函数加一个隐藏的额外参数,应该是可以做的吧?

有一些办法的。比如Neal Gafter的Type token(通过一个子类来绕出来这个T的class),或者传统地传一个String.class进去,或者新的Reified Generics(vaporware)。


这么麻烦 …… 还不如 .net 的泛型啊,至少那个用 typeof(T) 就可以取出 T 对应的 Type 对象了。

ajoo 写道
引用
这个 &hellip;&hellip; 一般的方案难道不是直接给 FooClient 传一个 Foo 进去么?就算沦落到直接用 Map ,也木有把 map.put(...) 这种东西直接放在 FooClient 里的道理呀?


我们谈的是测试代码呀兄弟。

当然也是传一个Foo进去。问题是这里的Foo不是接口,而是一个行为依赖于Map的内容的具体类。你要模拟一个id=0的情况,不得在Foo里面的那个Map上下功夫?
比如:
@Mock Foo foo;
public void testIdIsZero() {
  expect(foo.id()).andReturn(0); //表示foo.id()会被调用,我们对这个调用返回0.
  replay();
  assertEquals("expect to get this value", new FooClient(foo).run());
}

要是Foo是个行为依赖于Map的类的话,你怎么鼓捣?


明白了。不过这个算不上什么优势吧?无非就是 id 对应的类型变了,测试代码若是忘了对应改变,你的方案可以在编译时报错,用 Map 的要等到跑测试的时候才发现对吧?考虑单元测试扮演的角色,不认为这是很大的区别,至少我的单元测试是直接放在“编译后动作”里面的。

嗯嗯,而且要实现你这个能力也不是不可以(先声明,我不喜欢这个玩法):

public final class Converter  // 用组合了,省得你们这些讲究繁文缛节的 Java 程序员呱噪 ……
{
    private final Map<String, String> _map;
    public Base(Map<String, String> map) { _map = map; }
    public T GetProperty<T>(Map<String, String> map, String key, T defaultValue)
    {  
        String value = _map.get(key);
        if (value == null) {
            if (defaultValue == null) {
                throw ...;
            }
            return defaultValue;
        }
        return convert(value);
    }
}

public abstract class Foo
{
    private final Converter _conv;
    public virtual int id() { return _conv.GetProperty<int>("id", null); }
    public virtual String name() { return _conv.GetProperty<String>("name", ""); }
    public virtual Gender sex() { return _conv.GetProperty<Gender>("sex", null); }

    private final class Dummy : Foo {}
    public static Foo Instance(Map<String, String> map)
    {
        Foo foo = new Dummy();
        foo._conv = new Converter(map);
        return foo;
    }
}


测试代码要玩,可以自己继承一个 Foo 然后提供新的 id / name / sex 。

ajoo 写道
引用
你小子庸俗化了呀!当年那只特立独行的猪捏?猪肉涨价被人宰了吃了?

我管这叫&ldquo;成熟&rdquo;。


唉,世界上的猪,又少了一头 ……
36 楼 ajoo 2008-05-11  
维护有两种不同的问题:
一是代码非常赃,非常乱,copy-paste漫天飞,字符串到处用。这种东西往往原理比较简单,大致看一看就知道“为什么”这么乱了,无非就是原来那位很懒,只知道最naive的解决方法。但是真正要把问题清理好,维护成功,仍然是个噩梦。具体这个问题,你可以写得到处都是if-else,到处都是字符串,也最直观容易懂。也可以用elm的方法,做一层封装,易懂程度差一些,但是代码干净了,不过测试起来,mock起Foo, Bar来,还是要隔着那个Map的逻辑来隔靴搔痒,仍然到处飞字符串。

二是系统看上去简洁干净,井井有条,所有的开关,布线都魔术式地隐藏起来,要让普通人维护日常整洁粉容易,但是需要很有经验,水平较高的工程师来理解这一切魔法到底如何实现的。这种“高科技”,往往你一旦学会了解了它内部机理,一切就迎刃而解。就比如这个东西,是用了比较不寻常的技巧,但是你只要懂动态代理,看明白了这个技巧,一切不过都是顺理成章。

看维护者到底是一个体力劳动者,还是脑力劳动者了。
35 楼 hb123_net 2008-05-11  
如果真是这么个需求,这么来搞,我看接手维护的人要疯掉了,过度了吧
34 楼 pojo 2008-05-11  
那么维护可能就是大问题了。形式与内容不再是皮与肉的关系,而是衣服和身体的关系。它们可以在不同的时空产生,改变和销毁。
33 楼 ajoo 2008-05-10  
pojo 写道
有一点不明白。你如何保证形式(interface Foo)与内容(Map)的类型一致?

当然是手工的了,在写这个interface或者@interface的时候。总有一个地方要把类型不安全的字符串映射到安全的java函数上去。但是,就一次,不论在生产代码还是测试代码中。

相关推荐

    excel与properties文件相互转换

    在IT行业中,数据管理和配置管理经常涉及到不同格式的文件转换,比如Excel和Properties文件。Excel是一种广泛用于数据处理和分析的电子表格工具,而Properties文件则常见于Java开发中,用于存储配置信息。两者之间的...

    Java .properties中文资源批量转换工具和用法

    为了解决这个问题,开发人员通常需要使用特定的工具来批量处理和转换`.properties`文件,以确保它们正确地支持中文字符。 本话题将介绍一种Java `.properties`中文资源批量转换工具的使用方法,帮助开发者高效地...

    properties与json互转的简单实现,支持XDiamond批量增加

    此文件可以批量的将properties转换成json字符串并输出为txt文件. 使用:PropToJson.toJSONString(resourseArr,true) resourseArr为资源文件地址数组 true为对应txt文件到本地; false为不输出到本地

    properties资源文件转换插件

    "properties资源文件转换插件"是一款专为Eclipse开发环境设计的工具,旨在简化对.properties配置文件进行中文转换的过程。通常,在Java开发中,.properties文件用于存储应用程序的配置信息,如国际化(i18n)的文本。...

    java_properties编辑器支持自动转换ascii

    java_properties编辑器支持自动转换ascii,方便快捷无需再用JDK自动工具转换。

    properties文件文中乱码问题解决.doc

    使用 native2ascii 工具可以将 Properties 文件转换为 Unicode 编码字符的文件,然后将转换后的文件内容替换为原始文件的内容。这样可以解决 Properties 文件文中乱码问题。 Properties 文件是一种资源文件,用于...

    读取properties文件返回map

    4. **将Properties转换为Map** `Properties`类本身就是一个`Map`,可以直接通过`entrySet()`方法访问键值对。但如果你需要一个标准的`Map, String&gt;`,可以进行转换: ```java Map, String&gt; map = new HashMap(); ...

    Properties Editor for Eclipse

    2. **自动编码检测和转换**:该插件能够自动检测`.properties`文件的编码,并允许用户选择不同的字符集进行转换,确保国际化文本的正确显示。 3. **支持Unicode和特殊字符**:由于国际化的需要,Properties Editor...

    java properties文件中文转化

    博主分享了一个实用的方法,即通过Unicode转换工具来处理含有中文字符的properties文件,以确保数据的正确性。 首先,我们需要理解Java Properties文件的编码问题。默认情况下,Java Properties类在读取和写入文件...

    java解析Properties配置文件为对象Bean

    利用java的反射解析Properties文件转成对象 /** * 解析properties文件为对象 * @param * @param propPath * @param cls * @return * @throws InstantiationException * @throws ...

    js读取properties文件

    有时候,将.properties文件转换为JSON格式会更方便,因为JSON与JavaScript天生兼容。可以使用在线工具或者自定义脚本完成转换,然后在JavaScript中使用`JSON.parse()`来解析。 7. **错误处理** 在处理文件读取和...

    Properties Editor(在eclipse编辑struts2 资源文件的插件 由unicode转换成中文)

    Properties Editor是一款专为Eclipse开发的插件,主要用于编辑Struts2框架中的资源文件,尤其在处理Unicode编码和中文字符转换方面提供了极大的便利。这款插件解决了开发人员在使用Eclipse进行国际化(i18n)开发时...

    properties-yml.jar

    yml properties相互转换 工具 java小工具 命令:java -jar properties-yml.jar application.properties 生成application.properties 命令:java -jar properties-yml.jar application.yml 生成application.yml

    读取properties返回map并写入文件

    以下是一个简单的示例,展示如何使用Properties类加载文件并将其内容转换为Map: ```java import java.io.*; import java.util.*; public class PropertyHandler { public static Map, String&gt; loadProperties...

    解决Properties写中文乱码

    这个问题通常由两个主要因素引起:编码格式不匹配和读写过程中的编码转换不当。 首先,Properties文件默认使用ISO-8859-1编码,这是Java的标准编码。如果在文件中直接写入中文,而编辑器或程序以UTF-8或其他支持...

    propertiesToJSON:将Java .properties文件转换为JSON(使用JavaScript)

    将Java .properties文件转换为JSON(使用JavaScript)。 函数propertiesToJSON接受一个字符串并返回一个JavaScript对象。 读取node的本地文件: const fs = require ( "fs" ) ; const path = require ( "path" ) ;...

    java操作properties方法

    1. **预处理**:在编辑`.properties`文件时,可以使用Java提供的`native2ascii`工具或Eclipse的属性编辑器将中文字符转换为对应的UTF-8编码表示形式。 2. **程序中转换**:在代码中,可以使用`new String...

    myeclipse插件properties插件

    然而,尽管Eclipse本身已经提供了对`properties`文件的基本编辑功能,但有时开发者可能需要更高级的功能,如语法高亮、自动完成、编码转换等,这就是`myeclipse插件properties插件`的作用。 这个插件专为解决`...

    eclipse离线安装properties插件包,5.3.3版本

    2. **编码检测与转换**:插件可以自动检测文件的编码,并允许用户转换文件的编码格式,以适应不同的项目需求。 3. **语法高亮**:通过颜色区分关键字和普通文本,提高代码可读性。 4. **自动完成**:提供属性键的...

    写入properties文件时间并且读出时间

    - 如果需要将读取出的时间字符串转换回日期对象,可以使用`SimpleDateFormat.parse`或`java.time.format.DateTimeFormatter.parse`方法。例如: ```java try { Date lastUpdateTime = new SimpleDateFormat(...

Global site tag (gtag.js) - Google Analytics