Spiga

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

2009-09-07 11:15 by Jeffrey Zhao, 6270 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();

您有兴趣实现一下吗?

Add your comment

70 条回复

  1. #1楼 李永京      2009-09-07 11:22
    看下,还没想到
     回复 引用 查看   
  2. #2楼[楼主] Jeffrey Zhao      2009-09-07 11:35
    @李永京
    一定是NHibernate几个字把你吸引进来的。
     回复 引用 查看   
  3. #3楼 李永京      2009-09-07 11:52
    看看老赵有什么新想法啊。。。
     回复 引用 查看   
  4. #4楼[楼主] Jeffrey Zhao      2009-09-07 11:59
    @李永京
    嗯嗯……
    因为既然是领域模型,我觉得不能默认属性是没有逻辑的。
    因此,无论是延迟加载还是其他什么都好,都要想办法保持原有的逻辑。
    NHibernate这方面做得很不好。
     回复 引用 查看   
  5. #5楼 近近      2009-09-07 12:17
    问下赵老师,学习。net除了学习一门语言,比如c#,还需要学习点其他语言不,特别能配合c#用的,如c++,c++太广了,比如win32编程之类的,给点指点。
     回复 引用 查看   
  6. #6楼[楼主] Jeffrey Zhao      2009-09-07 12:35
    @近近
    你是为了学习还是使用?
    如果是使用,那么用什么就学什么。
    如果是为了学习,学F#吧。
     回复 引用 查看   
  7. #7楼 鹤冲天      2009-09-07 12:36
    关于,延迟加载,我以前见过一个这样子的写法:
    [Lazy]
    public abstract int SomeId {get; set;}
    

    CoolStorage
    http://www.codeplex.com/CoolStorage
     回复 引用 查看   
  8. #8楼 pk的眼泪      2009-09-07 12:40
    这里貌似不需要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. #9楼 Ivony...      2009-09-07 13:01
      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. #10楼 craboYang      2009-09-07 13:20
    就让NH Lazy去, 涉及逻辑, 我还是在Service处理妥当。
     回复 引用 查看   
  11. #11楼 卡不[未注册用户]2009-09-07 13:22
    keyi&ba
     回复 引用   
  12. #12楼 火星人.NET      2009-09-07 13:31
    一点也不理想~
     回复 引用 查看   
  13. #13楼[楼主] Jeffrey Zhao      2009-09-07 13:41
    引用craboYang:就让NH Lazy去, 涉及逻辑, 我还是在Service处理妥当。

    那么就很容易写成Transactional Scripts,而不是Domain Model了。既然是Domain Model,势必要包含一部分业务逻辑。
     回复 引用 查看   
  14. #14楼[楼主] Jeffrey Zhao      2009-09-07 13:41
    引用火星人.NET:一点也不理想~

    哪个地方不理想?
     回复 引用 查看   
  15. #15楼[楼主] Jeffrey Zhao      2009-09-07 13:41
    @Ivony...
    嗯嗯,和我之前写的Lazy<T>组件差不多,呵呵。
     回复 引用 查看   
  16. #16楼[楼主] Jeffrey Zhao      2009-09-07 13:42
    引用鹤冲天:
    关于,延迟加载,我以前见过一个这样子的写法:
    [Lazy]
    public abstract int SomeId {get; set;}
    

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

    这是为了告诉ORM框架这个字段延迟加载,加载的数据源由ORM框架确定。
    而我这边是要从外部给定一个值,而且可能在另一个地方这个字段就不需要延迟了,所以是不一样的。
     回复 引用 查看   
  17. #17楼 dispaly      2009-09-07 13:43
    引用Jeffrey Zhao:
    @Ivony...
    嗯嗯,和我之前写的Lazy<T>组件差不多,呵呵。

    不错
     回复 引用 查看   
  18. #18楼[楼主] Jeffrey Zhao      2009-09-07 13:43
    引用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. #19楼 dispaly      2009-09-07 13:44
    @Jeffrey Zhao
    ```````
     回复 引用 查看   
  20. #20楼 pk的眼泪      2009-09-07 14:37
    @Jeffrey Zhao
    通过loader赋值给base.LazyInt,然后再从base.LazyInt返回值,这就是所谓的延迟?对于延迟加载很疑惑,请指教,3Q.
     回复 引用 查看   
  21. #21楼 DreamTrue      2009-09-07 14:38
    看不懂,莫非大家都升级到3.5了
     回复 引用 查看   
  22. #22楼[楼主] Jeffrey Zhao      2009-09-07 14:39
    @pk的眼泪
    所谓的延迟不是指设置base.LazyInit,而是执行Loader的时机。
     回复 引用 查看   
  23. #23楼[楼主] Jeffrey Zhao      2009-09-07 14:39
    @DreamTrue
    4.0都要出来了,现在还没有升级到3.5就是你的不对了……
     回复 引用 查看   
  24. #24楼 DreamTrue      2009-09-07 14:49
    @Jeffrey Zhao
    晕,我们还在2.0呢
     回复 引用 查看   
  25. #25楼[楼主] Jeffrey Zhao      2009-09-07 14:57
    @DreamTrue
    该跟进的还是要跟进的。
     回复 引用 查看   
  26. #26楼 pk的眼泪      2009-09-07 15:54
    @Jeffrey Zhao
    thx,I see,一直在用VS2008,但就是没使用其新特性,貌似很畸形...
     回复 引用 查看   
  27. #27楼 tubo      2009-09-07 15:57
    最后的"辅助类库"看起来使用不方便,需要每个实例都要去设置loader方法?
    实际上某个属性延迟加载方法基本上和实例是无关的(当然这个方法需要传入一个唯一的值来表示不同的实例).

     回复 引用 查看   
  28. #28楼[楼主] Jeffrey Zhao      2009-09-07 16:00
    @tubo
    最后的辅助类库是通用的,换一个目标类也同样用法。
    其实和不延迟加载的区别是,不延迟加载是:
    xxx.Name = GetName();
    而延迟加载则是:
    Setup(x => x.Name, () => GetName());
    我觉得是很方便的,呵呵。
     回复 引用 查看   
  29. #29楼[楼主] Jeffrey Zhao      2009-09-07 16:00
    @pk的眼泪
    太浪费了。
     回复 引用 查看   
  30. #30楼 精密~顽石      2009-09-07 16:31
    最近一直在调查.net究竟就业情况怎么样,发现还是不行。像楼主这样,也大概只是把.net当成玩具。目前我所看到的还是要c/c++的多一些,你看李开复开了个新网站,招聘条件,c/c++,java,perl等都有,就是没有.net,我在深圳,看到招.net的,也少得可怜。唉,都想转别的了。
     回复 引用 查看   
  31. #31楼[楼主] Jeffrey Zhao      2009-09-07 16:33
    @精密~顽石
    呵呵,这些都是实际生产中遇到的问题,为什么说是玩具呢?
    李开富不懂.NET,你让他招什么好。
    除了Java的几个生产公司,各大外企都有招.NET的,你觉得还不够吗?
     回复 引用 查看   
  32. #32楼 精密~顽石      2009-09-07 16:34
    @Jeffrey Zhao
    就我目前的视野看来,好像招得很少,你到google生活中,搜一下,中国这几大城市的.net的招聘信息,很少。。
     回复 引用 查看   
  33. #33楼 pk的眼泪      2009-09-07 16:34
    @Jeffrey Zhao
    浪费是多余的,感觉3.5是拿来玩的而不是用的。
     回复 引用 查看   
  34. #34楼[楼主] Jeffrey Zhao      2009-09-07 16:36
    @精密~顽石
    看了你的博客,我觉得你的心态不好……
     回复 引用 查看   
  35. #35楼[楼主] Jeffrey Zhao      2009-09-07 16:37
    @pk的眼泪
    你可以去看看我写的那三篇关于委托的文章,你会发现这个方面可以让你提高无比多的生产力。
    C# 3.0的语言特性都是非常有用的,一旦上手,根本不想回头。直接拉开和Java这种劣质语言的差距。
     回复 引用 查看   
  36. #36楼 精密~顽石      2009-09-07 16:39
    @Jeffrey Zhao
    是呀,请问您一般怎么调节出好的心态?
     回复 引用 查看   
  37. #37楼[楼主] Jeffrey Zhao      2009-09-07 16:41
    @精密~顽石
    别把学历看太重,提高水平,用水平去说服别人。
     回复 引用 查看   
  38. #38楼 精密~顽石      2009-09-07 16:42
    @Jeffrey Zhao
    我在很多情况下都遭受过别人歧视我是搞.net的,虽然我只说我用网页后台技术是.net的,不过还是被歧视。
     回复 引用 查看   
  39. #39楼[楼主] Jeffrey Zhao      2009-09-07 16:44
    @精密~顽石
    他们为什么歧视你?理由是什么呢?
     回复 引用 查看   
  40. #40楼 craboYang      2009-09-07 16:45
    引用:
    那么就容易写成Transactional Scripts,而不是Domain Model了

    IMO,Model只是DTO级别,具备简单的验证即可。
    业务赋予Model生命, 而不是Model要保证业务。
    不同业务场景下,Model的限制要求不同。
     回复 引用 查看   
  41. #41楼 精密~顽石      2009-09-07 16:47
    @Jeffrey Zhao
    我就是纳闷呀,主要意思是大概是.net的没什么技术含量,不过我自己也觉得如此,目前我还没有突破在搞数据库的这个范围,整天就是围着数据库增删改,绑下页面,多了就腻了。
     回复 引用 查看   
  42. #42楼[楼主] Jeffrey Zhao      2009-09-07 16:51
    @craboYang
    Domain Model中的领域模型对象是带有业务逻辑的,这个我想不用再讨论了吧……DTO则是另外一回事情,只是用来携带对象。
    或者说,你认为Domain Model和Transactional Scripts有什么区别呢?
     回复 引用 查看   
  43. #43楼[楼主] Jeffrey Zhao      2009-09-07 16:52
    @精密~顽石
    那么,我博客上写的东西你都理解吗?
     回复 引用 查看   
  44. #44楼 精密~顽石      2009-09-07 16:57
    @Jeffrey Zhao
    不理解。这个概念在一本书上看过,我只是没用到,用到的时候搜关键字也许能搜到你这篇文章吧。我只是感觉有用的东西学一下,暂时对我来说用不着的,学了,没去用,过阵子就忘了,还不如不学。您工作也是用.net吗?
     回复 引用 查看   
  45. #45楼[楼主] Jeffrey Zhao      2009-09-07 16:59
    @精密~顽石
    我不是说我现在写的这篇文章,我是说我博客上目前400多篇文章上所写的东西,普遍地说,你都了解吗?
    我当然是用.net的,否则我花那么多精力研究.net做什么呢?
     回复 引用 查看   
  46. #46楼[楼主] Jeffrey Zhao      2009-09-07 17:00
    @精密~顽石
    还有,学一个东西不是学它怎么用,是学它的思想。
    不要看到它觉得用不着就不去学,看一遍,说不定在另外一个地方就会用到类似的做法了。
     回复 引用 查看   
  47. #47楼 近近      2009-09-07 17:02
    .net很强大,我虽然不懂多少,但是深入进去那可是相当的吸引人啊。特别是3.0的出来后,简直是太猛了.期待4.0的出来,看看是什么样的。赵老师说的F#要去学习下看看,不过那个好像好难的。
     回复 引用 查看   
  48. #48楼 精密~顽石      2009-09-07 17:03
    @Jeffrey Zhao
    我相信您的工作一定不是增删改,请问您现在主要用到的是.net的哪一块呢?能不能告诉我,哪方面应用比较多?
     回复 引用 查看   
  49. #49楼[楼主] Jeffrey Zhao      2009-09-07 17:06
    @精密~顽石
    嗯,其实也就是个普通的asp.net应用程序,比如myspace,facebook那样。
    myspace,facebook有啥厉害的,也不就是个web应用程序嘛,对不对?
     回复 引用 查看   
  50. #50楼 精密~顽石      2009-09-07 17:13
    @Jeffrey Zhao
    不对,只要你不用做增删改了,那么你就超脱了。感觉网站应用还是有很多做得好的,不过都不是基于.net的,google好像没有一个应用是用.net的,中国各大高校里面的管理信息系统,好像也是java技术的居多,.net至今我还没看到,不知道是我孤陋寡闻还是什么原因。
     回复 引用 查看   
  51. #51楼[楼主] Jeffrey Zhao      2009-09-07 17:16
    @精密~顽石
    首先,好的网站应用程序都可以用.NET做出来,只是他们没去做。
    其次,你的确孤陋寡闻了,Google是.NET的使用大户,Google Shop甚至是asp(不是aspx)做的,呵呵。
    中国各大高校里的.net也层出不穷,否则你认为中小型应用80-90%,世界500强50%的占有率是怎么来的……
     回复 引用 查看   
  52. #52楼 精密~顽石      2009-09-07 17:23
    @Jeffrey Zhao
    能不能给点 href 看看?让事实说话
     回复 引用 查看   
  53. #53楼 pk的眼泪      2009-09-07 17:29
    @精密~顽石
    无论是啥应用,都可以搞的很高深,致使是增删改查,关键不在于语言,而在于态度。
     回复 引用 查看   
  54. #54楼 精密~顽石      2009-09-07 17:30
    @pk的眼泪
    恩,是的。
     回复 引用 查看   
  55. #55楼 pk的眼泪      2009-09-07 17:30
    @Jeffrey Zhao
    下个项目考虑用新特性,贵在尝试,呵呵。
     回复 引用 查看   
  56. #56楼[楼主] Jeffrey Zhao      2009-09-07 17:31
    @精密~顽石
    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. #57楼 精密~顽石      2009-09-07 17:35
    @Jeffrey Zhao
    了解了。哎,还是把.net搞好,实在不行再转c/c++吧。java转了没意义。
     回复 引用 查看   
  58. #58楼 craboYang      2009-09-08 08:49
    引用Jeffrey Zhao:
    @craboYang
    Domain Model中的领域模型对象是带有业务逻辑的……DTO则是另外一回事情




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


    ActiveRecord迎合了充血的要求
    Hibernate的Mapping、Lazy、Proxy对对象的要求, 恰好支持了:如果Model是DTO, 他就让你够爽。
     回复 引用 查看   
  59. #59楼[楼主] Jeffrey Zhao      2009-09-08 09:08
    @craboYang
    那么我换句说法:“Domain Model中的领域模型对象是可能需要带有业务逻辑的”
    NH的做法导致只能支持DTO,至少属性中不能包含任何逻辑,这是不合理的。
    而贫血到DTO就接近Transactional Scripts了,NH至少要支持失血吧?
    现在用NH就必须把原本放在属性中的逻辑转移到其他成员中,破坏了原本设计良好的Domain Model。
    所以这点上NHibernate做的不好,我的项目迁就了NH的很多,所以替换很深。
     回复 引用 查看   
  60. #60楼 farstar[未注册用户]2009-09-08 09:55
    http://www.infoq.com/cn/articles/aspects-of-domain-model-mgmt

    这篇文章中提出的将领域模型与基础架构(例如延迟加载等)分离的思路比较合适
     回复 引用   
  61. #61楼[楼主] Jeffrey Zhao      2009-09-08 10:34
    @farstar
    我文章是提出一种解决方案,并没有说它应该在哪里使用啊,呵呵。
    事实上,领域模型和延迟加载在我这里的确是分离的。
    领域模型归领域模型定制,延迟加载靠LazyFactory负责。
    LazyFactory哪里调用呢?在基础架构里调用啊。
    所以我说这个做法理想,因为它既不会影响领域模型的逻辑实现,又把延迟加载透明化了。
     回复 引用 查看   
  62. #62楼 luotong      2009-09-08 11:35
    还是这种纯技术的讨论感觉最舒服,也符合博客园的氛围。
     回复 引用 查看   
  63. #63楼 craboYang      2009-09-08 14:20
    引用Jeffrey Zhao:
    NH的做法导致只能支持DTO,至少属性中不能包含任何逻辑,这是不合理的。


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

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

    要工具适应你的设计,除非你设计工具。
    按工具设计的方式设计,工具就不会带来困扰。
     回复 引用 查看   
  64. #64楼 craboYang      2009-09-08 14:22
    引用luotong:还是这种纯技术的讨论感觉最舒服,也符合博客园的氛围。


    JaveEye里的讨论更纯粹, 更深入。
    随便的话题都是追贴无数,也都很愿意交流。
    比如: “是我Out了,还是SOA瞎扯蛋?”http://www.javaeye.com/topic/451536?page=2
     回复 引用 查看   
  65. #65楼[楼主] Jeffrey Zhao      2009-09-08 14:24
    @craboYang
    javaeye是纯扯蛋的,以前看了几个话题,懒得去了,呵呵。
     回复 引用 查看   
  66. #66楼[楼主] Jeffrey Zhao      2009-09-08 14:27
    @craboYang
    我的理解是,NH是一个通用的工具,不应该限制用户的行为。既然号称已经支持的POCO,也号称支持Domain Model,但只支持“失血”的,连“贫血”都不支持,这样很不好。
    而且它一直强调Persistant Ignorance,其实很明显,需要项目的设计去配合它,这就不是一个好工具了。
    所以我说,要不是看在其他工具更不好,我也不会用NHibernate。
     回复 引用 查看   
  67. #67楼 Peter.X.Gao      2009-09-08 16:31
    老赵,没看明白这个接口的意思,能写的详细些吗?或者针对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. #68楼 craboYang      2009-09-09 08:40
    引用Jeffrey Zhao:
    @craboYang
    javaeye是纯扯蛋的,以前看了几个话题,懒得去了,呵呵。

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

    讨论氛围JavaEye要好上千倍。
     回复 引用 查看   
  69. #69楼[楼主] Jeffrey Zhao      2009-09-09 09:42
    @craboYang
    好吧。我认为javaeye只是表面看上去气氛热烈,但是讨论的内容和方式以扯蛋和装蛋居多,空对空。所以看着看着,就越来越不想看了……
     回复 引用 查看   
  70. #70楼 craboYang      2009-09-09 11:34
    引用Jeffrey Zhao:
    @craboYang
    好吧。我认为javaeye只是表面看上去气氛热烈,但是讨论的内容和方式以扯蛋和装蛋居多,空对空。所以看着看着,就越来越不想看了……


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

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

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

    所以博客和讨论在一次认知是不同阶段的事情。
     回复 引用 查看   
发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 1561675 YbKUwnGkcic=