跳至主要内容

博文

目前显示的是 四月, 2009的博文

做到忘记

学弟学妹们经常问我一个类似的问题:一道编程题自己怎么想也写不出来,转而看看其他人的解答,感觉代码里没有用什么奇技淫巧,所有语句都能看明白,为什么换成自己就写不出来呢?换个说法就是:关键词、语法等我都熟悉了,这样我是不是就学会C语言了?如果是,为什么我写不出代码?如果不是,那到底学到什么程度才算学会? 我想大部分同学应该都看过金大侠的武侠小说,《笑傲江湖》中,华山派的基本招式是“白云出岫->有凤来仪 ->天绅倒悬->白虹贯日->苍松迎客->...”,但风清扬最初让令狐冲使出“白虹贯日->有凤来仪...”,令狐冲显得不知所措,他觉得这两招完全格格不入。经过风清扬指点后才明白,与人较量时出招是随机应变的,下一步出什么招是由对手出的招数决定,而不是由自己使的招数决定。 同理,写C语言什么时候用if、什么时候用switch是由问题决定的,随问题的改变而改变我们的编码;说汉语、英语还是日语,这是由听者决定的,而不是自己喜欢什么就只说什么。 但凡学有所成,一招一式都不是死板的。比如《笑傲江湖》中,风清扬传授令狐冲独孤九剑时说:“剑法要旨是在一个‘悟’字,决不在死记硬记。等到通晓了这九剑的剑意,则无所施而不可,便是将全部变化尽数忘记,也不相干,临敌之际,更是忘记得越干净彻底,越不受原来剑法的拘束。 ”无独有偶,《倚天屠龙记》中张三丰教张无忌太极剑时,也是让他将所学剑法忘得一干二净才算学成。 我又要唠叨“工具理论”了:我们学的知识、技术等,都只是一个工具。学会使用工具就是在恰当的情景下解决问题。比如榔头是个好工具,用来钉钉子很方便,但大部分人不会用它来代替抹布去擦桌子吧。这就是要做到“忘记”,学会了如何使用榔头,不用一天到晚都在心里挂念着它,需要钉钉子时就很自然地想到使用榔头。这时候你是真的学会了,你知道这个工具(技术)什么时候该用,什么时候不该用。用它来解决问题,犹如行云流水,顺畅自然。

《HFDP》读书笔录(九)——单一责任原则

九、单一责任原则 一个类应该只有一个引起变化的原因 当我们允许一个类不但要完成自己的事情(管理某种聚合),还同时要担负更多责任(例如遍历)时,我们就给了这个类两个变化的原因。两个?没错,就是两个:如果这个集合改变的话,这个类也必须改变;如果我们遍历的方式改变的话,这个类也必须跟着改变。 单一责任原则的目标就是让类达到“高内聚性”(其实也就是类提供的方法是否符合类所在的抽象层次,依然是抽象问题)。 内聚( cohesion )是一个比单一责任更普遍的概念,但两者其实关系是很密切的。它用来度量一个类或模块紧密地达到单一目的或责任。 当一个模块或一个类被设计成只有一组相关的功能时,我们说它具有高内聚;反之,当被设计成支持一组不相关的功能时,我们说它具有低内聚。 遵守这个原则的类容易具有很高的凝聚力,而且比背负许多责任的低内聚类更容易维护。 翻译书 P339 倒数第二段说“如何解决呢?这个原则公司我们将一个责任只指派给一个类。”,我觉得这句话 不合理 ,应该是“一个类只指派一个责任”。原文的漏洞在于:一个责任只指派给一个类,也就意味着一个类可以被指派多个责任(只要这些责任没有被指派给其他类)。 以上就是《深入浅出设计模式》一书中提到的九条设计原则,以及我对它们的理解和评注。

《HFDP》读书笔录(八)——好莱坞原则

