论坛首页 Java企业应用论坛

动态properties转换

浏览 31988 次
该帖已经被评为精华帖
作者 正文
   发表时间:2008-05-09  
Elminster 写道
ajoo 写道
主要是这个默认值,<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>除了annotation想不到别的好办法解决。

别的人也给了些建议,比如自己写一个Property<T>类,在这个类里面实现类型转换和默认值处理。不过都要求比较多的代码,而且用起来客户代码还要依赖于一个不是很容易mock的Property类。反而不如动态代理简单干净,侵入性基本没有。用起来可以注射进一个接口,而不是PropertyConverter类,这样最容易测试。

你要用随便啦。反正都贴在blog里了,随便拷贝。


我比较笨,想不出来为什么写一个类负责转换会要求更多的代码,如下(木有写过 java,语法错误莫怪):

public abstract class Base
{
    private final Map<String, String> _map;
    public Base(Map<String, String> map) { _map = map; }
    public T GetProperty<T>(String key, T defaultValue)
    {
        String value = _map.get(key);
        if (value == null) {
            if (defaultValue == null) {
                throw ...;
            }
            return defaultValue;
        }
        return convert(value);
    }
}

public final class Foo : Base
{
    public Foo(Map<String, String> map) : base(map) {}

    public int id() { return Base.GetProperty<int>("id", null); }
    public String name() { return Base.GetProperty<String>("name", ""); }
    Gender sex() { return Base.GetProperty<Gender>("sex", null); }  
}
   
Map<String, String> map = ...;
Foo foo = new Foo(map);
foo.id();
foo.name();


当然啦,这个看起来木有你原来那个帅,不过相对而言要好懂一些吧?而且处理方法名和属性名不同的情况也不用额外多费手脚了。


其<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>实很多人还是更偏向你这个写法的 。我的那个也不见得就比这个好。只不过它更“有趣”,更值得聊。

有几个考虑我来说一下哈:

1. Base.GetProperty<String>()是不行滴,因为erasure,你要写成getProperty(String.class, "name", ""),写起来不是那么爽快 。
2. 这样客户代码要依赖Base这个具体类,而不是一个简单地接口。测试就不够方便了,类型安全没了,重构也比较麻烦了。比如你要测试id为0的情况,不能直接expect(foo.id()).andReturn(0),而是要map.put("id", "0")。当然,你可以让Foo实现一个接口,不过这样就比较麻烦了,又要写类,又要写接口。
3。不是你这个方法的问题。在java里面,一般应该是组合而不是继承Base。

0 请登录后投票
   发表时间:2008-05-09  
quaff 写道
这种代码差不多所有人都会写,还不如GetProperty做成静态方法然后引入,还去掉了必须继承Base这个限制


是啊,这种代码所有人都会写,这难道不好么?
GetProperty 做成静态方法也可以,这样每个 Foo 需要自己内部保存 map 的实例,多费一点功夫。在我看来,这个交换不太合算。
0 请登录后投票
   发表时间:2008-05-09  
Elminster 写道
quaff 写道
这种代码差不多所有人都会写,还不如GetProperty做成静态方法然后引入,还去掉了必须继承Base这个限制


是啊,这种代码所有人都会写,这难道不好么?
GetProperty 做成静态方法也可以,这样每个 Foo 需要自己内部保存 map 的实例,多费一点功夫。在我看来,这个交换不太合算。

静态方法不太好,最好是一个对象,只不过不是继承Base,而是包含一个MapConverter。

不过,问题还是一样,就是测试不方便,测试代码不够类型安全,也不容易重构。
0 请登录后投票
   发表时间:2008-05-09  
Elminster 写道
quaff 写道
这种代码差不多所有人都会写,还不如GetProperty做成静态方法然后引入,还去掉了必须继承Base这个限制


是啊,这种代码所有人都会写,这难道不好么?
GetProperty 做成静态方法也可以,这样每个 Foo 需要自己内部保存 map 的实例,多费一点功夫。在我看来,这个交换不太合算。

