Hello World
Spiga

较为理想的延迟代理的编写方式

2009-09-07 11:15 by 老赵, 7402 visits

之前我谈到,在普通情况下我们可以很轻松地写出过一个代理类,用来处理延迟加载的情况。当时给出了一个很简单的做法,也就是指创建基类,覆盖它的一些属性实现,类似这种:

public class LazySomeClass : SomeClass
{
    public override int SomeID
    {
        get
        {
            return this.LazySomeID.Value;
        }
        set
        {
            this.LazySomeID.Value = value;
        }
    }
 
    public Lazy<int> LazySomeID { get; set; }
}

不过我当时也提到,这么做可能够用,但是也有一些缺点。例如,它破坏了SomeID属性中包含的业务逻辑。可能SomeID原本会包含一些验证逻辑,或和另外一个属性加以同步,或发起INotifyPropertyChanging/Changed中的事件。这也是我认为NHibernate的延迟加载方法欠妥的原因,至于其他还有一些缺陷有机会在讨论。

因此我又想了想,理想中的延迟加载方式应该是什么样的呢?例如,同样是个SomeClass类,其中部分属性允许“设置”延迟加载:

public class SomeClass
{
    public SomeClass() { }

    public SomeClass(int i) { }

    public virtual int LazyInt { get; set; }

    public virtual bool LazyBoolean { get; set; }

    public int EagerInt { get; set; }

    public bool EagerBoolean { get; set; }

    // some other members...
}

如果是一个较为合理的延迟代理类,我认为它的写法应该是这样的:

public class LazySomeClass : SomeClass
{
    public override int LazyInt
    {
        get
        {
            if (!this.m_lazyIntLoaded)
            {
                if (this.m_lazyIntLoader != null)
                {
                    base.LazyInt = this.m_lazyIntLoader();
                    this.m_lazyIntLoader = null;
                }

                this.m_lazyIntLoaded = true;
            }

            return base.LazyInt;
        }
        set
        {
            base.LazyInt = value;
            this.m_lazyIntLoaded = true;
            this.m_lazyIntLoader = null;
        }
    }

    private bool m_lazyIntLoaded = false;
    private Func<int> m_lazyIntLoader = null;
    public Func<int> LazyIntLoader
    {
        get
        {
            return this.m_lazyIntLoader;
        }
        set
        {
            this.m_lazyIntLoader = value;
            this.m_lazyIntLoaded = false;
        }
    }
}

如果我们需要为LazyInt属性设置延迟加载,那么可以设置LazyIntLoader属性,它是一个Func<int>委托对象。这种实现方式看上去复杂,不过它有一定的合理性:

  1. 每个Loader只执行一次,直到提供新的Loader。
  2. Loader执行后,会赋值给base.LazyInt,保持基类的业务逻辑。
  3. 从base.LazyInt读取,同样保持基类的业务逻辑。
  4. 如果不需要延迟加载,那么属性的行为保持不变。

其中第4点非常重要,这意味着这是一种可以“标准化”的延迟加载代理类的标准写法。我们可以在运行时使用Emit生成新的类型,继承目标类,为每个virtual属性在子类中重写一份。由于在默认情况下属性的行为不会改变,因此这样的代理类不会有问题。甚至,“辅助类库”的接口我也想好了:

var builder = LazyFactory.Create(() => new SomeClass(10)
{
    EagerInt = 10,
    EagerBoolean = true
});

SomeClass value = builder
    .Setup(c => c.LazyInt, () => GetLazyValue<int>())
    .Setup(c => c.LazyBoolean, () => GetLazyValue<bool>())
    .Create();

您有兴趣实现一下吗?

Creative Commons License

本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名赵劼(包含链接),具体操作方式可参考此处。如您有任何疑问或者授权方面的协商,请给我留言

Add your comment