八、好莱坞原则 别调用(打电话给)我们,我们会调用(打电话给)你。 这是一个受好莱坞影响而启发的设计原则。 好莱坞的经典名言: 别打电话给我,我会打电话给你 。这句话体现了他们科学的管理方法:一个演员要想出演好莱坞大片,要做的不是老是打电话给导演等人一再地推荐自己;而是努力提高自己的专业水平和演技,等到你的能力能够胜任出演好莱坞大片时,导演等爱惜人才的人自然会主动找上门来。这样的管理方法,一来能防止行贿、贪污、腐败;再者,出演的演员都是导演精挑细选的、真正有实力的人。所以好莱坞才得以长盛不衰。 好莱坞原则同样可以给我们一种防止“ 依赖腐败 ”的方法。当高层组件依赖低层组件,而低层组件又依赖高层组件,而高层组件又依赖边侧组件,而边侧组件又依赖低层组件时,依赖腐败就发生了。在这种情况下,没有人可以轻易地搞懂系统是如何设计的。 在好莱坞原则之下,我们允许低层组件将自己挂钩到系统上,但是高层组件会决定什么时候和怎样使用这些低层组件。换句话说,高层组件对待低层组件的方式是“别调用我们,我们会调用你”。 遵循好莱坞原则后,只要每个方法尽一切努力去实现自己的说要做的任务就可以,当你能恰到好处地完成我们要的任务时,自然会有高层组件去调用它!否则,低层组件为了让自己被调用,先调用高层组件的方法,再反过来调用自己,这就出现了“ 环形依赖 ”! 示例 :比如我们设计一个操作数据库的类,把数据库查询的细节放到了子类的方法里来实现。如果子类“不知好歹”,一个劲地强调要查询数据库要先连接数据库,于是在自己的方法里加入判断并连接数据库的代码。这就违背了这个原则,这些细节高层组件之间会去管理,不需要每个子类多此一举。 好莱坞原则就是让每个方法 各司其职 ,把自己该做的任务完成好就可以!工厂方法模式、观察者模式、模板方法模式等都符合好莱坞原则。 为人处世也是如此,领导人知道一步一步该怎么走,并不需要手下的人来指手画脚,反而交领袖该怎么做,这些都是新手普遍会做的事情,愤世嫉俗,一切看不惯的事情都认为是错误的。在做需求分析时也一样,很多设计人员和客户谈到最后,角色换过来了:设计人员说,你这个项目一定要用 XX 语言做,而一个到底要做个什么东西这是客户自己决定的。就像上级下达命令一样,下级只负责按上级的意愿去完成任务。即使有调度不当,安排不周等不良后果,也是由上级负责。 和其他

《HFDP》读书笔录(七)——最少知识

七、最少知识( Least Knowledge )原则 只和你的密友谈话。 另一个名称叫墨忒耳法则( Law of Demeter )。 我们倾向于使用最少知识原则: 这个名字更直接。 法则( law )给人的感觉是强制的。事实上,没有任何原则是法律( law ),所有的原则都应该在有帮助的时候才遵守。所有的设计都不免需要折中(在抽象和速度之间取舍,在空间和时间之间平衡……)。虽然原则提供了方针,但在采用原则之前,必须全盘考虑所有的因素。 我感觉,这个原则更多的是属于类模式,因为类模式的关系才是在编译时决定的(同样,是否遵循这个原则,在编码后编译是已经决定了)。 这个原则希望我们在设计中,不要让太多的类耦合在一起,免得修改系统中一部分,会影响到其他部分。 那如何才能避免“赢得太多的朋友和影响太多的对象”?就是要遵循以下指导方针: 就任何对象而言,在该对象的方法内,我们只应该调用属于以下范围的方法: 该对象本身 被当作方法的参数而传递进来的对象 此方法所创建或实例化的任何对象 对象的任何组件 前三条方针告诉我们,如果某对象是调用其他的方法的返回结果,不要调用该对象的方法! 而第四条,则把“组件”想像成是被实例化变量所引用的任何对象,换句话说,把这想像成是“有一个”( Has-A )关系。 示例 : public House { WeatherStation station; // 其他的方法和构造器 public float getTemp () { return station.getThermometer ().getTemperature (); // 违反最少只是原则了,因为在此调用的方法属于另一个调用的返回对象。 } } public House { WeatherStation station; // 其他的方法和构造器 public float getTemp () { Thermometer thermometer = station.getThermometer (); return getTempHelper (thermometer); } public f

《HFDP》读书笔录(六)——依赖倒置原则

六、依赖倒置原则( Dependency Inversion Principle ) 依赖抽象,不依赖具体类。 扯淡 :其实我有时想,也许类继承上这种树状结构,理想状态会是仅仅有叶子节点是具体类(所有具体类都是叶子节点,叶子节点可能不是具体类)。 这个原则说明了:不能让高层组件依赖低层组件,而且,不管高层或低层组件,“两者”都应该依赖抽象。 所谓高层组件,是由其他低层组件定义其行为的类。例如上例中的借阅管理系统,它的行为是由可借阅的对象决定的,而可借阅的接口则是一个低层组件。 其实,这个原则听起来很像是“针对接口编程,不针对实现编程”,不是吗?的确很相似,然而这里更强调“抽象”。 另外,我觉得“针对接口编程”是从 实现者 ( 被调用者 )的角度来看“接口”,而“依赖倒置原则”是从 调用者 的角度来看: OO 是符合人的思维的,人在某一个时刻理解某一个对象,往往只是看他们的某个侧面,而并不是他们的全部。 假设,我在公司里组织了一批 Java 程序员,我和这些人在公司里交流是通过“ Java 程序员”这个统一的接口,而“ Java 程序员”可能并非他们的全部。 模拟到代码里,就是这么人的类 "class Person extends ... implements Java 程序员 ..." 。 而在我和他们交互时,仅仅使用了 "Java 程序员 一号员工 = [ 张三 ];" ,这是比较符合生活逻辑的。他们各自在家庭里可能有充当了“儿子”、“女儿”等不同的身份。而我们在公司这个环境下,只关心他们是“ Java 程序员”这一部分的特性,这才是合理的。 因此,直接用 "Java 程序员 一号员工 = new Person(" 张三 ");" ,这样针对实现来写,就太过具体,让我们关心的事情太多了,有很多冗余。不符合此原则。 于是引入“工厂模式”等“创建型模式”就是为了避免违背这一原则。 下面的指导方针,能帮你避免在 OO 设计中违反依赖倒置原则: 变量不一可以持有具体类的引用。   如果使用 new ,就会持有具体类的引用。你可以改用工厂来避开这样的做法。 不要让类派生自具体类。   如果派生自具体类,你就会依赖具体类。请派生自一个抽象(接口或抽象类)

《HFDP》读书笔录(五)——开闭原则

五、开闭原则 类应该对扩展开放,对修改关闭。即不用修改原有程序的前提下新增一个类型。这是 最重要 的设计原则之一! “开”是指对扩展“开放”,“闭”是指对修改“关闭”。其中那这个“扩展”包括“类型上的扩展”和“功能上的扩展”。比如“装饰者模式”这样能再添加责任,而不用修改原有的代码,属于功能上开放扩展;而“策略模式”,能再实现新的算法添加到算法族里,同样不用修改旧代码。这样的扩展属于类型上的扩展。 我们的目标是允许类容易扩展,在不修改现有代码的情况下,就可搭配新的行为。如能实现这样的目标,有什么好处呢?这样的设计具有弹性可以应对改变,可以接受新的功能来应对改变的需求。 要遵循开闭原则,方法就是尽量遵循“多使用组合,少使用继承”: 利用组合( composition )和委托( delegation )可以在运行时具有继承行为的效果。 组合的强大威力:利用继承设计子类的行为,是在编译时静态决定的,而且所有的子类都会继承到相同的行为。然而,如果能够利用组合的做法扩展对象的行为,就可以在运行时动态地进行扩展。 利用此技巧,可以把多个新职责,甚至是设计超类时还没有想到的职责加在对象上。而且,可以不用修改原来的代码。 通过动态组合对象,可以写新的代码添加新的功能,而无须修改现有代码。 既然无须修改现有代码,那么引进 bug 或产生意外副作用的机会就将大幅度减少 。 副作用 :遵循开放 - 关闭原则,通常会引入新的抽象层次,增加代码的复杂度。你需要把注意力集中在设计中 最有可能改变 的地方,然后应用开放 - 关闭原则。 实例:“装饰者模式”就是一个完全遵循“开闭原则”的模式。 在选择 需要被扩展的代码部分 时要小心。每个地方都采用开放 - 关闭原则,是一种浪费,也没必要,还会导致代码变得复杂且难以理解。

《HFDP》读书笔录(四)——松散耦合原则

四、松散耦合原则 为交互对象之间的松耦合设计而努力 松散耦合( loose coupling )是理想的设计特征之一(《 代码大全 2 》 P80 )。 我觉得,这条原则是我们的终极目标,其他八条原则只是为了实现这个目标的方法。 松散耦合意味着在设计时让程序的各个组成部分之间关联最小。通过应用类接口中的合理抽象、封装性及信息隐藏等原则(第一、二原则),设计出相互关联尽可能最少的类。减少关联也就减少了集成、测试与维护时的工作量。 松散耦合的威力 :对象之间的相互依赖降到最低,因此能够应对变化, 独立地 复用任意对象。所以可以建立更富有弹性的系统。 实现方法:遵循抽象和封装等原则,尤其是“针对接口编程”。 效果:能实现“松散耦合”,会很自然地伴随“可扩展性( extensibility )”、“可重用性( re usability )”等设计特性。 举例:要设计一个图书馆借阅管理系统( BorrowManager ),必然会牵涉到“书籍( Book )”这个类。 如果“管理系统”知道得太具体(直接引用 Book 这个类),或者“书籍类”为迎合“上司”在自己的接口中提供了“借阅方法”。虽然这样在“图书借阅管理系统”中能很好的工作,但它们相互之间关系太密切,了解太多,以至于不能被其他复用。例如,无法在网上书店里复用上面那个带有“借阅方法”的“书籍类”;同样无法在“唱片借阅系统”中复用上面的“借阅管理系统”。 比较合理的做法应该是定义好“书籍”的抽象类(或接口)、和“可借阅”接口,然后由图书管理的书去实现它们。而管理系统只和“可借阅”接口打交道。如下图: 为人处世 也是如此:乱七八糟的事情知道得越多,就越难从“漩涡”中脱身。不久前腾讯公司控告跳槽的几个高层人员,因为他们知道一些商业机密,跳槽会带来泄密的后果。那些技术人员面临官司缠身,不再是一只自由的小小鸟,可以展翅飞翔。想要自由的生活,就不要“知道”那么多。

《HFDP》读书笔录(三)——多用组合,少用继承

三、多用组合,少用继承   面向对象之路,承诺了“复用”。借由继承,好东西可以一再被利用,所以程序开发时间就会大幅减少。   但我们总是需要花很多时间在系统的维护和变化上,因此软件开发完成“后”比完成“前”需要花更多的时间。   而“继承”只能帮助减少完成“前”的时间,因此我们要借助“组合”, 致力于提高可维护性和可扩展性上的复用程度 。   在上面“封装变化”里也提到过,继承有局限性,它能处理“实现方式”和“类”的关系是一( 一种算法实现 )对一(一 个实现该方法的类 )的情境。   但面对一对多时,就力不从心了,会造成很多重复的代码。此时就个要多用组合。而组合,同样能使用一对一的情况(也许会显得小题大作)。

《HFDP》读书笔录(二)——针对接口

二、针对接口(超类型: Java 中的 interface 和 abstract class )编程,不针对实现编程   定义和实现一个类时,我们时刻要注意的就是它说提供的接口是否符合它体现的抽象层次。比如一个“书”的类里包含了“获取书名、作者名”等书本信息是合理的,但要出现“扫地”等方法,就是显得不合乎逻辑了。所以,每公开或隐藏一个方法或属性时,就要问问自己,我这么做是否符合了该类所在的抽象?   在接近(也可能达到)了这一目标后,实现接口提供的方法可能有很多种不同的实现方法,我们就可以不必把这些实现“硬编码”进方法里,可以其中一些“行为类”,把实现转交给这些行为类来进行,这样就可以从行为中解耦出来,具有更富弹性的类(比如能动态指定行为)。但副作用也是显而易见的:会因此多出许多额外的类来(类个数急剧膨胀)。 策略模式就是这么做的,可以参考第一条原则。

《HFDP》读书笔录(一)——封装变化

一、封装变化 找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。 引入背景 :   在策略模式一章中,同一个“鸭子”基类,会有“绿头鸭”、“红头鸭”、“橡皮鸭”、“诱饵鸭”等多个子类。   而对于飞行( fly )这一行为来说,“绿头鸭”、“红头鸭”等代表的真实鸭子有统一的飞行行为,而“橡皮鸭”、“诱饵鸭”等代表的假鸭子的飞行行为空。   如果每个子类的行为都是不同的,那用继承来实现是合理且优雅的;但遇到像这种:子类的某一个行为不尽相同,那用继承就会造成有相同行为的子类里存在重复的代码,造成代码难以维护。   这时就需要把这几类会变化的地方抽象出来,实现多个不同的“算法”,比如上例的真鸭“能飞”的行为和假鸭子“不能飞”的行为。然后实现方式由继承改为组合,在子类的构造器中“静态指定”具体使用那个“实现算法”。这就是策略模式! 适用范围 : 某一方法有两种或多种不同的“算法”实现方式。 每一种“算法”都有多个类去实现。   简单的讲,就是“实现方式”和“类”的关系犹如一张“二分图”一样有 一 ( 一种算法实现 )对 多 ( 多个实现该方法的类 )关系时。

《Head First Design Patterns》读书笔录(零)

前言 :寒假在家昼夜不分疯狂地看了一个星期,一口气看完《 Head First Design Patterns 》,感觉还是意犹未尽。尤其是对最后一章一笔带过的 9 个模式感觉遗憾万分。于是再次把玩了一番整部书,把散落在其中的金子收集到一处,并记下自己的看法,以供以后欣赏~ 模式的定义 模式:在某 情境 下,针对某 问题 的某种 解决方案 。 情境:应用某个模式的情况。这应该是不断重复出现的情况。 问题:你想在某情境下达到的目标,但也可以是某情境下的约束。 解决方案:这是我们的目标。一个通用的设计,用来解决约束,达到目标。 通俗版本:如果你发现自己处于某个情境下,面对着所欲达到的目标被一群约束影响着的问题,然而,你能够应用某个设计,克服这些约束并达到该目标,将你领向某个解决方案。 设计模式和我崇拜的武术 在我看来,“模式”就像武侠小说里的,大侠们使的武功招式(一个模式对应一种武功招式): 就是因为有了武功招式,才让专业习武者和市井流氓打架时的拳打脚踢区分开来; 同理,设计模式等专业术语也让专业软件设计师和业余编程爱好者有了本质区别。 武功招式普遍都有一些很响亮的名字,比如太极里的“白鹤亮翅”; 同样,每个设计模式都有一个形象贴切的名字,像“观察者模式”。 武功招式,都是由最基本的拳、掌、钩等(或者剑术的刺、劈等)基本招式组成,并以各门各派的“内功心法”为指导,形成能够强身健体、修身养性的招数; 设计模式,同样是由最基本的基础元素:抽象、封装、多台、继承组成,并合理遵循贯彻 Object-Oriented 设计原则,产生富有弹性的类图结构和设计框架。 武功招式会被滥用,初学者做起事情来,处处都是有头有脸的招式,在木板上订枚钉子,好好的榔头不用,非要用什么一阳指、千斤坠; 设计模式同样会有如此尴尬,初学者“为了用设计模式而用设计模式”,甚至扬言写个 Hello World 也要用设计模式,这时候就病入膏肓了。 《笑傲江湖》中,风清扬教令狐冲“独孤九剑”的时候,倾囊相授之后要他尽数忘记。以后在比剑的过程中随心所欲,想起那一招就使出哪一招。 悟道(编程之道)者的心智能够看到模式在何处能够自然融入,不必去思考这个地方用“策略模式”好还是用“模板方法模式”好,一切顺其自然。 武学的一种高境界,是无招胜有招,因

厚积薄发

  如果看过我以前的文章,也许会发现我更新空间的周期很长,所以我大多时候都不敢称它们为“日志”。因为我希望对自己要求严格些,不做到满意不轻易拿出来和大家分享。   从小我爸爸妈妈就教育我:山外有山,人外有人。在小时候,如果我和他们提起我的一些“奇思妙想”、“远大抱负”等,他们的回答总是“你能想到的别人早就想到了”、“你会的人家都会”……我知道他们是怕我骄傲,教育我为人谦虚低调,不过我也因此习惯成自然。所以我对什么事情有想法,或者感觉有所领悟。我都不忙于发表自己的看法,而仅仅把它记录在我的Google Docs里,权限自然是private的;而我做了那些小玩具程序大部分也都收罗在私人的Google Sites里。   作为一个在校本科生,我的知识太少了,涉猎领域也太狭隘。对事物的评论和判断就未免片面、主观。我是很怕自己会犯一些常识性的错误而惹人笑话的。就像我们不能在背后议论别人的好坏,因为我们并非当事人,对事情的了解可能只是偏听偏信。我比较欣赏“厚积薄发”,一来它让我觉得踏实;再者,这也是对其他人负责。就如现在CSDN的博客系统,纵使有许多好玩的新特性,能吸引一批人去尝试。但因为技术的不成熟,三天两头的出毛病,新鲜感过后终究是会失去忠实用户。   另外,我也不想卷入那些“版权”、“抄袭”、“专利”的争论漩涡中(我不会轻易说这样的话,我现在有底气说是因为我觉得我目前的生活很安逸)。现在大家的法律意识渐渐增强了,这是一件好事,毕竟我们中华人民共和国是法制、德治、仁治的伟大国家。但学术研究,观点撞车是难免的,至少我相信两个人在没有交流的情况下是有可能想到一个相同的点子的,比如牛顿和莱布尼兹都创立了微积分;现在的搜索引擎虽然已经相当强大,但还远没有达到理想的效果。由于关键字的不恰当,或者别的什么原因没搜到其他人的成果,也是在所难免的。我时常在想:如果我作为一门新技术,我最大的心愿或许应该是能为更多的人服务。而并不希望看到这么多人仅仅为了争夺新技术的“所有权 ”、“专利权”等一大堆和技术本身不相关的事情争得面红耳赤,官司来官司去的。我也坚信完善的法律会是以人为本的!但目前来说,我只能尽量避免这样的误会。   我并不是在宣传让大家都无私奉献。我周围有些同学总喜欢抨击人类的是是非非,感叹人类多么丑恶、行为多么猪狗不如……但我始终相信人类区别于动物。抛开一切文明,赤裸裸地去评价人类为

我的空间

  我开过好几个空间。   第一个是网易163 博客,在高中时赶时髦开的。那时候忙着高考,加上我高中的生活过得很单调,也的确没什么东西值得拿出来和大伙儿分享,所以成了摆设。毕业后更新过几篇,但总觉得首页对文章的预览功能让人不满意,就一狠心把它关了。   后来同学帮我开通了QQ 空间(那时QQ 空间还不能自己申请,需要其他用户帮忙开通),我想QQ 上人气也旺,可以遥相呼应,一石激起千层浪!但苦于QQ 空间对文章大小作了很严格的限制,容不下我那么多废话,因此也是痛并快乐着。后来发生意外,腾讯突然指出我的QQ号是靓号,要求续费才能再使用。苦于连换了三张手机卡都不成功,后来发现那些手机号也被移动和联通收回了,那可是我花钱买的号码呀!!受不了这些霸王条约,再次狠心把帐号仍掉。   第三回转到百度( http://hi.baidu.com/redraiment ),上大一时开的。我感觉百度也是一个求稳的公司,这比较符合我的性格。尤其是能直接粘贴带HTML 格式的文字,写ACM 等解题报告感觉很方便。但后来百度改版,对文章的大小作了限制,贴的代码格式也经常被打乱格式,又考虑到搬家,但找半天也没找到能关闭空间的按钮,感觉很无奈...   第四回搬家到CSDN( http://blog.csdn.net/redraiment )。我想,在国内CSDN 算得上比较专业的程序员站点了,Blog 系统自然也应该会“有程序员特色”吧^_^。但总觉得CSDN 的Blog 很不稳定,就在我夸完它的时候,却意外地发现文章不能发表了,看了官方博客才知道“评论系统”也临时停用了半个多月了。这让我感觉不是很好,觉得他们有点不负责,拿用户当小白鼠作实验。当然,CSDN 的新功能倒是不断添加,也自带插入代码的功能,用起来很方便。而且在那里也有很多志同道合的朋友偶尔关注一下我的空间,让我很有存在感。只是出了Bug 修复效率不高,博客系统到现在还是瘫痪着。   古人有孟母三迁,我的空间都搬了五回家了。现在我来到Blogger,它和Google 紧密联系在一起。我一直对Google心存崇敬之心。不是崇洋媚外,只是他为人处世的方法值得我们信赖。我觉得Google对用户一直很负责。我使用很多Google的产品:一开机,打开浏览器,主页就是iGoogle,用Gmail收发信件、用Reader分享一些大师的观点、用Doc