我的意思是大家都会的就没什么必要拿来讨论了,还有java不能多重继承,所以最好不要弄出个Base来
0 请登录后投票
   发表时间:2008-05-09  
ajoo 写道
其实很多人还是更偏向你这个写法的 。我的那个也不见得就比这个好。只不过它更“有趣”,更值得聊。

有几个考虑我来说一下哈:

1. Base.GetProperty<String>()是不行滴,因为erasure,你要写成getProperty(String.class, "name", ""),写起来不是那么爽快 。
2. 这样客户代码要依赖Base这个具体类,而不是一个简单地接口。测试就不够方便了,类型安全没了,重构也比较麻烦了。比如你要测试id为0的情况,不能直接expect(foo.id()).andReturn(0),而是要map.put("id", "0")。当然,你可以让Foo实现一个接口,不过这样就比较麻烦了,又要写类,又要写接口。
3。不是你这个方法的问题。在java里面,一般应该是组合而不是继承Base。


1. 木有想到 …… java 的泛型这么弱?是所有的泛型函数调用都要这样,还是 String 比较特殊?
2. 原来你的方案,也要依赖你这个泛型类 PropertyConverter<T> 吧?这和依赖 Base 区别在哪里?而且我没有发现哪里损失了类型安全,说来听听?
3. 哪个面向对象语言里面都是组合优于继承,OO 原则么。不过我觉得这种地方用继承更直截了当,目前也看不见什么更复杂的东西。将来若是这里真的变复杂了,也可以重构。
0 请登录后投票
   发表时间:2008-05-09  
引用

1. 木有想到 …… java 的泛型这么弱?是所有的泛型函数调用都要这样,还是 String 比较特殊?

所有的类型都是。因为这里你需要的不是一个编译时类型,而是运行时的。

引用
2. 原来你的方案,也要依赖你这个泛型类 PropertyConverter<T> 吧?这和依赖 Base 区别在哪里?而且我没有发现哪里损失了类型安全,说来听听?

不要。我会注射一个Function<Map, T>进去,(或者任何的一个从Map到T的接口)。PropertyConverter只在最外层的容器里面用 。否则我干嘛PropertyConverter.to(Foo.class).from(map)这么麻烦?干啥不PropertyConverter.convert(map, Foo.class)?

类型安全嘛,比如你写map.put("id", "0"),就隐含依赖于id是int这个 事实,如果一旦id被重构成别的类型,或者改名 ,这里编译器和ide不会提醒不会报错。

引用
3. 哪个面向对象语言里面都是组合优于继承,OO 原则么。不过我觉得这种地方用继承更直截了当,目前也看不见什么更复杂的东西。将来若是这里真的变复杂了,也可以重构。

我倒是觉得用继承比较费解,这里更象一个has-a而不是is-a。

0 请登录后投票
   发表时间:2008-05-09  
有点像springmvc里面的binder
0 请登录后投票
   发表时间:2008-05-10  
ajoo 写道
所有的类型都是。因为这里你需要的不是一个编译时类型,而是运行时的。


不明白你这里所说的“运行时的类型”是什么含义。这个 GetProperty<String>("name", ""); 是直接写在代码里的,编译时就可以知道,java 编译器应该很容易就可以处理。

ajoo 写道
不要。我会注射一个Function<Map, T>进去,(或者任何的一个从Map到T的接口)。PropertyConverter只在最外层的容器里面用 。否则我干嘛PropertyConverter.to(Foo.class).from(map)这么麻烦?干啥不PropertyConverter.convert(map, Foo.class)?


嗯,这是一个优势。

ajoo 写道
类型安全嘛,比如你写map.put("id", "0"),就隐含依赖于id是int这个 事实,如果一旦id被重构成别的类型,或者改名 ,这里编译器和ide不会提醒不会报错。


不论用什么方案,map.put("id", "0") 或是类似的东西总是会在某个地方顽强地存在的。

ajoo 写道
我倒是觉得用继承比较费解,这里更象一个has-a而不是is-a。