71 条回复

  1. 李永京
    *.*.*.*
    链接

    李永京 2009-09-07 11:22:00

    看下,还没想到

  2. 老赵
    admin
    链接

    老赵 2009-09-07 11:35:00

    @李永京
    一定是NHibernate几个字把你吸引进来的。

  3. 李永京
    *.*.*.*
    链接

    李永京 2009-09-07 11:52:00

    看看老赵有什么新想法啊。。。

  4. 老赵
    admin
    链接

    老赵 2009-09-07 11:59:00

    @李永京
    嗯嗯……
    因为既然是领域模型,我觉得不能默认属性是没有逻辑的。
    因此,无论是延迟加载还是其他什么都好,都要想办法保持原有的逻辑。
    NHibernate这方面做得很不好。

  5. 近近
    *.*.*.*
    链接

    近近 2009-09-07 12:17:00

    问下赵老师,学习。net除了学习一门语言,比如c#,还需要学习点其他语言不,特别能配合c#用的,如c++,c++太广了,比如win32编程之类的,给点指点。

  6. 老赵
    admin
    链接

    老赵 2009-09-07 12:35:00

    @近近
    你是为了学习还是使用?
    如果是使用,那么用什么就学什么。
    如果是为了学习,学F#吧。

  7. 鹤冲天
    *.*.*.*
    链接

    鹤冲天 2009-09-07 12:36:00

    关于,延迟加载,我以前见过一个这样子的写法:

    [Lazy]
    public abstract int SomeId {get; set;}
    

    CoolStorage
    http://www.codeplex.com/CoolStorage

  8. pk的眼泪
    *.*.*.*
    链接

    pk的眼泪 2009-09-07 12:40:00

    这里貌似不需要m_lazyIntLoaded

    public class LazySomeClass : SomeClass
    {
        public override int LazyInt
        {
            get
            {
                    if (this.m_lazyIntLoader != null)
                    {
                        base.LazyInt = this.m_lazyIntLoader();
                        this.m_lazyIntLoader = null;
                    }
    
                return base.LazyInt;
            }
            set
            {
                base.LazyInt = value;
            }
        }
    
        private Func<int> m_lazyIntLoader = null;
        public Func<int> LazyIntLoader
        {
            get
            {
                return this.m_lazyIntLoader;
            }
            set
            {
                this.m_lazyIntLoader = value;
            }
        }
    }
    
    

  9. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-09-07 13:01:00

      public class LazyLoader<T>
      {
    
        private Func<T> _loader;
        private T _value;
        private bool _loaded;
    
        public LazyLoader( Func<T> loader )
        {
          _loader = loader;
        }
    
        public T GetValue()
        {
          if ( !_loaded )
            _value = _loader();
    
          return _value;
        }
    
        public static implicit operator T( LazyLoader<T> loader )
        {
    
          if ( loader == null )
            return default( T );
    
          return loader.GetValue();
        }
    
        public static implicit operator LazyLoader<T>( T value )
        {
          var loader = new LazyLoader<T>( null );
          loader._value = value;
          loader._loaded = true;
          return loader;
        }
    
        public static implicit operator LazyLoader<T>( Func<T> loader )
        {
          return new LazyLoader<T>( loader );
        }
      }
    

    随手写的一个另一种实现的小东西,没做线程冲突处理。

  10. craboYang
    *.*.*.*
    链接

    craboYang 2009-09-07 13:20:00

    就让NH Lazy去, 涉及逻辑, 我还是在Service处理妥当。

  11. 卡不[未注册用户]
    *.*.*.*
    链接

    卡不[未注册用户] 2009-09-07 13:22:00

    keyi&ba

  12. 火星人.NET
    *.*.*.*
    链接

    火星人.NET 2009-09-07 13:31:00

    一点也不理想~

  13. 老赵
    admin
    链接

    老赵 2009-09-07 13:41:00

    火星人.NET:一点也不理想~


    哪个地方不理想?

  14. 老赵
    admin
    链接

    老赵 2009-09-07 13:41:00

    craboYang:就让NH Lazy去, 涉及逻辑, 我还是在Service处理妥当。


    那么就很容易写成Transactional Scripts,而不是Domain Model了。既然是Domain Model,势必要包含一部分业务逻辑。

  15. 老赵
    admin
    链接

    老赵 2009-09-07 13:41:00

    @Ivony...
    嗯嗯,和我之前写的Lazy<T>组件差不多,呵呵。

  16. 老赵
    admin
    链接

    老赵 2009-09-07 13:42:00

    鹤冲天:
    关于,延迟加载,我以前见过一个这样子的写法:

    [Lazy]
    public abstract int SomeId {get; set;}
    

    CoolStorage
    http://www.codeplex.com/CoolStorage


    这是为了告诉ORM框架这个字段延迟加载,加载的数据源由ORM框架确定。
    而我这边是要从外部给定一个值,而且可能在另一个地方这个字段就不需要延迟了,所以是不一样的。

  17. dispaly
    *.*.*.*
    链接

    dispaly 2009-09-07 13:43:00

    Jeffrey Zhao:
    @Ivony...
    嗯嗯,和我之前写的Lazy<T>组件差不多,呵呵。


    不错

  18. 老赵
    admin
    链接

    老赵 2009-09-07 13:43:00

    pk的眼泪:
    这里貌似不需要m_lazyIntLoaded

    public class LazySomeClass : SomeClass
    {
        public override int LazyInt
        {
            get
            {
                if (this.m_lazyIntLoader != null)
                {
                    base.LazyInt = this.m_lazyIntLoader();
                    this.m_lazyIntLoader = null;
                }
    
                return base.LazyInt;
            }
            set
            {
                base.LazyInt = value;
            }
        }
    
        private Func<int> m_lazyIntLoader = null;
        public Func<int> LazyIntLoader
        {
            get
            {
                return this.m_lazyIntLoader;
            }
            set
            {
                this.m_lazyIntLoader = value;
            }
        }
    }


    嗯,似乎是这样的,呵呵。

  19. dispaly
    *.*.*.*
    链接

    dispaly 2009-09-07 13:44:00

    @Jeffrey Zhao
    ```````

  20. pk的眼泪
    *.*.*.*
    链接

    pk的眼泪 2009-09-07 14:37:00

    @Jeffrey Zhao
    通过loader赋值给base.LazyInt,然后再从base.LazyInt返回值,这就是所谓的延迟?对于延迟加载很疑惑,请指教,3Q.

  21. DreamTrue
    *.*.*.*
    链接

    DreamTrue 2009-09-07 14:38:00

    看不懂,莫非大家都升级到3.5了

  22. 老赵
    admin
    链接

    老赵 2009-09-07 14:39:00

    @DreamTrue
    4.0都要出来了,现在还没有升级到3.5就是你的不对了……

  23. 老赵
    admin
    链接

    老赵 2009-09-07 14:39:00

    @pk的眼泪
    所谓的延迟不是指设置base.LazyInit,而是执行Loader的时机。

  24. DreamTrue
    *.*.*.*
    链接

    DreamTrue 2009-09-07 14:49:00

    @Jeffrey Zhao
    晕,我们还在2.0呢

  25. 老赵
    admin
    链接

    老赵 2009-09-07 14:57:00

    @DreamTrue
    该跟进的还是要跟进的。

  26. pk的眼泪
    *.*.*.*
    链接

    pk的眼泪 2009-09-07 15:54:00

    @Jeffrey Zhao
    thx,I see,一直在用VS2008,但就是没使用其新特性,貌似很畸形...

  27. tubo
    *.*.*.*
    链接

    tubo 2009-09-07 15:57:00

    最后的"辅助类库"看起来使用不方便,需要每个实例都要去设置loader方法?
    实际上某个属性延迟加载方法基本上和实例是无关的(当然这个方法需要传入一个唯一的值来表示不同的实例).

  28. 老赵
    admin
    链接

    老赵 2009-09-07 16:00:00

    @tubo
    最后的辅助类库是通用的,换一个目标类也同样用法。
    其实和不延迟加载的区别是,不延迟加载是:
    xxx.Name = GetName();
    而延迟加载则是:
    Setup(x => x.Name, () => GetName());
    我觉得是很方便的,呵呵。

  29. 老赵
    admin
    链接

    老赵 2009-09-07 16:00:00

    @pk的眼泪
    太浪费了。

  30. 精密~顽石
    *.*.*.*
    链接

    精密~顽石 2009-09-07 16:31:00

    最近一直在调查.net究竟就业情况怎么样,发现还是不行。像楼主这样,也大概只是把.net当成玩具。目前我所看到的还是要c/c++的多一些,你看李开复开了个新网站,招聘条件,c/c++,java,perl等都有,就是没有.net,我在深圳,看到招.net的,也少得可怜。唉,都想转别的了。

  31. 老赵
    admin
    链接

    老赵 2009-09-07 16:33:00

    @精密~顽石
    呵呵,这些都是实际生产中遇到的问题,为什么说是玩具呢?
    李开富不懂.NET,你让他招什么好。
    除了Java的几个生产公司,各大外企都有招.NET的,你觉得还不够吗?

  32. pk的眼泪
    *.*.*.*
    链接

    pk的眼泪 2009-09-07 16:34:00

    @Jeffrey Zhao
    浪费是多余的,感觉3.5是拿来玩的而不是用的。

  33. 精密~顽石
    *.*.*.*
    链接

    精密~顽石 2009-09-07 16:34:00

    @Jeffrey Zhao
    就我目前的视野看来,好像招得很少,你到google生活中,搜一下,中国这几大城市的.net的招聘信息,很少。。

  34. 老赵
    admin
    链接

    老赵 2009-09-07 16:36:00

    @精密~顽石
    看了你的博客,我觉得你的心态不好……

  35. 老赵
    admin
    链接

    老赵 2009-09-07 16:37:00

    @pk的眼泪
    你可以去看看我写的那三篇关于委托的文章,你会发现这个方面可以让你提高无比多的生产力。
    C# 3.0的语言特性都是非常有用的,一旦上手,根本不想回头。直接拉开和Java这种劣质语言的差距。

  36. 精密~顽石
    *.*.*.*
    链接

    精密~顽石 2009-09-07 16:39:00

    @Jeffrey Zhao
    是呀,请问您一般怎么调节出好的心态?

  37. 老赵
    admin
    链接

    老赵 2009-09-07 16:41:00

    @精密~顽石
    别把学历看太重,提高水平,用水平去说服别人。

  38. 精密~顽石
    *.*.*.*
    链接

    精密~顽石 2009-09-07 16:42:00

    @Jeffrey Zhao
    我在很多情况下都遭受过别人歧视我是搞.net的,虽然我只说我用网页后台技术是.net的,不过还是被歧视。

  39. 老赵
    admin
    链接

    老赵 2009-09-07 16:44:00

    @精密~顽石
    他们为什么歧视你?理由是什么呢?

  40. craboYang
    *.*.*.*
    链接

    craboYang 2009-09-07 16:45:00

    引用:
    那么就容易写成Transactional Scripts,而不是Domain Model了

    IMO,Model只是DTO级别,具备简单的验证即可。
    业务赋予Model生命, 而不是Model要保证业务。
    不同业务场景下,Model的限制要求不同。

  41. 精密~顽石
    *.*.*.*
    链接

    精密~顽石 2009-09-07 16:47:00

    @Jeffrey Zhao
    我就是纳闷呀,主要意思是大概是.net的没什么技术含量,不过我自己也觉得如此,目前我还没有突破在搞数据库的这个范围,整天就是围着数据库增删改,绑下页面,多了就腻了。

  42. 老赵
    admin
    链接

    老赵 2009-09-07 16:51:00

    @craboYang
    Domain Model中的领域模型对象是带有业务逻辑的,这个我想不用再讨论了吧……DTO则是另外一回事情,只是用来携带对象。
    或者说,你认为Domain Model和Transactional Scripts有什么区别呢?

  43. 老赵
    admin
    链接

    老赵 2009-09-07 16:52:00

    @精密~顽石
    那么,我博客上写的东西你都理解吗?

  44. 精密~顽石
    *.*.*.*
    链接

    精密~顽石 2009-09-07 16:57:00

    @Jeffrey Zhao
    不理解。这个概念在一本书上看过,我只是没用到,用到的时候搜关键字也许能搜到你这篇文章吧。我只是感觉有用的东西学一下,暂时对我来说用不着的,学了,没去用,过阵子就忘了,还不如不学。您工作也是用.net吗?

  45. 老赵
    admin
    链接

    老赵 2009-09-07 16:59:00

    @精密~顽石
    我不是说我现在写的这篇文章,我是说我博客上目前400多篇文章上所写的东西,普遍地说,你都了解吗?
    我当然是用.net的,否则我花那么多精力研究.net做什么呢?

  46. 老赵
    admin
    链接

    老赵 2009-09-07 17:00:00

    @精密~顽石
    还有,学一个东西不是学它怎么用,是学它的思想。
    不要看到它觉得用不着就不去学,看一遍,说不定在另外一个地方就会用到类似的做法了。

  47. 近近
    *.*.*.*
    链接

    近近 2009-09-07 17:02:00

    .net很强大,我虽然不懂多少,但是深入进去那可是相当的吸引人啊。特别是3.0的出来后,简直是太猛了.期待4.0的出来,看看是什么样的。赵老师说的F#要去学习下看看,不过那个好像好难的。

  48. 精密~顽石
    *.*.*.*
    链接

    精密~顽石 2009-09-07 17:03:00

    @Jeffrey Zhao
    我相信您的工作一定不是增删改,请问您现在主要用到的是.net的哪一块呢?能不能告诉我,哪方面应用比较多?

  49. 老赵
    admin
    链接

    老赵 2009-09-07 17:06:00

    @精密~顽石
    嗯,其实也就是个普通的asp.net应用程序,比如myspace,facebook那样。
    myspace,facebook有啥厉害的,也不就是个web应用程序嘛,对不对?

  50. 精密~顽石
    *.*.*.*
    链接

    精密~顽石 2009-09-07 17:13:00

    @Jeffrey Zhao
    不对,只要你不用做增删改了,那么你就超脱了。感觉网站应用还是有很多做得好的,不过都不是基于.net的,google好像没有一个应用是用.net的,中国各大高校里面的管理信息系统,好像也是java技术的居多,.net至今我还没看到,不知道是我孤陋寡闻还是什么原因。

  51. 老赵
    admin
    链接

    老赵 2009-09-07 17:16:00

    @精密~顽石
    首先,好的网站应用程序都可以用.NET做出来,只是他们没去做。
    其次,你的确孤陋寡闻了,Google是.NET的使用大户,Google Shop甚至是asp(不是aspx)做的,呵呵。
    中国各大高校里的.net也层出不穷,否则你认为中小型应用80-90%,世界500强50%的占有率是怎么来的……

  52. 精密~顽石
    *.*.*.*
    链接

    精密~顽石 2009-09-07 17:23:00

    @Jeffrey Zhao
    能不能给点 href 看看?让事实说话

  53. pk的眼泪
    *.*.*.*
    链接

    pk的眼泪 2009-09-07 17:29:00

    @精密~顽石
    无论是啥应用,都可以搞的很高深,致使是增删改查,关键不在于语言,而在于态度。

  54. pk的眼泪
    *.*.*.*
    链接

    pk的眼泪 2009-09-07 17:30:00

    @Jeffrey Zhao
    下个项目考虑用新特性,贵在尝试,呵呵。

  55. 精密~顽石
    *.*.*.*
    链接

    精密~顽石 2009-09-07 17:30:00

    @pk的眼泪
    恩,是的。

  56. 老赵
    admin
    链接

    老赵 2009-09-07 17:31:00

    @精密~顽石
    google用asp.net制作了大量内部系统,可惜外面访问不到,我们著名的苏鹏老师7月还是8月还去google培训.net呢。
    至于google store本来用asp……好像现在已经换成asp.net了……
    http://www.googlestore.com/Fun/Fold+Up+Flyer.axd
    你可以看看它的response header:

    Server Microsoft-IIS/6.0
    X-Powered-By ASP.NET
    X-Aspnet-Version 2.0.50727

  57. 精密~顽石
    *.*.*.*
    链接

    精密~顽石 2009-09-07 17:35:00

    @Jeffrey Zhao
    了解了。哎,还是把.net搞好,实在不行再转c/c++吧。java转了没意义。

  58. craboYang
    *.*.*.*
    链接

    craboYang 2009-09-08 08:49:00

    Jeffrey Zhao:
    @craboYang
    Domain Model中的领域模型对象是带有业务逻辑的……DTO则是另外一回事情





    单你这句我就不赞同,Domain Model可以贫血到DTO, 可以充血到封装所有业务。


    ActiveRecord迎合了充血的要求
    Hibernate的Mapping、Lazy、Proxy对对象的要求, 恰好支持了:如果Model是DTO, 他就让你够爽。

  59. 老赵
    admin
    链接

    老赵 2009-09-08 09:08:00

    @craboYang
    那么我换句说法:“Domain Model中的领域模型对象是可能需要带有业务逻辑的”
    NH的做法导致只能支持DTO,至少属性中不能包含任何逻辑,这是不合理的。
    而贫血到DTO就接近Transactional Scripts了,NH至少要支持失血吧?
    现在用NH就必须把原本放在属性中的逻辑转移到其他成员中,破坏了原本设计良好的Domain Model。
    所以这点上NHibernate做的不好,我的项目迁就了NH的很多,所以替换很深。

  60. farstar[未注册用户]
    *.*.*.*
    链接

    farstar[未注册用户] 2009-09-08 09:55:00

    http://www.infoq.com/cn/articles/aspects-of-domain-model-mgmt

    这篇文章中提出的将领域模型与基础架构(例如延迟加载等)分离的思路比较合适

  61. 老赵
    admin
    链接

    老赵 2009-09-08 10:34:00

    @farstar
    我文章是提出一种解决方案,并没有说它应该在哪里使用啊,呵呵。
    事实上,领域模型和延迟加载在我这里的确是分离的。
    领域模型归领域模型定制,延迟加载靠LazyFactory负责。
    LazyFactory哪里调用呢?在基础架构里调用啊。
    所以我说这个做法理想,因为它既不会影响领域模型的逻辑实现,又把延迟加载透明化了。

  62. luotong
    *.*.*.*
    链接

    luotong 2009-09-08 11:35:00

    还是这种纯技术的讨论感觉最舒服,也符合博客园的氛围。

  63. craboYang
    *.*.*.*
    链接

    craboYang 2009-09-08 14:20:00

    Jeffrey Zhao:
    NH的做法导致只能支持DTO,至少属性中不能包含任何逻辑,这是不合理的。



    说到点上了, 我的意思就是,如果从DTO的角度,NH会跟你配合的很好, 从充血Model角度, 他会变成处处阻碍。

    所以你觉得NH很不好,而我觉得NH非常好。我用H从Java到.NET,也有3年了,觉得非常好,所以,只是角度的问题。

    要工具适应你的设计,除非你设计工具。
    按工具设计的方式设计,工具就不会带来困扰。

  64. craboYang
    *.*.*.*
    链接

    craboYang 2009-09-08 14:22:00

    luotong:还是这种纯技术的讨论感觉最舒服,也符合博客园的氛围。



    JaveEye里的讨论更纯粹, 更深入。
    随便的话题都是追贴无数,也都很愿意交流。
    比如: “是我Out了,还是SOA瞎扯蛋?”http://www.javaeye.com/topic/451536?page=2

  65. 老赵
    admin
    链接

    老赵 2009-09-08 14:24:00

    @craboYang
    javaeye是纯扯蛋的,以前看了几个话题,懒得去了,呵呵。

  66. 老赵
    admin
    链接

    老赵 2009-09-08 14:27:00

    @craboYang
    我的理解是,NH是一个通用的工具,不应该限制用户的行为。既然号称已经支持的POCO,也号称支持Domain Model,但只支持“失血”的,连“贫血”都不支持,这样很不好。
    而且它一直强调Persistant Ignorance,其实很明显,需要项目的设计去配合它,这就不是一个好工具了。
    所以我说,要不是看在其他工具更不好,我也不会用NHibernate。

  67. Peter.X.Gao
    *.*.*.*
    链接

    Peter.X.Gao 2009-09-08 16:31:00

    老赵,没看明白这个接口的意思,能写的详细些吗?或者针对SomeClass做个实现,谢谢

    var builder = LazyFactory.Create(() => new SomeClass(10)
    {
    EagerInt = 10,
    EagerBoolean = true
    });

    SomeClass value = builder
    .Setup(c => c.LazyInt, () => GetLazyValue<int>())
    .Setup(c => c.LazyBoolean, () => GetLazyValue<bool>())
    .Create();


  68. craboYang
    *.*.*.*
    链接

    craboYang 2009-09-09 08:40:00

    Jeffrey Zhao:
    @craboYang
    javaeye是纯扯蛋的,以前看了几个话题,懒得去了,呵呵。


    如果博客园没有类似你这样的帖子, 我懒得来博客园。

    讨论氛围JavaEye要好上千倍。

  69. 老赵
    admin
    链接

    老赵 2009-09-09 09:42:00

    @craboYang
    好吧。我认为javaeye只是表面看上去气氛热烈,但是讨论的内容和方式以扯蛋和装蛋居多,空对空。所以看着看着,就越来越不想看了……

  70. craboYang
    *.*.*.*
    链接

    craboYang 2009-09-09 11:34:00

    Jeffrey Zhao:
    @craboYang
    好吧。我认为javaeye只是表面看上去气氛热烈,但是讨论的内容和方式以扯蛋和装蛋居多,空对空。所以看着看着,就越来越不想看了……



    嗯,讨论式的内容不适合主动去看,而是当有类似的疑问和考量时去看才会觉得有价值。

    而博客方式即使你没这方面的需求,很多时候也是一种陶冶,一次学习。

    一次完整的认知过程应该是:了解->系统性深入->理解->运用->讨论->再运用

    所以博客和讨论在一次认知是不同阶段的事情。

  71. 链接

    ilovehaley.kid 2011-09-02 15:41:47

    路过~

    mark~

    学习~

发表回复

登录 / 登录并记住我 ,登陆后便可删除或修改已发表的评论 (请注意保留评论内容)

昵称:(必填)

邮箱:(必填,仅用于Gavatar

主页:(可选)

评论内容(大于5个字符):

  1. Your Name yyyy-MM-dd HH:mm:ss

使用Live Messenger联系我