我已经逐渐倾向于不用 has-a 、is-a 这种方式来考虑如何构造程序了 …… 算了,这个话题说起来要费太多口水,得空的时候再学习老庄写“敲响 is-a 的丧钟”吧,:D:D
0 请登录后投票
   发表时间:2008-05-10  
引用
ajoo 写道
所有的类型都是。因为这里你需要的不是一个编译时类型,而是运行时的。


不明白你这里所说的“运行时的类型”是什么含义。这个 GetProperty<String>("name", ""); 是直接写在代码里的,编译时就可以知道,java 编译器应该很容易就可以处理。


C++可以在编译时处理这种情况是因为它用扩展法。在编译的时候它知道T是String。而Java不知道。在getProperty()内部你要调用convert(targetType, value)的时候,你不知道targetType是什么。


引用

ajoo 写道
类型安全嘛,比如你写map.put("id", "0"),就隐含依赖于id是int这个 事实,如果一旦id被重构成别的类型,或者改名 ,这里编译器和ide不会提醒不会报错。


不论用什么方案,map.put("id", "0") 或是类似的东西总是会在某个地方顽强地存在的。

用proxy,字符串仅仅会在PropertyConverterTest一个类里面存在,而我会建立一些完全虚假的FooForTest接口和一些虚构的property,这些东西测试好了之后再也不会变了。

而客户代码因为直接依赖业务接口Foo,永远不会需要基于字符串进行测试。比如:
@interface Foo {
  int id();
  String name() default "foo";
  Gender sex() default UNKNOWN;
}

public class FooClient {
  private final Function<Map<String, String>, Foo> converter;

  public void run(Map<String, String> map) {
    Foo foo = converter.from(map);
    if (foo.id() == 1) {
       ...
    }
    if (foo.sex() == MALE) {
      ...
    }
  }
}

你看测试里我是不是再也不关心Map里面放的是什么了?反正直接mock一个Foo来用就是了。

用GetProperty的话,那么FooTest, BarTest, BazTest等等等等都要依赖字符串的。


引用
ajoo 写道
我倒是觉得用继承比较费解,这里更象一个has-a而不是is-a。


我已经逐渐倾向于不用 has-a 、is-a 这种方式来考虑如何构造程序了 …… 算了,这个话题说起来要费太多口水,得空的时候再学习老庄写“敲响 is-a 的丧钟”吧,:D:D


我已经逐渐学会用大家喜欢用的词来讨论问题了,即使它们不够精确,或者说穿了就是狗屁。
0 请登录后投票
   发表时间:2008-05-10  
ajoo 写道
C++可以在编译时处理这种情况是因为它用扩展法。在编译的时候它知道T是String。而Java不知道。在getProperty()内部你要调用convert(targetType, value)的时候,你不知道targetType是什么。


明白了,type erasure 的泛型总是出乎我意料地弱 …… 那么,在泛型函数内取 T 对应的 class 对象行不行?这个只需要编译器给泛型函数加一个隐藏的额外参数,应该是可以做的吧?

ajoo 写道
用proxy,字符串仅仅会在PropertyConverterTest一个类里面存在,而我会建立一些完全虚假的FooForTest接口和一些虚构的property,这些东西测试好了之后再也不会变了。

而客户代码因为直接依赖业务接口Foo,永远不会需要基于字符串进行测试。比如:
@interface Foo {
  int id();
  String name() default "foo";
  Gender sex() default UNKNOWN;
}

public class FooClient {
  private final Function<Map<String, String>, Foo> converter;

  public void run(Map<String, String> map) {
    Foo foo = converter.from(map);
    if (foo.id() == 1) {
       ...
    }
    if (foo.sex() == MALE) {
      ...
    }
  }
}

你看测试里我是不是再也不关心Map里面放的是什么了?反正直接mock一个Foo来用就是了。

用GetProperty的话,那么FooTest, BarTest, BazTest等等等等都要依赖字符串的。


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

ajoo 写道
我已经逐渐学会用大家喜欢用的词来讨论问题了,即使它们不够精确,或者说穿了就是狗屁。


你小子庸俗化了呀!当年那只特立独行的猪捏?猪肉涨价被人宰了吃了?
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics